C++ Multi-split a string - c++

I'm trying to split some data i receive, the data is like this:
0010|chocolate|cookie;458|strawberry|cream;823|peanut|butter;09910|chocolate|icecream
so first i need to separe each section of food (separed with ";") and then get the ID of only the food sections that contains "chocolate" on it, the problem is that the data is not static so i can't predict how many times a food section with "chocolate" will appear.
Here is the code where i split the food sections and get the quantity of sections that are in the data:
#include <string>
#include <sstream>
#include <vector>
#include <iostream>
#include <fstream>
using namespace std;
vector<string> &split(const string &s, char delim, vector<string> &elems)
{
stringstream ss(s);
string item;
while (getline(ss, item, delim))
{
elems.push_back(item);
}
return elems;
}
vector<string> split(const string &s, char delim)
{
vector<string> elems;
split(s, delim, elems);
return elems;
}
char* data = "0010|chocolate|cookie;458|strawberry|cream;823|peanut|butter;09910|chocolate|icecream";
int main()
{
vector<string> food = split(data, ';');
cout << number of food sections is : " << food.size();
return 0;
}
It works, but now i want it to read in ALL the sections and list me which ones contains "chocolate" on it like:
0010|chocolate|cookie
09910|chocolate|icecream
then list me only the ID's of the sections that contains chocolate on it, which probably is possible with the same split vector i use.
0010
09910

It just depends how rich your data is. Ultimately you have to throw a recursive descent parser at it. But this seems simpler.
Can semicolon be escaped? If not, go though, and each time you hit a semicolon, store the index in a growing vector. That gives you record starts. Then step through the records. Create a temporary string which consists of the record up to the semicolon, then search for the string "chocolate". If it matches, the id is the first field in your record, so up to the first | character.

Try using a function to find a word inside a string delimited by delim, like this one:
bool find(string vfood, string s, char delim)
{
std::istringstream to_find(vfood);
for (std::string word; std::getline(to_find, word, delim); ) if (word == s) return true;
return false;
}
And then you can find whatever you want within each string of 'food'
vector<string> food_with_chocolate;
for (string &s : food)
{
if (find(s, "chocolate", '|')) food_with_chocolate.push_back(s);
}

Related

Separating input C++

I am making a text adventure game in C++. Right now I am getting the input like this:
string word1, word2;
cin >> word1 >> word2;
parse(word1, word2);
An example input could be
goto store
Right now, to quit you would have to type quit and any other text to quit.
How can I make it so the input is separated by a space and I can tell if the second string is empty.
UPDATE
I tried the first answer, and i get this error on windows:
The instruction at 0x00426968 referenced memory at 0x00000000.
The memory could not be read.
Click OK to terminate the program.
#include <vector>
#include <string>
#include <sstream>
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
if(!item.empty()) //item not elems
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, elems);
return elems;
}
This function std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) can split a string with an user-define delimiter,and stores each string to a vector.Note that if a separated string is empty, it will not join the string array.
This function std::vector<std::string> split(const std::string &s, char delim)is one of the last function wrapper, so you only need to pass two parameters, convenient for you to use.
use split function like this:
string line;
getline(std::cin,line);
auto strings =split(line,' ');
check strings size you could know many words there are.
If strings size equals 1,so your second string is empty.
I think this is what you're looking for. I don't know how parsing works, but this is how I would solve the problem
string word1, word2;
cin >> word1;
if(word1 == "quit"){
//quit code
}
else
cin >> word2;
By asking for the input separately, you are able to insert this if statement. This if checks to see if the first string of input is "quit", ignores the second piece, and runs the quit code. If the first piece of input is not "quit", it will then ask for the second piece of input.
Using this code, I take the whole input in one string called InputText and analyze it char by char in a loop. I stored the chars in a string called Ch to avoid common issues of it showing the ASCII code instead of my char when I declare it as a char. I keep appending chars to a temp string called Temp. I have an int called Stage which decides where my temp string should go. If the Stage is 1 then I store it in word1, whenever I reach the first space I increase the Stage to 2 and reset the temp; therefore starts storing in word2 now.. In case if there were more than 1 space, you can show an error message at the default of my switch. In case there was only one word and no spaces at all, you can know since I initialized word2 = "" and it remains that way after the loop.
string Ch, InputText, word1 = "", word2 = "", Temp = "";
unsigned short int Stage = 1;
cin >> InputText;
for(int i = 0; i < InputText.length(); i++){
Ch = to_string(InputText[i]);
if (Ch == " "){
Stage++;
Temp = "";
}
else{
switch (Stage){
case 1:
Temp.append(Ch);
word1 = Temp;
break;
case 2:
Temp.append(Ch);
word2 = Temp;
break;
default: //User had more than 1 space in his input; Invalid input.
}
}
}
if (word1 == "quit" && word2 == ""){
//Your desired code
}

