I have a file with multiple lines.
lines contain integers separated by commas
In the following code it only parses the first line, but not the renaming lines. Any insight about I am doing wrong ?
void parseLines(std::ifstream &myfile){
std::string line, token;
std::stringstream ss;
int i;
vector<int> r;
while(myfile) {
getline(myfile, line);
ss.str("");
ss.str(line);
if(myfile){
cout << "SS" << ss.str() << endl;
while (std::getline(ss, token, ',')){
std::cout << token << std::endl;
}
}
}
}
Any insight about I am doing wrong?
The state of ss needs to be reset before data from the second line can be read.
Better yet, move the construction of ss inside the loop.
While at it,
replace while(myfile) by while(getline(myfile, line)).
Move the declaration of token inside the loop.
void parseLines(std::ifstream &myfile){
std::string line;
int i;
vector<int> r;
while( getline(myfile, line) )
{
std::stringstream ss(line);
std::string token;
while (std::getline(ss, token, ',')){
std::cout << token << std::endl;
}
}
}
The issue here is the stringstream is not local to the while loop. When you read from the stringstream the first time you exhaust the stream which causes the EOF flag to be set. If you do not clear that then you will never read any more information from it even if you load more. The simplest way to get around this is to make the stringstream local to the loop body so you start off with a fresh one on each iteration and you do no have to worry about cleaning up the flags. That would make your code look like
while(getline(myfile, line)) // also moved the line reading here to control when to stop the loop
{
std::stringstream ss(line);
while (std::getline(ss, token, ',')){
std::cout << token << std::endl;
}
}
Related
I have a text file with a series two strings delimited by a colon on each line.
I'm using getline to grab the entire line then string stream to split the two strings and put them onto a vector. The code works fine on the first pass it grabs the strings perfectly. Then after that on the 2nd pass of the while loop and so forth it doesn't grab the new input. The string stream seems to leave the original first values for some reason.
if (infile.is_open()) {
std::stringstream ss;
std::string current_line;
std::string tempProxy;
std::string tempPort;
while (std::getline(infile, current_line)) {
ss << current_line;
std::getline(ss, tempProxy, ':');
std::getline(ss, tempPort);
std::cout << tempProxy << " and " << tempPort << std::endl;
}
Any idea why it doesn't want to grab the strings from current_line on any pass except the first iteration?
You're reusing ss but not resetting it correctly. When you extract the second word from the first line, the stream is exhausted and put in an 'EOF' state. When streams are in this or any other 'error' state they don't do anything. You have to clear the error before you can continue to use them.
If you were to check for errors returned by operator<< and getline in the loop (or if you were to cause ss to throw exceptions on errors*) you would find they are indicating that they are not successful past the first iteration. It's a good general practice to always check for errors, and especially so when you're debugging.
You can clear the error by changing your loop:
while (std::getline(infile, current_line)) {
ss.clear(); // clears the error, not the contents
ss << current_line;
However doing this means that ss will accumulate all the lines in its internal buffer. The code will produce your expected output unless the file is large and you run out of memory or something like that.
You can see the accumulating internal buffer with the following:
while (std::getline(infile, current_line)) {
ss.clear();
ss << current_line;
std::cout << "ss internal buffer: " << ss.str();
Instead of using the formatted input to add ss you are probably better off using the .str() member to set it, which will replace the previous data instead of adding to it.
while (std::getline(infile, current_line)) {
ss.clear();
ss.str(current_line);
Alternatively you can construct a new stringstream in each iteration of the loop. This does ensure that no error states or data are carried over from previous iterations. It may also be slower, but you'll have to profile that for yourself.
while (std::getline(infile, current_line)) {
std::stringstream ss(current_line);
* Exceptions are nice because you don't need to remember to check them... except in cases like this where they're not enabled by default. Also I've noticed some C++ implementations have bugs in their iostreams exception code because people don't use it much.
I think you're looking for something like:
if (infile.is_open()) {
std::stringstream ss;
std::string current_line;
std::string tempProxy;
std::string tempPort;
while (std::getline(infile, current_line)) {
std::stringstream to_split;
to_split.str(current_line);
std::getline(to_split, tempProxy, ':');
std::getline(to_split, tempPort);
std::cout << tempProxy << " and " << tempPort << std::endl;
}
I am reading data from a file and putting it into string tokens like so:
std::vector<Mytype> mytypes;
std::ifstream file("file.csv");
std::string line;
while (std::getline(file, line)){
std::stringstream lineSs(line);
std::vector<std::string> tokens;
std::string token;
while (std::getline(lineSs, token, ',')){
tokens.push_back(token);
}
Mytype mytype(tokens[0], tokens[1], tokens[2], tokens[3]);
mytypes.push_back(mytype);
}
Obviously a pretty standard way of doing this. However the data has no NULL values, instead it will just be empty at that point. What I mean is the data may look something like this:
id0,1,2,3
id1,,2,
id2,,,3
The case of the middle line is causing me problems, because nothing is getting pushed back into my tokens vector after the "2", though there should be an empty string. Then I get some out_of_range problems when I try to create an instance of Mytype.
Until now I have been checking to see if the last char of each line is a comma, and if so, appending a space to the end of the line. But I was wondering if there was a better way to do this.
Thanks.
The difference is that line 2 has !lineSs.eof() before the last call to getline(). So you should stop the loop not if getline() returns false (note: this isn't really getline() returning false, but the stream being false when casted to bool); instead, stop it once lineSs.eof() returns true.
Here is a modification of your program that shows the idea:
int main() {
std::string line;
while (std::getline(std::cin, line)){
std::stringstream lineSs(line);
std::vector<std::string> tokens;
do {
std::string token;
std::getline(lineSs, token, ',');
tokens.push_back(token);
std::cout << "'" << token << "' " << lineSs.eof() << ' ' << lineSs.fail() << std::endl;
} while(!lineSs.eof());
std::cout << tokens.size() << std::endl;
}
}
It will show "3" on the last line for "1,2,3", and "4" for "1,2,3,".
A simple way to add a null string to the vector if the line ends with a comma is just to check for that before you create mytype. If you add
if (line.back() == ',')
tokens.push_back("");
After your inner while loop then this will add an empty string to tokens in the event that you end will a null column.
So
while (std::getline(lineSs, token, ',')){
tokens.push_back(token);
}
Becomes
while (std::getline(lineSs, token, ',')){
tokens.push_back(token);
}
if (line.back() == ',')
tokens.push_back("");
if(inputFile.is_open()){
while(!inputFile.eof()){
getline(inputFile, line);
ss << line;
while(ss){
ss >> key;
cout << key << " ";
lineSet.insert(lineNumber);
concordance[key] = lineSet;
}
lineNumber++;
}
}
For some reason, the while loop is kicking out after the first iteration and only displays the first sentence of my input file. The rest of the code works fine, I just can't figure out why it thinks the file has ended after the first iteration.
Thanks
Firstly you should be reading the file without using eof , as πάντα ῥεῖ notes (see here for explanation):
while( getline(inputFile, line) )
{
// process the line
}
Note that the preceding if is not necessary either.
The main problem , assuming ss is a stringstream you defined earlier, comes from the logic:
ss << line;
while(ss){
// stuff
}
The while loop here only exits when ss fails. But you never reset ss to be in a good state. So although your outer loop does read every line of the file, all of the lines after the first line never generate any output.
Instead you need to reset the stringstream each time:
ss.clear();
ss.str(line);
while (ss) {
// stuff
}
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 creating a program (In C++) that takes an ASCII file and reads a few values from each line until it reaches the end of the file. I am using ifstream to read the file, and I have never had problems with it stopping when I use the ifstream.eof() method. This time, however, even though it found the eof character in my test case, when I analyzed my other files, it is infinite looping because it never finds the eof character. Is this a coding issue, or an issue with my files?
string line = "";
unsigned long pos = 0;
ifstream curfile(input.c_str());
getline(curfile, line);
int linenumber = 0;
cout<<"About to try to read the file"<<endl;
if (!curfile.good())
cout<<"Bad file read"<<endl;
while (!curfile.eof())
{
cout<<"Getting line "<<linenumber<<endl;
linenumber++;
pos = line.find_first_of(' ');
line = line.substr(pos+1, line.size()-1);
pos = line.find_first_of(' ');
current.push_back(atof(line.substr(0, pos).c_str()));
for (int i = 0; i<4; i++)
{
pos = line.find_first_of(' ');
line = line.substr(pos+1, line.size()-1);
}
pos = line.find_first_of(' ');
dx.push_back(atof(line.substr(0, pos).c_str()));
pos = line.find_first_of(' ');
line = line.substr(pos+1, line.size()-1);
pos = line.find_first_of(' ');
dy.push_back(atof(line.substr(0, pos).c_str()));
getline(curfile, line);
}
EDIT: When I first run the loop, currentfile.good() returns false...what am I doing that causes it to return that?
First thing is first, you shouldn't check like that. eof() doesn't return true until after a failed read. But you can do better (and easier)!
check the stream state with the implicit conversion to void* which can be used in a bool context. Since most of the read operations on streams return a reference to the stream, you can write some very consice code like this:
std::string line;
while(std::getline(currentfile, line)) {
// process line
}
Basically what it is doing is saying "while I could successfully extract a line from currentfile, do the following", which is what you really meant to say anyway ;-);
Like I said, this applies to most stream operations, so you can do things like this:
int x;
std::string y;
if(std::cin >> x >> y) {
// successfully read an integer and a string from cin!
}
EDIT: The way I would rewrite your code is like this:
string line;
unsigned long pos = 0;
int linenumber = 0;
ifstream curfile(input.c_str());
std::cout << "About to try to read the file" << std::endl;
while (std::getline(curfile, line)) {
std::cout << "Getting line " << linenumber << std::endl;
linenumber++;
// do the rest of the work with line
}
Do not do it like that.
EOF is not the only thing you'll encounter while reading. There's a bunch of errors you might get, and so the best is to simply test the stream itself:
while(currentfile)
{
// read somehow
}
If you're reading lines, then, the simplest way is:
std::string line;
while(std::getline(currentfile, line))
{
// use line
}
Your first call to getline is triggering one of the fail-bits on the ifstream object. That is why if you do a check for a fail-bit using ios::good(), you never enter your read loop. I would check to see what the value of line is ... it's probably empty, meaning you're having another issue reading your file, like maybe permissions problems, etc.
The problem is here:
if (!curfile.good())
cout<<"Bad file read"<<endl; // OK you print bad.
while (!curfile.eof()) // But the loop is still entered.
// Another reason to **NEVER** to use
// while (file.eof()) // as bad does not mean eof
// though eof is bad
Try this:
void readFile(std::istream& str)
{
std::string line;
while(std::getline(str, line))
{
std::stringstream lineStream(line);
std::string ignoreWord;
int number[3];
lineStream >> ignoreWord // reads one space seporated word
>> number[0] // reads a number
>> ignoreWord >> ignoreWord >> ignoreWords // reads three words
>> number[1] // reads a number
>> number[2]; // reads a number
current.push_back(number[0]);
dx.push_back(number[1]);
dy.push_back(number[2]);
}
}