I'm sure there's a newbie mistake here, but I can't for the life of me figure it out.
I'm trying to use getline() to read a two word line (first and last names), and then sort the names into member fields of a struct. However, getline() seems to be rolling past the end of the lines and appending the first word of the next line to the last word of the line it should be getting.
In other words, when trying to read the last word of a line, getline() is reading that word and the first word of the next line.
The input file is of the form:
Seattle Mariners
Norichiki Aoki
Seth Smith
Robinson Cano
Here's my code:
struct Player {
string firstName;
string lastName;
float avg;
};
struct Team {
Player roster[8];
string teamName;
};
Team home = {};
stringstream iss;
string token;
string lineread;
while (getline(inFile, lineRead, '\n')){
iss << lineRead;
if (getline(iss, token, ' '))
{
if(s % 2 == 0)
home.roster[s/2].firstName = lineRead;
else
home.roster[s/2].lastName = lineRead;
}
s++;
cout << "token:" << token << endl;
}
The output I'm getting looks like this:
token: Seattle
token: MarinersNorichiki
token: AokiSeth
token: SmithRobinson
But I would like to have
token: Seattle
token: Mariners
token: Norichiki
token: Aoki
std::cin.getline() can run into problems when used with std::cin >> var.
getline can be provided a third argument--a "stop" character. This character ends getline's input. The character is eaten and the string is terminated. Example:
std::cin.getline(str, 100, '|')
If std::cin.getline() is not provided a "stop" character as a third argument, it will stop when it reaches a newline.
Given:
float fl;
std::cin >> fl;
char str[101]
std::cin.getline(str, 101);
And you type: 3.14
3.14 is read into fl . The newline following the 3.14 is still sitting on the input buffer.
std::cin.getline(str, 101) immediately processes the newline that is still on the input buffer. str becomes an empty string.
The illusion is that the application "skipped" the std::cin.getline() statement.
The solution is to add std::cin.ignore(); immediately after the first std::cin statement. This will grab a character off of the input buffer (in this case, newline) and discard it.
std::cin.ignore() can be called three different ways:
No arguments: A single character is taken from the input buffer and discarded:
std::cin.ignore(); //discard 1 character
One argument: The number of characters specified are taken from the input buffer and discarded:
std::cin.ignore(33); //discard 33 characters
Two arguments: discard the number of characters specified, or discard characters up to and including the specified delimiter (whichever comes first):
std::cin.ignore(26, '\n'); //ignore 26 characters or to a newline, whichever comes first
Try something more like this instead:
struct Player {
string firstName;
string lastName;
float avg;
};
struct Team {
Player roster[8];
string teamName;
};
Team home = {};
int s = 0;
string line;
while (getline(inFile, line))
{
istringstream iss(line);
iss >> home.roster[s].firstName;
iss >> home.roster[s].lastName;
home.roster[s].avg = ...;
cout << "first: " << home.roster[s].firstName << ", last: " << home.roster[s].lastName << endl;
if (++s == 8) break;
}
Related
I am trying to read from a file like this
1 23 5 15
3 18 8 6 11
But then whenever it goes to the last string "15", it also reads "3" from then next line.
Here is my code for this part:
ifstream myFileRent;
myFileRent.open("rented.txt");
if (!myFileRent.is_open())
{
cout << "ERROR: File is corrupted or does not exists!";
}
while (!myFileRent.eof())
{
getline(myFileRent, customerID, ' ');
while (getline(myFileRent, video_ID, ' '))
{
InsertCusRent(stoi(customerID), video_ID);
}
}
This is what it shows in debug when I am in the "15" before the next line "3"
video_ID : "15\n3" : std::string
Basically, first digit of the line will go to customerID, and every next digits will be pushed in to stack, which is why I used while because every line are not equal in length.
Thanks for every comments, code is finally working!
string line, customerID, video_ID;
ifstream myFileRent;
myFileRent.open("rented.txt");
if (!myFileRent.is_open())
{
cout << "ERROR: File is corrupted or does not exists!";
}
while (!myFileRent.eof())
{
getline(myFileRent, line);
stringstream ss(line);
getline(ss, customerID, ' ');
while (getline(ss, video_ID, ' '))
{
if (customerID != " ")
{
InsertCusRent(stoi(customerID), video_ID);
}
}
}
I would do it like this:
string line;
ifstream myFileRent;
myFileRent.open("rented.txt");
if (!myFileRent.is_open())
{
cout << "ERROR: File is corrupted or does not exists!";
}
// Don't use eof here.
// Even if it is not eof does not mean that there is data to read.
// The next read may still fail. So try and read the data if it
// fails the while loop will not enter.
while (getline(myFileRent, line))
{
// You have a line. Lets use it in a stream.
stringstream ss(std::move(line));
// You convert customerID into an integer. Just
// make it an integer now.
int customerID;
std::string video_ID;
// Use the operator>> to read the customer ID into an integer.
ss >> customerID;
// To read simple white space separated values use operator>>
// It will read ints or string as appropriate.
while (ss >> video_ID)
{
InsertCusRent(customerID, video_ID);
}
}
PS. You can get your WORKING code reviewed once working at https://codereview.stackexchange.com/
Basically I first takes an integer as input and then test case follows. My each test case is an string. I am suppose to print the string back if the starting patten of string matches "HI A" and it is case-insensitive. I wrote the code below to accomplish to this. My problem is that when I press enter after each input, getline takes newline character as new input. I have tried to tackle this by using extra getline after each input but the issue is still there. Program gets stuck in the loop even though I have put a break condition. What am I doing wrong?
#include <iostream>
#include <string>
using namespace std;
int main(){
int N;
cin >>N;
string nl;
getline(cin,nl);
for (int i=0;i<N;i++){
string s;
getline(cin,s);
//cout <<"string"<<s<<endl;
int flag=0;
if ((s.at(0)=='h'||s.at(0)=='H')&&(s.at(1)=='i'||s.at(1)=='I')&&(s.at(2)==' ')&&(s.at(3)=='a'||s.at(3)=='A')) flag=1;
if (flag==1) cout << s;
//cout << "not " <<s;
string ne;
cout << "i="<< i<<endl;
if (i==N-1) {break;}
getline(cin,ne);
}
}
Here is sample input:
5
Hi Alex how are you doing
hI dave how are you doing
Good by Alex
hidden agenda
Alex greeted Martha by saying Hi Martha
Output should be:
Hi Alex how are you doing
ignore() function does the trick. By default, it discards all the input suquences till new line character.
Other dilimiters and char limit can be specified as well.
http://www.cplusplus.com/reference/istream/istream/ignore/
In your case it goes like this.
cin >> N;
cin.ignore();
Your cin >>N stops at the first non-numeric character, which is the newline. This you have a getline to read past it, that's good.
Each additional getline after that reads the entire line, including the newline at the end. By putting in a second getline you're skipping half your input.
So, your real problem isn't that getline eats newlines, but that your second getline(cin, ne) is eating a line...
And that is because you mistakenly think that you need two getline operations to read one line - or something like that. Mixing "linebased" and "itembased" input does have confusing ways to deal with newlines, so you do need something to "skip" the newline left behind frin cin >> N;, but once you have got rid of that, you only need ONE getline to read up and including the newline at the end of a line.
I am writing this answer with the hopes that it may help someone else out there that wants a very simple solution to this problem.
In my case the problem was due to some files having different line endings such as '\r' vs. '\n'. Everything worked fine in windows but then it failed in Linux.
The answer was actually simple. I created a function removeNewLineChar after each line was read in. That way the char was removed. The removeNewLineChar takes in the line that was read in and copies it over character by character into a new string but it avoids copying either of the newline characters.
Here is an example:
string trim(string line)
{
string newString;
for (char ch : line)
{
if (ch == '\n' || ch == '\r')
continue;
newString += ch;
}
return newString;
}
//some function reading a file
while (getline(fin, line)) {
line = trim(line);
//... do something with the line
line = "";
}
you just need to accept the fact that getline will give you '\n' at the end. One solution is remove '\n' after getting it. Another solution is do not write the additional 'endl'. for example, for your problem, you can use this code
int N;
cin >> N;
string line;
getline(cin, line); // skip the first new line after N.
for (int i = 0; i < N; i++) {
string line;
getline(cin, line);
string first4 = line.substr(0, 4);
// convert to upper case.
std::transform(first4.begin(), first4.end(), first4.begin(), std::ptr_fun<int, int>(std::toupper)); // see http://en.cppreference.com/w/cpp/algorithm/transform
if (first4 == "HI A") {
cout << line; // do not include "<< endl"
}
}
cin.ignore() worked for me.
void House::provideRoomName()
{
int noOfRooms;
cout<<"Enter the number of Rooms::";
cin>>noOfRooms;
cout<<endl;
cout<<"Enter name of the Rooms::"<<endl;
cin.ignore();
for(int i=1; i<=noOfRooms; i++)
{
std::string l_roomName;
cout<<"Room"<<"["<<i<<"] Name::";
std::getline(std::cin, l_roomName);
}
}
std::string line;
std::cin>>std::ws; // discard new line not processed by cin
std::getline(std::cin,line);
From Notes section https://en.cppreference.com/w/cpp/string/basic_string/getline
When consuming whitespace-delimited input (e.g. int n; std::cin >> n;) any whitespace that follows, including a newline character, will be left on the input stream. Then when switching to line-oriented input, the first line retrieved with getline will be just that whitespace. In the likely case that this is unwanted behaviour, possible solutions include:
An explicit extraneous initial call to getline
Removing consecutive whitespace with std::cin >> std::ws
Ignoring all leftover characters on the line of input with cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
I recently bought a C++ Primer and got stuck with a problem. I have to read a sequence of words using cin and store the values in a vector. After having unusual problems, I found out that while(cin >> words) invites problems (like infinite loop) if you expect invalid inputs: Using cin to get user input
int main()
{
string words;
vector<string> v;
cout << "Enter words" << endl;
while (cin >> words)
{
v.push_back(words);
}
for(auto b : v)
cout << b << " ";
cout << endl;
return 0;
}
Therefore, I'm trying to find an alternative to this problem. Help ?
That link you provided regarding input problems is a little different. It's talking about when you expect the user to enter a particular value, but you might fail to read the value (let's say it's an integer) because something else was entered. In that case, it's good to use getline to retrieve a whole line of input and then parse the value out.
In your case, you're just after words. When you read a string from a stream, it will give you all consecutive non-whitespace characters. And, ignoring punctuation for a moment, you can call that a "word". So when you talk about 'invalid input', I don't see what you mean. The loop will continue to give you "words" until there are none left in the stream, at which point it will error:
vector<string> words;
string word;
while( cin >> word ) words.push_back(word);
However, if you expect the user to enter all words on one line and press enter to finish, then you need to use getline:
// Get all words on one line
cout << "Enter words: " << flush;
string allwords;
getline( cin, allwords );
// Parse words into a vector
vector<string> words;
string word;
istringstream iss(allwords);
while( iss >> word ) words.push_back(word);
Or you can do this:
cout << "Enter words, one per line (leave an empty line when done)\n";
vector<string> words;
string line;
while( getline(cin, line) )
{
// Because of the word check that follows, you don't really need this...
if( line.size() == 0 ) break;
// Make sure it's actually a word.
istringstream iss(line);
string word;
if( !(iss >> word) ) break;
// If you want, you can check the characters and complain about non-alphabet
// characters here... But that's up to you.
// Add word to vector
words.push_back(word);
}
I know about getline() but it would be nice if cin could return \n when encountered.
Any way for achieving this (or similar)?
edit (example):
string s;
while(cin>>s){
if(s == "\n")
cout<<"newline! ";
else
cout<<s<<" ";
}
input file txt:
hola, em dic pere
caram, jo també .
the end result shoud be like:
hola, em dic pere newline! caram, jo també .
If you are reading individual lines, you know that there is a newline after each read line. Well, except for the last line in the file which doesn't have to be delimited by a newline character for the read to be successful but you can detect if there is newline by checking eof(): if std::getline() was successful but eof() is set, the last line didn't contain a newline. Obviously, this requires the use of the std::string version of std::getline():
for (std::string line; std::getline(in, line); )
{
std::cout << line << (in.eof()? "": "\n");
}
This should write the stream to std::cout as it was read.
The question asked for the data to be output but with newlines converted to say "newline!". You can achieve this with:
for (std::string line; std::getline(in, line); )
{
std::cout << line << (in.eof()? "": "newline! ");
}
If you don't care about the stream being split into line but actually just want to get the entire file (including all newlines), you can just read the stream into a std::string:
std::string file((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
Note, however, that this exact approach is probably fairly slow (although I know that it can be made fast). If you know that the file doesn't contain a certain character, you can also use std::getline() to read the entire file into a std::string:
std::getline(in, file, 0);
The above code assumes that your file doesn't contain any null characters.
A modification of #Dietmar's answer should do the trick:
for (std::string line; std::getline(in, line); )
{
std::istringstream iss(line);
for (std::string word; iss >> word; ) { std::cout << word << " "; }
if (in.eof()) { std::cout << "newline! "; }
}
Just for the record, I ended up using this (I wanted to post it 11h ago)
string s0, s1;
while(getline(cin,s0)){
istringstream is(s0);
while(is>>s1){
cout<<s1<<" ";
}
cout<<"newline! ";
}
I am currently doing c++ and am going through how to take in an sentence through a string and reverse the words (This is a word......word a is This etc)
I have looked at this method:
static string reverseWords(string const& instr)
{
istringstream iss(instr);
string outstr;
string word;
iss >> outstr;
while (iss >> word)
{
outstr = word + ' ' + outstr;
}
return outstr;
}
int main()
{
string s;
cout << "Enter sentence: ";
getline(cin, s);
string sret = reverseWords(s);
cout << reverseWords(s) << endl;
return 0;
}
I have gone through the function and kind of understand but I am a bit confused as to EXACTLY what is going on at
iss >> outstr;
while (iss >> word)
{
outstr = word + ' ' + outstr;
}
return outstr;
Can anybody explain to me the exact process that is happening that enables the words to get reversed?
Thank you very much
iss is an istringstream, and istringstreams are istreams.
As an istream, iss has the operator>>, which reads into strings from its string buffer in a whitespace delimeted manner. That is to say, it reads one whitespace separated token at a time.
So, given the string "This is a word", the first thing it would read is "This". The next thing it would read would be "is", then "a", then "word". Then it would fail. If it fails, that puts iss into a state such that, if you test it as a bool, it evaluates as false.
So the while loop will read one word at a time. If the read succeeds, then the body of the loop appends the word to the beginning of outstr. If it fails, the loop ends.
iss is a stream, and the >> is the extraction operator. If you look upon the stream as a continuous line of data, the extraction operator removes some data from this stream.
The while loop keep extracting words from the stream until it is empty (or as long as the stream is good one might say). The inside of the loop is used to add the newly extracted word to the end of the outstr
Look up information about c++ streams to learn more.
The instruction:
istringstream iss(instr);
allows instr to be parsed when the operator>> is used, separating words thourgh a whitespace character. Each time the operator >> is used it makes iss point to the next word of the phrase stored by instr.
iss >> outstr; // gets the very first word of the phrase
while (iss >> word) // loop to get the rest of the words, one by one
{
outstr = word + ' ' + outstr; // and store the most recent word before the previous one, therefore reversing the string!
}
return outstr;
So the first word retrieved in the phrase is actually stored in the last position of the output string. And then all the subsequent words read from the original string will be put before the previous word read.