C++ check for specific number of input

I have a function that prompts the user for input. If they input more than the number of words I want(3), then an error should be printed. How do I approach this? I found out how to check if the input is < 3, but not > 3.
struct Info
{
std::string cmd;
std::string name;
std::string location;
}
Info* get_string()
{
std::string raw_input;
std::getline(std::cin, raw_input);
std::istringstream input(raw_input);
std::string cmd;
std::string name;
std::string location;
input>>cmd;
input>>name;
input>>location;
Info* inputs = new Info{cmd, name, location};
return inputs;
}
The function I have automatically takes 3 strings and stores them in my struct, which I check later to see if any part of the struct is empty (for example: "Run" "Joe" ""), but what if they enter in 4 strings? Thank you
you can split the input string into words with a space delimiter and then check the number of words. you can use the function below to split your input. after this you can check the size of the vector.
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
using namespace std;
vector<std::string> split(const string &s, char delim) {
stringstream ss(s);
string item;
vector<string> res;
while (getline(ss, item, delim)) {
if(item.length()==0)continue;
res.push_back(item);
}
return res;
}
int _tmain(int argc, _TCHAR* argv[])
{
string theString;
cin>>theString;
vector<string> res=split(theString, ' ');
if(res.size()>3)
{
//show error
}
return 0;
}
The problem with this and with Ferdinand's idea is that in order to test if a 4th string exists, you have to "ask" for it. If it exists, you can error, but if it doesn't then it sits there waiting for input and the user wonders what is going wrong.
Thus I'm going to modify your code slightly. It's fairly straight forward. If the user enters a space in the last "word", then you know that there is an issue and can deal with it as you wish.
// Replace input >> location; with the below
// Get until the line break, including spaces
getline(input, location);
// Check if there is a space (I.e. 2+ words)
if(location.find(" ") != string::npos){
// If so, fail
}
Resources for learning:
http://www.cplusplus.com/reference/string/string/find/
http://www.cplusplus.com/reference/string/string/getline/

Get a word from a string, change it, and put it back to the correct place

I am trying to receive an input, whatever it may be. Do something to the word, and then cout the word back to the user. Here is the thing, I cannot restrict the user from entering whatever they want, so I have to deal with the consequences. Before you guys start accusing me, no, its not a homework.
I have to keep the words in their respective places. So if someone writes
> " he*llo !w3rld ."
I have to keep the metacharacters in their places.
So lets say the change was just to scramble. It has to output
> " eh*olo !w3ldr ."
What I want to do with the word once I retrieve it is not relevant. The only problem I'm having is actually recognizing the word, doing something to it and returning it.
To make you understand better here is my below code
int main(){
string str;
cout<<"Enter a string: ";
getline(cin, str);
cout<<endl;
str= str + " ";
int wordFirstLetter = 0;
int currentWordLen = 0;
int p=0;
while((unsigned)p <str.length())
{
if (isalpha(str[p])) // if letter
{
str[p] = tolower(str[p]);
currentWordLen++;
}
else if(!isalpha(str[p])) { //if no letter
cout <<change(str.substr(wordFirstLetter,currentWordLen)) + " ";
wordFirstLetter = p+1;
//currentWordLen++;
currentWordLen = 0;
}
p++;
//cout<<wordFirstLetter<<" "<<currentWordLen<<endl;
}
return 0;
}
As you can see here I am running a a function called change on the substring every time the space in the array of the string is not a letter. But this fails miserably, because if the sentence starts with a space or has multiple spaces then the program crashes.
I have been looking around and thinking a lot about this. There needs to be two states, where I find a letter and where I don't. If I find something before I even hit one letter I can print it out.
I need to preserve that letter in some other space while I keep looking inside the sentence. When I hit something that is not a letter I need to change the word and print it along with whatever I hit and then reset the space and keep going. I just can't get around my head the functional way of doing this.
There is no need to use regular expressions, I feel it would be overkill. So please do not come to me with modern regex libraries and try to teach them to me and since I am not writing in pearl where regex is integrated I have no use for them. And there is no need to use Finite state machines.
I feel like this is such an easy thing but I just can't hit the spot.
In another thread someone suggested the below code to find words
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, elems);
return elems;
}
It got a lot of positive reviews but I don't know how to implement that and work with what I want.
I will edit as you guys ask me questions. Thanks
You can do something like that :
#include <vector>
#include <string>
#include <iostream>
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
if ( item.lenght() > 0) { // This checks if the string is not empty, to filter consecutive white space.
elems.push_back(item);
}
}
return elems;
}
std::string doSomething(const std::string original) {
// do whatever you want with original which is a word.
return modified; // where modified is std::string
}
int main() {
std::string input;
std::vector<string> listOfWords;
std::cout << "Input your phrase :" << std::endl;
std::cin >> input;
split(input, " ", &listOfWords);
for(int i = 0; i < listOfWords.size(); i++) {
std::cout << "Input was : " << listOfWords[i]
<< "now is : " << doSomething(listOfWords[i]);
}
}

