I'm working on a program that takes user input two times. The user will input a sentence, and then enter a key phrase to remove each found match throughout the sentence, and return what's left. The code I have below for the 'logic' is...
char delim[AFFIX];
strcpy(delim, userInput); //storing the phrase user wants to remove
char *token = strtok(sentence, delim);
while (token)
{
cout << token << endl;
token = strtok(NULL, delim);
}
The issue I'm having is that strtok removes every single instance of a character found. For example, if the user wishes to remove all instances of 'pre' then the word "preformed" would turn into "fo m d" instead of formed. Is there a way to restrict strtok from removing EVERY instance of a character found, and only remove the series of characters?
I understand that working with strings or vectors would make my life much easier, but I want to work with arrays of chars. I'm really sorry if this isn't clear enough, I'm very new to c++. Any advice on how to approach this problem would be greatly appreciated. Thank you for your time.
A easy and more or less dirty solution would be to just add spaces before and after your search word. To ensure it's actually a full word and not part of a word.
Eg:
Input: "pre"
Check: " pre "
To ensure you also cut the first word you would need to check the first part of the string and add a space before it if the first word is also the inputted word.
Example:
#include <string>
#include <iostream>
void removeSubstrs(std::string& s, std::string& p) {
std::string::size_type n = p.length();
for (std::string::size_type i = s.find(p);
i != std::string::npos;
i = s.find(p))
s.erase(i, n);
}
void check(std::string& s, std::string& p) {
int slen = s.length();
int plen = p.length();
std::string firstpart = s.substr (0,plen);
std::string lastpart = s.substr (slen-plen,slen);
if (firstpart == p) {
s = " " + s;
}
if (lastpart == p) {
s += " ";
}
}
int main ()
{
std::string str = "pre test inppre test pre";
std::string search = "pre";
std::string pattern = " " + search + " ";
check(str, search);
removeSubstrs(str, pattern);
std::cout << str << std::endl;
return 0;
}
strtok isn't the right choice here, you can think of strtok's 2nd argument as a list of delimiters (each char in that list is a delimiter) and it breaks the string into tokens using those delimiters.
A better solution would be something along the lines of strstr and strcat.
maybe something like this (using MSVC++)
void removeSubstr(char *string, const char *sub, int n) {
char *match;
int len = strlen(sub);
while ((match = strstr(string, sub))) {
*match = '\0';
strcat_s(string, n, match + len);
}
}
int main() {
char test[] = "abcxyz123xyz";
removeSubstr(test, "xyz", sizeof(test) / sizeof(char));
cout << test << endl;
}
strcat_s is a more secure version of strcat (MSVC++ likes it way more than strcat :p)
There are better ways of doing this ofc but I'm restricting myself to char arrays here as you requested...
Related
I have this code and I want to insert a new line after each dot with a insert statement. I get the error message no matching member function for call to 'insert'. What am I doing wrong?
#include<iostream>
#include<string>
using namespace std;
int main ()
{
string const inText1 = "I'm reading. ";
string const inText2 = "I like to read. ";
string const inText3 = "I'm gonna read that book. ";
string const inText4 = "She's reading. ";
string const inText5 = "He's reading. ";
string const inText6 = "READ. ";
string const inText7 = "Reading. ";
string inText8=inText1+inText2+inText3+inText4+inText5+inText6+inText7;
string::size_type dotpos = inText10.find(".");
if(dotpos != string::npos)
inText10.insert(dotpos, endl);
cout << inText10 << endl;
return 0;
}
Assuming inText10 is just inText8, this is the working code.
Changes done:
Earlier, the inText10 string was not found. So, insert() on that object might have produced your error.
The position to insert newline is NOT dotpos. Its dotpos + 1
Note this code just adds a newline after finding the first dot. To add newline after every dot, use while loop to find all dots and insert newline char next to it.
#include<iostream>
#include<string>
using namespace std;
int main ()
{
string const inText1 = "I'm reading. ";
string const inText2 = "I like to read. ";
string const inText3 = "I'm gonna read that book. ";
string const inText4 = "She's reading. ";
string const inText5 = "He's reading. ";
string const inText6 = "READ. ";
string const inText7 = "Reading. ";
string inText8=inText1+inText2+inText3+inText4+inText5+inText6+inText7;
string::size_type dotpos = inText8.find(".");
if(dotpos != string::npos)
inText8.insert(dotpos + 1, "\n");
cout << inText8 << endl;
return 0;
}
There are a lot of undefined variables in your code, but basically you receive 'no matching member function for call to 'insert'' because you are trying to insert std::endl into a string. std::endl can't be casted to char of any type, so, you can't do this.
To fix your issue, replace
inText10.insert(dotpos, endl);
with
inText10.insert(dotpos, '\n');
By the way, if you want to insert \n after every single dot, you should do find() in a loop, since find() returns a single matched char position, not an array.
To learn more, why you can't insert std::endl, read this std::endl description at cppreference
Hi I am new to community.
I have a question regarding std::string.
Is it possible to write into specific index of string keeping rest of string empty ?
e.g.
std::string tempString = "";
tempString ->insert(10, "Some String I want to Insert");
//index position and string to insert
and final answer would be tempString = "__________Some String I want to Insert"
where characters 0 through 9 are uninitialized.
this is possible with char * once memory is allocated.
but is this possible with std::strings ?
Thank you in advance and thank you for having me in this community :)
Given some insertion position
size_t insertion_position = 10;
And some std::string or const char* to be inserted
const char *text = "Some String I want to Insert";
This will do it efficiently.
std::string tempString( insertion_position, ' ' );
tempString += text;
Or as a one liner:
std::string tempString = std::string( insertion_position, ' ' ) + text;
check this out, you may be able to use a stringstream instead of cout. Then you can just get the output into a string instead of seeing it dumped on the console
// using the fill character
#include <iostream> // std::cout
int main () {
char prev;
std::cout.width (10);
std::cout << 40 << '\n';
prev = std::cout.fill ('x');
std::cout.width (10);
std::cout << 40 << '\n';
std::cout.fill(prev);
return 0;
}
Output:
40
xxxxxxxx40
Is it possible to write into specific index of string keeping rest of string [uninitialized] ?
No.
std::basic_string is not designed to represent collections of uninitialized values.
This question already has answers here:
C's strtok() and read only string literals
(5 answers)
Closed 8 years ago.
I have a simple code where Iam trying to go through a char* and spit it into separate words. Here is the simple code I have.
#include <iostream>
#include <stdio.h>
int main ()
{
char * string1 = "- This is a test string";
char * character_pointer;
std::cout << "Splitting stringinto tokens:" << string1 << std::endl;
character_pointer = strtok (string1," ");
while (character_pointer != NULL)
{
printf ("%s\n", character_pointer);
character_pointer = strtok (NULL, " ");
}
return 0;
}
I am getting an error that will not allow me to do this.
So my question is, how do I go through and find each word in a char*. For my actual program I am working on, one of my libraries returns a paragraph of words as a const char* and I need to stem each word using a stemming algorithm (I know how to do this, I just do not know how to send each individual word to the stemmer). If someone could just solve how to get the example code to work, I will be able to figure it out. All of the examples online use a char[] for string1 instead of a char* and I cannot do that.
This is the simplest (codewise) way I know to split a string in c++:
std::string string1 = "- This is a test string";
std::string word;
std::istringstream iss(string1);
// by default this splits on any whitespace
while(iss >> word) {
std::cout << word << '\n';
}
or like this if you want to specify a delimiter.
while(std::getline(iss, word, ' ')) {
std::cout << word << '\n';
}
Here's a corrected version, try it out:
#include <iostream>
#include <stdio.h>
#include <cstring>
int main ()
{
char string1[] = "- This is a test string";
char * character_pointer;
std::cout << "Splitting stringinto tokens:" << string1 << std::endl;
character_pointer = strtok (string1," ");
while (character_pointer != NULL)
{
printf ("%s\n", character_pointer);
character_pointer = strtok (NULL, " ");
}
return 0;
}
There are different ways you could do this in C++.
If space is your delimited then you can get the tokens this way:
std::string text = "- This is a test string";
std::istringstream ss(text);
std::vector<std::string> tokens;
std::copy(std::istream_iterator<std::string>(ss),
std::istream_iterator<std::string>(),
std::back_inserter<std::vector<std::string>>(tokens));
You can also tokenize the string in C++ using regular expressions.
std::string text = "- This is a test string";
std::regex pattern("\\s+");
std::sregex_token_iterator it(std::begin(text), std::end(text), pattern, -1);
std::sregex_token_iterator end;
for(; it != end; ++it)
{
std::cout << it->str() << std::endl;
}
Forget about strtok. To get exactly what you seem to be
aiming for:
std::string const source = "- This is a test string";
std::vector<std::string> tokens;
std::string::const_iterator start = source.begin();
std::string::const_iterator end = source.end();
std::string::const_iterator next = std::find( start, end, ' ' );
while ( next != end ) {
tokens.push_back( std::string( start, next ) );
start = next + 1;
next = std::find( start, end, ' ' );
}
tokens.push_back( std::string( start, next ) );
Of course, this can be modified as much as you want: you can use
std::find_first_of is you want more than one separator, or
std::search if you want a multi-character separator, or even
std::find_if for an arbitrary test (with a lambda, if you have
C++11). And in most of the cases where you're parsing, you can
just pass around two iterators, rather than having to construct
a substring; you only need to construct a substring when you
want to save the extracted token somewhere.
Once you get used to using iterators and the standard
algorithms, you'll find it a lot more flexible than strtok,
and it doesn't have all of the drawbacks which the internal
state implies.
I've been looking thousand of questions and answers about what I'm going to ask, but I still didn't find the way to do what I'm gonna to explain.
I have a text file from which I have to extract information about several things, all of them with the following format:
"string1":"string2"
And after that, there is more information, I mean:
The text file is something like this:
LINE 1
XXXXXXXXXXXXXXXXXXXXXXXXXXXX"string1":"string2"XXXXXXXXXXXXXXXXXXXXXXXXXX"string3":"string4"XXXXXXXXXXXXXXXXXXXXXXXXXXXX...('\n')
LINE 2
XXXXXXXXXXXXXXXXXXXXXXXXXXXX"string5":"string6"XXXXXXXXXXXXXXXXXXXXXXXXXX"string7":"string8"XXXXXXXXXXXXXXXXXXXXXXXXXXXX...
XXX represents irrelevant information I do not need, and theEntireString (string used in the code example) stores all the information of a single line, not all the information of the text file.
I have to find first the content of string1 and store the content of string2 into another string without the quotes. The problem is that I have to stop when I reache the last quote and I don't know how exactly do this. I suppose I have to use the functions find() and substr(), but despite having tried it repeatedly, I did not succeed.
What I have done is something like this:
string extractInformation(string theEntireString)
{
string s = "\"string1\":\"";
string result = theEntireString.find(s);
return result;
}
But this way I suppose I store into the string the last quote and the rest of the string.
"find" function just give you the position of matched string to get the resulting string you need to use the "subst" function. Try This
string start,end;
start = theEntireString.substr(1,theEntireString.find(":")-2);
end = theEntireString.substr(theEntireString.find(":")+2,theEntireString.size()-1);
That will solve you problem
Assuming either the key or value contains a quotation mark. The following will output the value after the ":". You can also use it in a loop to repeatedly extract the value field if you have multiple key-value pairs in the input string, provided that you keep a record of the position of last found instance.
#include <iostream>
using namespace std;
string extractInformation(size_t p, string key, const string& theEntireString)
{
string s = "\"" + key +"\":\"";
auto p1 = theEntireString.find(s);
if (string::npos != p1)
p1 += s.size();
auto p2 = theEntireString.find_first_of('\"',p1);
if (string::npos != p2)
return theEntireString.substr(p1,p2-p1);
return "";
}
int main() {
string data = "\"key\":\"val\" \"key1\":\"val1\"";
string res = extractInformation(0,"key",data);
string res1 = extractInformation(0,"key1",data);
cout << res << "," << res1 << endl;
}
Outputs:
val,val1
Two steps:
First we have to find the position of the : and splice the string into two parts:
string first = theEntireString.substr(0, theEntireString.find(":"));
string second = theEntireString.substr(theEntireString.find(":") + 1);
Now, we have to remove the "":
string final_first(first.begin() + 1, first.end() - 1);
string final_second(second.begin() + 1, second.end() - 1);
You don't need any string operation. I hope the XXXXX doesn't contain any '"', so You can read the both strings directly from the file:
ifstream file("input.txt");
for( string s1,s2; getline( getline( file.ignore( numeric_limits< streamsize >::max(), '"' ), s1, '"' ) >> Char<':'> >> Char<'"'>, s2, '"' ); )
cout << "S1=" << s1 << " S2=" << s2 << endl;
the little help-function Char is:
template< char C >
std::istream& Char( std::istream& in )
{
char c;
if( in >> c && c != C )
in.setstate( std::ios_base::failbit );
return in;
}
#include <regex>
#include <iostream>
using namespace std;
const string text = R"(
XXXXXXXXXXXXXXXXXXXXXXXXXXXX"string1":"string2"XXXXXXXXXXXXXXXXXXXXXXXXXX"string3" :"string4" XXXXXXXXXXXXXXXXXXXXXXXXXXXX...
XXXXXXXXXXXXXXXXXXXXXXXXXXXX"string5": "string6"XXXXXXXXXXXXXXXXXXXXXXXXXX"string7" : "string8" XXXXXXXXXXXXXXXXXXXXXXXXXXXX...
)";
int main() {
const regex pattern{R"~("([^"]*)"\s*:\s*"([^"]*)")~"};
for (auto it = sregex_iterator(begin(text), end(text), pattern); it != sregex_iterator(); ++it) {
cout << it->format("First: $1, Second: $2") << endl;
}
}
Output:
First: string1, Second: string2
First: string3, Second: string4
First: string5, Second: string6
First: string7, Second: string8
Running (with clang and libc++): http://coliru.stacked-crooked.com/a/f0b5fd383bc227fc
This is how raw string literals look in an editor that understand them: http://bl.ocks.org/anonymous/raw/9442865/
I'm trying to compare a char * to a to a std::string
const char *s = "0#s072116\tblah\tblah\blah";
std::string id = "072116";
I need to compare these two, basically before the first \t and after the first 3 chars on the left. The width of the id can vary. :(
I'm not very good at C++. Any ideas??
Thanks
You can do it as follows:
#include <iostream>
#include <string>
using namespace std;
...
int main() {
const char *s = "0#s072116\tblah\tblah\blah";
string ss = s;
string id = "072116";
int found = ss.find(id);
cout << "found is: " << found;
}
If id is a substring in ss, then found will be the position of the first occurrence of id in ss.
If id is not a substring in ss, then found will be a negative number.
More examples on find.
Caveat:
The code above is based on the assumption your meant "...basically before the first \t and after the first 3 chars on the left..." as a way to point out where the substring would be matched in this particular example.
If instead it is a requirement that must be met for all instances (i.e. const char *s = "0#s\tblah\tblah\blah072116" should not be matched), then the provided code sample is not sufficient.
const char *position = std::search(s, s + std::strlen(s), id.begin(), id.end());
if (*position == '\0')
std::cout << "Not found\n";
else
std::cout << "Found at offset " << (position - s) << '\n';