Mixing ifstream getline and >> [duplicate] - c++

This question already has answers here:
program skips over getline [duplicate]
(2 answers)
Closed 7 years ago.
After using a >> to input a float value, getline ceases to work correctly and reads trash. How can I avoid this?
string settingsFile = "TerrainSettings.txt";
ifstream settingsStream;
settingsStream.open(settingsFile.data());
assert (settingsStream.is_open());
string line;
getline(settingsStream,line); // fine here
getline(settingsStream,line); // fine here
getline(settingsStream,line); // fine here
float frequency;
float octaves;
float amplitude;
settingsStream>>frequency; // fine here
getline(settingsStream,line); // here it gets trash, why?
settingsStream>>octaves; // trash
getline(settingsStream,line);
settingsStream>>amplitude;
Edit: An inclusion of ignore(), generates the following error:
settingsStream>>frequency;
settingsStream.ignore(256,'\n');
getline(settingsStream,line); // fine here
settingsStream>>octaves; // trash here
getline(settingsStream,line);
settingsStream>>amplitude;
sample input:
/*Settings for Terrain Rendering, please input values on their respective lines*/
/**/
Frequency:
24
Octaves:
120
Amplitude:
1.25

First of all, start using std::strings and std::getline. Secondly, the reason for the trashing is that there's still a newline in the buffer. If you expect the user to be entering values one line at a time, you should use istream::ignore to skip everything up to and including the next newline character. For example:
std::string s;
float f;
std::cin >> f;
std::cin.ignore(big_num, '\n');
std::getline(std::cin, s);
What big_num should be depends on your expectations about the input. If you don't mind writing a lot and want to be on the safe side, use std::numeric_limits<std::streamsize>::max(). If you do mind writing a lot, make a constant of the suitable type that you use everywhere.
For example, the above code will parse the following so that f = 5.0f, s = Hello!.
5
Hello!
However, it will parse the following exactly the same way:
5 Hi!
Hello!
If you want to preserve the Hi!, you shouldn't ignore things, and instead define a proper grammar that you'll use to parse the document.

Related

How does one correctly store data into an array struct with stringstream? [duplicate]

This question already has answers here:
Why does reading a record struct fields from std::istream fail, and how can I fix it?
(9 answers)
Closed 6 years ago.
I was wondering how to store data from a CSV file into a structured array. I realize I need to use getline and such and so far I have come up with this code:
This is my struct:
struct csvData //creating a structure
{
string username; //creating a vector of strings called username
float gpa; //creating a vector of floats called gpa
int age; //creating a vector of ints called age
};
This is my data reader and the part that stores the data:
csvData arrayData[10];
string data;
ifstream infile; //creating object with ifstream
infile.open("datafile.csv"); //opening file
if (infile.is_open()) //error check
int i=0;
while(getline(infile, data));
{
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
i++;
}
Further, this is how I was attempting to print out the information:
for (int z = 0; z<10; z++)
{
cout<<arrayData[z].username<<arrayData[z].gpa<<arrayData[z].age<<endl;
}
However, when running this command, I get a cout of what seem to be random numbers:
1.83751e-0383 03 4.2039e-0453 1.8368e-0383 07011688
I assume this has to be the array running not storing the variables correctly and thus I am reading out random memory slots, however, I am unsure.
Lastly, here is the CSV file I am attempting to read.
username,gpa,age
Steven,3.2,20
Will,3.4,19
Ryan,3.6,19
Tom,3,19
There's nothing in your parsing code that actually attempts to parse the single line into the individual fields:
while(getline(infile, data));
{
This correctly reads a single line from the input file into the data string.
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
You need to try to explain to your rubber duck how this is supposed to take a single line of comma-separated values, like the one you showed in your question:
Steven,3.2,20
and separate that string into the individual values, by commas. There's nothing about the >> operator that will do this. operator>> separates input using whitespaces, not commas. Your suspicions were correct, you were not parsing the input correctly.
This is a task that you have to do yourself. I am presuming that you would like, as a learning experience, or as a homework assignment, to do this yourself, manually. Well, then, do it yourself. You have the a single line in data. Use any number of tools that C++ gives you: the std::string's find() method, or std::find() from <algorithm>, to find each comma in the data string, then extract each individual portion of the string that's between each comma. Then, you still need to convert the two numeric fields into the appropriate datatypes. And that's when you put each one of them into a std::istringstream, and use operator>> to convert them to numeric types.
But, having said all that, there's an alternative dirty trick, to solve this problem quickly. Recall that the original line in data contains
Steven,3.2,20
All you have to do is replace the commas with spaces, turning it into:
Steven 3.2 20
Replacing commas with spaces is trivial with std::replace(), or with a small loop. Then, you can stuff the result into a std::istringstream, and use operator>> to extract the individual whitespace-delimited values into the discrete variables, using the code that you've already written.
Just a small word of warning: if this was indeed your homework assignment, to write code to manually parse and extract comma-delimited values, it's not guaranteed that your instructor will give you the full grade for taking the dirty-trick approach...
UNDER CONSTRUCTION
Ton, nice try and nice complete question. Here is the answer:
1) You have a semicolon after the loop:
while(getline(infile, data));
delete it.
How did I figure that out easily? I compiled with all the warnings enabled, like this:
C02QT2UBFVH6-lm:~ gsamaras$ g++ -Wall main.cpp
main.cpp:24:33: warning: while loop has empty body [-Wempty-body]
while(getline(infile, data));
^
main.cpp:24:33: note: put the semicolon on a separate line to silence this warning
1 warning generated.
In fact, you should get that warning without -Wall as well, but get into using it, it will also make good to you! :)
2) Then, you read some elements, but not 10, so why do you print 10? Print as many as the ones you actually read, i.e. i.
When you try to print all 10 elements of your array, you print elements that are not initialized, since you didn't initialize your array of structs.
Moreover, the number of lines in datafile.csv was less than 10. So you started populating your array, but you stopped, when the file didn't have more lines. As a result, some of the elements of your array (the last 6 elements) remained uninitialized.
Printing uninitialized data, causes Undefined Behavior, that's why you see garbage values.
3) Also this:
if (infile.is_open()) //error check
could be written like this:
if (!infile.is_open())
cerr << "Error Message by Mr. Tom\n";
Putting them all together:
WILL STILL NOT WORK, BECAUSE ss >> arrayData[i].username; eats the entire input line and the next two extractions fail, as Pete Becker said, but I leave it here, so that others won't make the same attempt!!!!!!!
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;
struct csvData //creating a structure
{
string username; //creating a vector of strings called username
float gpa; //creating a vector of floats called gpa
int age; //creating a vector of ints called age
};
int main()
{
csvData arrayData[10];
string data;
ifstream infile; //creating object with ifstream
infile.open("datafile.csv"); //opening file
if (!infile.is_open()) { cerr << "File is not opened..\n"; }
int i=0;
while(getline(infile, data))
{
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
i++;
}
for (int z = 0; z< i; z++)
{
cout<<arrayData[z].username<<arrayData[z].gpa<<arrayData[z].age<<endl;
}
return 0;
}
Output:
C02QT2UBFVH6-lm:~ gsamaras$ g++ -Wall main.cpp
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
username,gpa,age00
Steven,3.2,2000
Will,3.4,1900
Ryan,3.6,1900
Tom,3,1900
But wait a minute, so now it works, but why this:
while(getline(infile, data));
{
...
}
didn't?
Because, putting a semicolon after a loop is equivalent to this:
while()
{
;
}
because as you probably already know loops with only one line as a body do not require curly brackets.
And what happened to what I thought it was the body of the loop (i.e. the part were you use std::stringstream)?
It got executed! But only once!.
You see, a pair of curly brackets alone means something, it's an anonymous scope/block.
So this:
{
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
i++;
}
functioned on its one, without being part of the while loop, as you intended too!
Any why did it work?! Because you had declared i before the loop! ;)