Split a string by a one-char delimiter in C++

I have seen some very popular questions here, on StackOverflow about splitting a string in C++, but every time, they needed to split that string by the SPACE delimiter. Instead, I want to split an std::string by the ; delimiter.
This code is taken from a n answer on StackOverflow, but I don't know how to update it for ;, instead of SPACE.
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
int main() {
using namespace std;
string sentence = "And I feel fine...";
istringstream iss(sentence);
copy(istream_iterator<string>(iss),
istream_iterator<string>(),
ostream_iterator<string>(cout, "\n"));
}
Can you help me?
Here is one of the answers from Split a string in C++? that uses any delimiter.
I use this to split string by a delim. The first puts the results in a pre-constructed vector, the second returns a new vector.
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, elems);
return elems;
}
Note that this solution does not skip empty tokens, so the following will find 4 items, one of which is empty:
std::vector<std::string> x = split("one:two::three", ':');

How to let std::istringstream treat a given character as white space?

Using std::istringstream it is easy to read words separated by white space. But to parse the following line, I need the character / to be treated like white space.
f 104/387/104 495/574/495 497/573/497
How can I read values separated by either slash or white space?
One way is to define a ctype facet that classifies / as white-space:
class my_ctype : public std::ctype<char> {
public:
mask const *get_table() {
static std::vector<std::ctype<char>::mask>
table(classic_table(), classic_table()+table_size);
table['/'] = (mask)space;
return &table[0];
}
my_ctype(size_t refs=0) : std::ctype<char>(get_table(), false, refs) { }
};
From there, imbue the stream with a locale using that ctype facet, then read words:
int main() {
std::string input("f 104/387/104 495/574/495 497/573/497");
std::istringstream s(input);
s.imbue(std::locale(std::locale(), new my_ctype));
std::copy(std::istream_iterator<std::string>(s),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
If boost is available, then boost::split() would be a possible solution. Populate a std::string using std::getline() and then split the line:
#include <iostream>
#include <vector>
#include <string>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>
int main()
{
std::vector<std::string> tokens;
std::string line("f 104/387/104 495/574/495 497/573/497");
boost::split(tokens, line, boost::is_any_of("/ "));
for (auto& token: tokens) std::cout << token << "\n";
return 0;
}
Output:
f
104
387
104
495
574
495
497
573
497
If you know when to split by either slash or whitespace, you can use std::getline
std::istringstream is("f 104/387/104 495/574/495 497/573/497");
std::string f, i, j, k;
std::getline(is, f, ' ');
std::getline(is, i, '/');
std::getline(is, j, '/');
std::getline(is, k, ' ');
Alternatively, you can use formatted input and discard the slashes manually
std::string f;
int i, j, k;
char slash;
is >> f >> i >> slash >> j >> slash >> k;
I'm sure this isn't the best way at all, but I was working on an exercise in the book Programming Principles and Practice Using C++ 2nd Ed. by Bjarne Stroustrup and I came up with a solution that might work for you. I searched around to see how others were doing it (which is how I found this thread) but I really didn't find anything.
First of all, here's the exercise from the book:
Write a function vector<string> split(const string& s, const string&
w) that returns a vector of whitespace-separated substrings from the
argument s, where whitespace is defined as "ordinary whitespace" plus
the characters in w.
Here's the solution that I came up with, which seems to work well. I tried commenting it to make it more clear. Just want to mention I'm pretty new to C++ (which is why I'm reading this book), so don't go too hard on me. :)
// split a string into its whitespace-separated substrings and store
// each string in a vector<string>. Whitespace can be defined in argument
// w as a string (e.g. ".;,?-'")
vector<string> split(const string& s, const string& w)
{
string temp{ s };
// go through each char in temp (or s)
for (char& ch : temp) {
// check if any characters in temp (s) are whitespace defined in w
for (char white : w) {
if (ch == white)
ch = ' '; // if so, replace them with a space char ('')
}
}
vector<string> substrings;
stringstream ss{ temp };
for (string buffer; ss >> buffer;) {
substrings.push_back(buffer);
}
return substrings;
}
Then you can do something like this to use it:
cout << "Enter a string and substrings will be printed on new lines:\n";
string str;
getline(cin, str);
vector<string> substrings = split(str, ".;,?-'");
cout << "\nSubstrings:\n";
for (string s : substrings)
cout << s << '\n';
I know you aren't wanting to split strings, but this is just an example of how you can treat other characters as whitespace. Basically, I'm just replacing those characters with ' ' so they literally do become whitespace. When using that with a stream, it works pretty well. The for loop(s) might be the relevant code for your case.