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/
Related
I am creating a function that splits a sentence into words, and believe the way to do this is to use str.substr, starting at str[0] and then using str.find to find the index of the first " " character. Then update the starting position parameter of str.find to start at the index of that " " character, until the end of str.length().
I am using two variables to mark the beginning position and end position of the word, and update the beginning position variable with the ending position of the last. But it is not updating as desired in the loop as I currently have it, and cannot figure out why.
#include <iostream>
#include <string>
using namespace std;
void splitInWords(string str);
int main() {
string testString("This is a test string");
splitInWords(testString);
return 0;
}
void splitInWords(string str) {
int i;
int beginWord, endWord, tempWord;
string wordDelim = " ";
string testWord;
beginWord = 0;
for (i = 0; i < str.length(); i += 1) {
endWord = str.find(wordDelim, beginWord);
testWord = str.substr(beginWord, endWord);
beginWord = endWord;
cout << testWord << " ";
}
}
It is easier to use a string stream.
#include <vector>
#include <string>
#include <sstream>
using namespace std;
vector<string> split(const string& s, char delimiter)
{
vector<string> tokens;
string token;
istringstream tokenStream(s);
while (getline(tokenStream, token, delimiter))
{
tokens.push_back(token);
}
return tokens;
}
int main() {
string testString("This is a test string");
vector<string> result=split(testString,' ');
return 0;
}
You can write it using the existing C++ libraries:
#include <string>
#include <vector>
#include <iterator>
#include <sstream>
int main()
{
std::string testString("This is a test string");
std::istringstream wordStream(testString);
std::vector<std::string> result(std::istream_iterator<std::string>{wordStream},
std::istream_iterator<std::string>{});
}
Couple of issues:
The substr() method second parameter is a length (not a position).
// Here you are using `endWord` which is a poisition in the string.
// This only works when beginWord is 0
// for all other values you are providing an incorrect len.
testWord = str.substr(beginWord, endWord);
The find() method searches from the second paramer.
// If str[beginWord] contains one of the delimiter characters
// Then it will return beginWord
// i.e. you are not moving forward.
endWord = str.find(wordDelim, beginWord);
// So you end up stuck on the first space.
Assuming you got the above fixed. You would be adding space at the front of each word.
// You need to actively search and remove the spaces
// before reading the words.
nice things you could do:
Here:
void splitInWords(string str) {
You are passing the parameter by value. This means you are making a copy. A better technique would be to pass by const reference (you are not modifying the original or the copy).
void splitInWords(string const& str) {
An Alternative
You can use the stream functionality.
void split(std::istream& stream)
{
std::string word;
stream >> word; // This drops leading space.
// Then reads characters into `word`
// until a "white space" character is
// found.
// Note: it emptys words before adding any
}
I have a program that takes in a user input which can range from a 5 char command like "help" and to also support flag-type commands like "delete -p 'George'"
I don't have much experience with c++, other than doing a bunch of for loops, was wondering if there was a more effective way to parse the array of char.
Could someone point me to the right direction?
Aside from boost library as suggested in the comment, if you're parsing a relative small set of arguments, you can use simple std::cin for taking in arguments as the programme runs, something like:
#include <iostream>
#include <string>
#include <vector>
int main() {
std::vector<std::string> args;
std::string arg;
while(std::cin >> arg) {
args.push_back(arg);
}
}
The above requires an EOF(not carriage return) to mark the end of commands.
For carriage return to mark the end of command, you'll need getline(), as demonstrated:
std::vector<std::string> get_args() {
using std::string;
using std::stringstream; // don't forget to include <sstream> header
string line;
getline(std::cin, line);
stringstream ss;
ss << line;
std::vector<string> cmds;
string cmd;
while (ss >> cmd) {
cmds.push_back(cmd);
}
return cmds;
}
Or if you'd like your main function to take arguments:
int main(int argc, char **argv) {
// The call to the excutable itself will be the 0th element of this vector
std::vector<std::string> args(argv, argv + argc);
}
Yes you can assign an char array to string like this:
char array[5] = "test";
string str (array);
cout << str;
output :
test
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);
}
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
}
Hey all so I have to get values from a text file, but the values don't stand alone they are all written as this:
Population size: 30
Is there any way in c++ that I can read from after the ':'?
I've tried using the >> operator like:
string pop;
inFile >> pop;
but off course the whitespace terminates the statement before it gets to the number and for some reason using
inFile.getline(pop, 20);
gives me loads of errors because it does not want to write directly to string for some reason..
I don't really want to use a char array because then it won't be as easy to test for the number and extract that alone from the string.
So is there anyway I can use the getline function with a string?
And is it possible to read from after the ':' character?
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdlib>
using namespace std;
int main()
{
string fname;
cin >> fname;
ifstream inFile;
inFile.open(fname.c_str());
string pop1;
getline(inFile,pop1);
cout << pop1;
return 0;
}
ok so here is my code with the new getline, but it still outputs nothing. it does correctly open the text file and it works with a char array
You are probably best to read the whole line then manipulate the string :-
std::string line;
std::getline(inFile, line);
line = line.substr(19); // Get character 20 onwards...
You are probably better too looking for the colon :-
size_t pos = line.find(":");
if (pos != string::npos)
{
line = line.substr(pos + 1);
}
Or something similar
Once you've done that you might want to feed it back into a stringstream so you can read ints and stuff?
int population;
std::istringstream ss(line);
ss >> population;
Obviously this all depends on what you want to do with the data
Assuming your data is in the form
<Key>:<Value>
One per line. Then I would do this:
std::string line;
while(std::getline(inFile, line))
{
std::stringstream linestream(line);
std::string key;
int value;
if (std::getline(linestream, key, ':') >> value)
{
// Got a key/value pair
}
}