sstream not working properly? [duplicate]

This question already has an answer here:
How to clear stringstream? [duplicate]
(1 answer)
Closed 8 years ago.
I am using sstream to parse a string. The string contains an arbitrary number of integers separated by spaces. But sstream is not parsing the input properly. Here's my code-
#include<cstdio>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<sstream>
#include<iostream>
using namespace std;
vector<int> val[10010];
int main(){
int n,i,j,temp;
stringstream stream;
string s;
scanf("%d",&n);
vector<int>::iterator it;
for(i=0; i<n; i++){
getline(cin,s);
stream.str(s);
while(1) {
stream >> temp;
val[i].push_back(temp);
if(!stream)
break;
}
for(it=val[i].begin(); it!=val[i].end(); it++)
printf("%d ",*it);
printf("\n");
}
return 0;
}
Here are the test cases : Code
Not sure if this is your main error, but you have a \n - new line after scanf("%d",&n);, scanf is only grabbing integer, new line is left and getline is not reading your input.
You can ignore this new line with below, just before calling getline:
if ( isspace(cin.peek()) )
cin.ignore();
also, its better to:
if ( stream >> temp ) {
// do something with temp
}
now you always add string with previously parsed value as last element.
Also, you should clear stringstream before using it again, otherwise it stays in error state and you are not able to use it. Add this: stream.clear(); before stream.str(s);.
What's happening is clear. The first line you read is empty
(because you've left the '\n' in the stream when reading the
count), then you parse it with stream, ending with stream in
a failed state. And once stream is in a failed state, it
stays that way, and all further operations are no-ops.
More generally, there are a number of problems with your code:
Don't mix C style IO and iostream, especially not on the same
file. Sometimes it's unavoidable, when you have to deal with
legacy libraries, but don't do it intentionally. In fact,
there's never any reason to use C style IO in C++.
Don't define all of your variables at the top. Don't define
them until you need them. In particular, there's no point in
declaring the string stream until you're in the loop (which
would avoid the problem you're seeing).
Do check that std::getline has succeeded before using the
values you have read.
The idiomatic way of doing something like this would use
a vector of vectors, with push_back:
std::vector<std::vector<int>> val;
// and for the loop...
std::string line;
while ( std::getline( std::cin, line ) ) {
val.push_back( std::vector<int>() );
std::istringstream parse( line );
int i;
while ( parse >> i ) {
val.back().push_back( i );
}
}
Except, perhaps, with more error checking. (Did the parse >>
i in the while fail because it has read all of the line, or
because some one slipped an "abc" in as a number?)
Add cin.get (); after your scanf -- this consumes the \n at the end of your line of input where you're reading in n. After that, you'll stop getting nonsense in your vector. cin.ignore(SOME_BIG_NUMBER) might also work, skipping not just the next char (likely \n) but also spaces preceding it.
There's one other fix you need: in the innermost loop, if(!stream) break; must come before val[i].push_back(temp);, or you'll get an extra copy of the last int on your input line.
I further recommend that you either use stream I/O or C-style I/O (printf, scanf) and not both -- but that's not your problem here.

