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
}
Related
Okay, so I'm having a bit of a problem here. The thing is this code works on a friend's computer but I'm getting segmentation faults when I try to run it.
I am reading a file looking like so:
word 2 wor ord
anotherword 7 ano oth the her erw wor ord
...
And I want to parse every word of the file. The first two words (e.g. word and 2) are to be erased but saving the first one in another variable in the process.
I've looked around a bit on accomplishing this, and I've come up with this half-assed piece of code that seems to work on my friends' computer but not mine.
Dictionary::Dictionary() {
ifstream ip;
ip.open("words.txt", ifstream::in);
string input;
string buf;
vector<string> tokens; // Holds words
while(getline(ip, input)){
if(input != " ") {
stringstream ss(input);
while(ss >> buf) {
tokens.push_back(buf);
}
string werd = tokens.at(0);
tokens.erase(tokens.begin()); // Remove the word from the vector
tokens.erase(tokens.begin()); // Remove the number indicating trigrams
Word curr(werd, tokens);
words[werd.length()].push_back(curr); // Put the word at the vector with word length i.
tokens.clear();
}
}
ip.close();
}
What's the best of of parsing this kind of structure in a file and removing the first two elements but saving the others? As you can see, I'm making a Word object that contains a string and a vector for later use.
Regards
EDIT; It seems to add the first line fine, but on removal of the second element, it crashes with a segmentation fault error.
EDIT; words.txt contain this:
addict 4 add ddi dic ict
sinister 6 ini ist nis sin ste ter
test 2 est tes
cplusplus 7 cpl lus lus plu plu spl usp
Without leading blank spaces or ending blanks. Not that it reads all the way anyway.
Word.cc:
#include <string>
#include <vector>
#include <algorithm>
#include "word.h"
using namespace std;
Word::Word(const string& w, const vector<string>& t) : word(w), trigrams(t) {}
string Word::get_word() const {
return word;
}
unsigned int Word::get_matches(const vector<string>& t) const {
vector<string> sharedTrigrams;
set_intersection(t.begin(),t.end(), trigrams.begin(), trigrams.end(), back_inserter(sharedTrigrams));
return sharedTrigrams.size();
}
First of all, there is error in the number of closing }s in your posted code. If you indent them properly, you will see that your code is:
while(getline(ip, input))
{
if(input != " ")
{
stringstream ss(input);
while(ss >> buf) {
tokens.push_back(buf);
}
}
string werd = tokens.at(0);
tokens.erase(tokens.begin());
tokens.erase(tokens.begin());
Word curr(werd, tokens);
words[werd.length()].push_back(curr);
tokens.clear();
}
}
Assuming that is a small typo in posting, the other problem is that tokens is an empty list when input == " " yet you continue to use tokens as though it has 2 or more items in it.
You can fix that by moving everything inside the if statement.
while(getline(ip, input))
{
if(input != " ")
{
stringstream ss(input);
while(ss >> buf) {
tokens.push_back(buf);
}
string werd = tokens.at(0);
tokens.erase(tokens.begin());
tokens.erase(tokens.begin());
Word curr(werd, tokens);
words[werd.length()].push_back(curr);
tokens.clear();
}
}
I would add further checks to make it more robust.
while(getline(ip, input))
{
if(input != " ")
{
stringstream ss(input);
while(ss >> buf) {
tokens.push_back(buf);
}
string werd;
if ( !tokens.empty() )
{
werd = tokens.at(0);
tokens.erase(tokens.begin());
}
if ( !tokens.empty() )
{
tokens.erase(tokens.begin());
}
Word curr(werd, tokens);
words[werd.length()].push_back(curr);
tokens.clear();
}
}
You forgot to include the initialization of the variable "words" in your code. Just looking at it, I am guessing you are initializing "words" to be a fixed-length array of vectors, but then read a word that is off the end of the array. Bang, you're dead. Add a check to "werd.length()" to ensure it is strictly less than the length of "words".
ifstream ip;
ip.open("words.txt", ifstream::in);
string input;
while(getline(ip, input)){
istringstream iss(input);
string str;
unsigned int count = 0;
if(iss >> str >> count) {
vector<string> tokens { istream_iterator<string>(iss), istream_iterator<string>() }; // Holds words
if(tokens.size() == count)
words[str.length()].emplace_back(str, tokens);
}
}
ip.close();
This is what I used to make it work.
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/
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]);
}
}
I have a program that need to get multiple cstrings. I current get one at a time and then ask if you want to input another word. I cannot find any simple way to get just one input with words divided be whitespace. i.e. "one two three" and save the the input in an array of cstrings.
typedef char cstring[20]; cstring myWords[50];
At the moment I am trying to use getline and save the input to a cstring and then I am trying to use the string.h library to manipulate it. Is that the right approach? How else could this be done?
If you really have to use c-style strings, you could use istream::getline, strtok and strcpy functions:
typedef char cstring[20]; // are you sure that 20 chars will be enough?
cstring myWords[50];
char line[2048]; // what's the max length of line?
std::cin.getline(line, 2048);
int i = 0;
char* nextWord = strtok(line, " \t\r\n");
while (nextWord != NULL)
{
strcpy(myWords[i++], nextWord);
nextWord = strtok(NULL, " \t\r\n");
}
But much better would be to use std::string, std::getline, std::istringstream and >> operator instead:
using namespace std;
vector<string> myWords;
string line;
if (getline(cin, line))
{
istringstream is(line);
string word;
while (is >> word)
myWords.push_back(word);
}
std::vector<std::string> strings;
for (int i = 0; i < MAX_STRINGS && !cin.eof(); i++) {
std::string str;
std::cin >> str;
if (str.size())
strings.push_back(str);
}
I am trying to split a string using spaces as a delimiter. I would like to store each token in an array or vector.
I have tried.
string tempInput;
cin >> tempInput;
string input[5];
stringstream ss(tempInput); // Insert the string into a stream
int i=0;
while (ss >> tempInput){
input[i] = tempInput;
i++;
}
The problem is that if i input "this is a test", the array only seems to store input[0] = "this". It does not contain values for input[2] through input[4].
I have also tried using a vector but with the same result.
Go to the duplicate questions to learn how to split a string into words, but your method is actually correct. The actual problem lies in how you are reading the input before trying to split it:
string tempInput;
cin >> tempInput; // !!!
When you use the cin >> tempInput, you are only getting the first word from the input, not the whole text. There are two possible ways of working your way out of that, the simplest of which is forgetting about the stringstream and directly iterating on input:
std::string tempInput;
std::vector< std::string > tokens;
while ( std::cin >> tempInput ) {
tokens.push_back( tempInput );
}
// alternatively, including algorithm and iterator headers:
std::vector< std::string > tokens;
std::copy( std::istream_iterator<std::string>( std::cin ),
std::istream_iterator<std::string>(),
std::back_inserter(tokens) );
This approach will give you all the tokens in the input in a single vector. If you need to work with each line separatedly then you should use getline from the <string> header instead of the cin >> tempInput:
std::string tempInput;
while ( getline( std::cin, tempInput ) ) { // read line
// tokenize the line, possibly with your own code or
// any answer in the 'duplicate' question
}
Notice that it’s much easier just to use copy:
vector<string> tokens;
copy(istream_iterator<string>(cin),
istream_iterator<string>(),
back_inserter(tokens));
As for why your code doesn’t work: you’re reusing tempInput. Don’t do that. Furthermore, you’re first reading a single word from cin, not the whole string. That’s why only a single word is put into the stringstream.
The easiest way: Boost.Tokenizer
std::vector<std::string> tokens;
std::string s = "This is, a test";
boost::tokenizer<> tok(s);
for(boost::tokenizer<>::iterator it=tok.begin(); it != tok.end(); ++it)
{
tokens.push_back(*it);
}
// tokens is ["This", "is", "a", "test"]
You can parameter the delimiters and escape sequences to only take spaces if you wish, by default it tokenize on both spaces and punctuation.
Here a little algorithm where it splits the string into a list just like python does.
std::list<std::string> split(std::string text, std::string split_word) {
std::list<std::string> list;
std::string word = "";
int is_word_over = 0;
for (int i = 0; i <= text.length(); i++) {
if (i <= text.length() - split_word.length()) {
if (text.substr(i, split_word.length()) == split_word) {
list.insert(list.end(), word);
word = "";
is_word_over = 1;
}
//now we want that it jumps the rest of the split character
else if (is_word_over >= 1) {
if (is_word_over != split_word.length()) {
is_word_over += 1;
continue;
}
else {
word += text[i];
is_word_over = 0;
}
}
else {
word += text[i];
}
}
else {
word += text[i];
}
}
list.insert(list.end(), word);
return list;
}
There probably exists a more optimal way to write this.