c++: istringstream

I am creating a simple command parser using c++, and I am trying to use istream >> to check whether I am inputting a number or a character.
input:
a = 10
b = a
parser.cpp:
string inputLine, varLeft, equal, varRight;
double varValue
// please see update below
while(getline(cin, inputLine)){
istringstream stringSplitter(inputLine);
stringSplitter >> varLeft >> equal;
if(!(stringSplitter >> varValue)){
stringSplitter >> varRight;
}
}
The goal is, later in the code, if varRight is empty, I assume that the line of input was a double literal, otherwise, it's a string that denotes a variable. I understand there might be logical errors associated with mixed input starting with digits, but I am assuming that all input is correctly formatted for now. Why is the a in the second line of input discarded? What fix do you propose?
Update
The problem is not with the while loop statement, it is with the if statement at the end of the while code block.
In the actual code, the loop is not actually a while loop; I am using a vector object holding string commands, and iterating through them using a for loop which goes through the vector using iterators. But, in order to please the commenters, I have fixed it above.
If an input function fails, the stream will not allow any more extractions until you clear the failure state. You'll need to do that, after you've checked that the input to varValue failed, with std::basic_ios::clear:
if(!(stringSplitter >> varValue)){
stringSplitter.clear();
stringSplitter >> varRight;
}
I don't know how you're doing /* not end of input */ at the moment (hopefully you're not checking eof()!), but it's recommended that you do:
while (getline(cin, inputLine)) {
// ...
}
This checks that the line input was successful before diving into the loop.

Reading a whole line from file in c++

I am working on a program in c++ and I need to read an entire string of text from a file. The file contains an address on one of the lines like this "123 Easy Ave" this has to be pulled into one string or char. Here is the code I have:
The data file:
Matt Harrold
307C Meshel Hall
1000 .01 4
The data is made up of a first and last name which become CustomerName, the next line is the address which is to populate Address, Then three numbers: principle, InterestRate, Years.
The code:
float InterestRate = 0;
int Years = 0;
char Junk [20];
int FutureValue;
float OnePlusInterestRate;
int YearNumber;
int count = 0;
char CustomerName;
string Address;
int * YearsPointer;
CustomerFile >> CustomerName
>> Address
>> Principle
>> InterestRate
>> Years;
Right now it only pulls in "103C" and stops at the space... Help is greatly appreciated!
Edit: In response to feedback on my question, I have edited it to use the more appropriate std::getline instead of std::istream::getline. Both of these would suffice, but std::getline is better suited for std::strings and you don't have to worry about specifying the string size.
Use std::getline() from <string>.
There is a good reference and example here: http://www.cplusplus.com/reference/string/getline/.
You'll also need to be careful combining the extraction (>>) operator and getline. The top answer to this question (cin>> not work with getline()) explains briefly why they shouldn't be used together. In short, a call to cin >> (or whatever input stream you are using) leaves a newline in the stream, which is then picked up by getline, giving you an empty string. If you really want to use them together, you have to call std::istream::ignore in between the two calls.
use std::getline from the <string> header.

C++ Getline simple error [duplicate]

This question already has an answer here:
What am I not understanding about getline+strings?
(1 answer)
Closed 7 years ago.
I would like to point I am not good in C++, I usually use C but i thought that get to know some C++ basic will be good so I need to send data to server and normal std::cin >> variable can't do it because it only reads input to space so I read about getline and it's working great, but when I do something liek this in infinite loop:
for (;;)
{
std::cout << "Hello" << std::endl;;
std::getline(std::cin,darek.wiadomosc);
}
Durning first itteration it shows on screen double Hello like:
Hello
Hello
String that is being entered
But after one loop it shows everything good. It's problem with getline I'm sure because when I changed it to std::cin >> just for test it worked. Can anybody answer to my simple question?
std::cin leaves the terminator in the stream (be it a space or a newline).
So if you do:
std::string foo, bar;
std::cin >> foo;
std::getline(std::cin, bar);
with input as:
Hello
World
bar will end up with the empty string, not "World", because getline will stop at the first newline, which is the one after "Hello".
To bring this back to your question, if you have instances of std::cin >> before the start of your loop, you may have a stray newline, making the first iteration behave in unexpected ways. You can use std::cin.ignore to ignore any newlines left in the stream.