Skip line reading file with string::getline in C++ - c++

I'm using the following code to get lines out of a file until "#" is not found. It works, but I don't understand why it gets the same line twice. How do I avoid it, i.e., skip a line after string::find(...) has been called?
do {
getline(file, line);
} while(line.find("#") != string::npos);

Simply put, it doesn't.
You're not performing any checks whatsoever for end-of-input.
It's only after the second iteration, and the second call to getline, that EOF is set on the stream and the .find() operation fails.
The EOF flag is not set when EOF is reached, but after an attempt to read past EOF fails.
Write this:
do {
} while (getline(file, line) && line.find("#") != string::npos);
Oops! Now your loop body is empty, and there are much better ways of writing this.
Perhaps:
while (getline(file, line) && line.find("#") != string::npos)) {}
That's better. =)

You should check if you have reached the end of your file before you check the string it returns.
do {
getline(file, line);
} while(file.good() && line.find("#") != string::npos);

Related

C++: Using getline to input from a text file either skips the first line or messes up the rest

I'm trying to read in from a specially formatted text file to search for specific names, numbers, etc. In this case I want to read the first number, then get the name, then move on to the next line. My problem seems to be with while loop condition for reading through the file line by line. Here is a sample of the txt file format:
5-Jon-4-Vegetable Pot Pie-398-22-31-Tue May 07 15:30:22
8-Robb-9-Pesto Pasta Salad-143-27-22-Tue May 07 15:30:28
1-Ned-4-Vegetable Pot Pie-398-22-31-Tue May 07 15:30:33
I'll show you two solutions I've tried, one that skips the first line in the file and one that doesn't take in the very last line. I've tried the typical while(!iFile.eof()) as a last ditch effort but got nothing.
transactionLog.clear();
transactionLog.seekg(0, std::ios::beg);
std::string currentName, line, tempString1, tempString2;
int restNum, mealNum;
bool nameFound = false;
int mealCount[NUMMEALS];
std::ifstream in("patronlog.txt");
while(getline(in, line))
{
getline(in, tempString1, '-');
getline(in, currentName, '-');
if(currentName == targetName)
{
if(getline(in, tempString2, '-'))
{
mealNum = std::stoi(tempString2);
mealCount[mealNum - 1] += 1;
nameFound = true;
}
}
I believe I understand what's going in this one. The "getline(in, line)" is taking in the first line entirely, and since I'm not using it, it's essentially being skipped. At the very least, it's taking in the first number, followed by the name, and then doing the operations correctly. The following is the modification to the code that I thought would fix this.
while(getline(in, tempString1, '-'))
{
getline(in, currentName, '-');
// same code past here
}
I figured changing the while loop condition to the actual getline of the first item in the text file would work, but now when I look at it through the debugger, on the second loop it sets tempString1 to "Vegetable Pot Pie" rather than the next name on the next line. Ironically though this one does fine on line #1, but not for the rest of the list. Overall I feel like this has gotten me farther from my intended behavior than before.
You need to parse the contents of lines after they are read. You can use a std::istringstream to help you with that.
while(getline(in, line))
{
// At this point, the varible line contains the entire line.
// Use a std::istringstream to parse its contents.
std::istringstream istr(line);
getline(istr, tempString1, '-'); // Use istr, not in.
getline(istr, currentName, '-'); // ditto
...
}

ifstream eof function (C++)

I have this code:
string a = "D:\\Users\\user-pc\\Desktop\\testing\\a.txt";
ifstream f;
/*edit*/ string line;
/*edit*/ getline(f, line);
f.open(a);
if ( f.eof() )
cout << "ended";
else
cout << "nope"
and the file 'a.txt' which has nothing in it.
the output is nope, alway nope. I don't get it.. Do I use it wrong?
EDIT: still .eof() not working
std::basic_istream<...>::eof() only returns true if a recent extraction set the appropriate bit in the stream state. When a file stream is constructed, its stream state is always that of std::ios_base::goodbit.
An alternative is to check if the next available character is the EOF character:
if (f.peek() == std::char_traits<char>::eof())
eofbit error state flag is only set once a read tries to read past the end of the file.

String has a '\n' too much

I have made a Class called FileReader. This is in my read function of this class. It opens a file and reads it. Of course it puts the content of the file in a variable called "content" of my class. It's at the last line.
std::string file_content;
std::string temp;
std::ifstream file;
file.open(filepath,std::ios_base::in);
while(!file.eof()){
temp.clear();
getline(file, temp);
file_content += temp;
file_content += '\n';
}
file_content = file_content.substr(0, file_content.length()-1); //Removes the last new line
file.close();
content = file_content;
The file I am opening has the following content :
"Hello\nWhat's up\nCool".
Of course I didn't write exactly \n in my textfile. But as you can see there is no new line at the end.
My problem is, "content" has, whenever I print it to the screen, a new line at the end. But I removed the last new line... What's wrong?
Classic error, using eof before you read instead of after. This is correct
while (getline(file, temp))
{
file_content += temp;
file_content += '\n';
}
or if you must use eof, remember to use it after getline not before.
for (;;)
{
getline(file, temp);
if (file.eof()) // eof after getline
break;
file_content += temp;
file_content += '\n';
}
It's incredible how many people think that eof can predict whether the next read will have an eof problem. But it doesn't, it tells you that the last read had an eof problem. It's been like this throughout the entire history of C and C++ but it's obviously counter-intuitive because many, many people make this mistake.
eof doesn't get set until you attempt to read past the end of the file. Your loop is iterating four times for three lines; the last iteration reads no data, though.
The more correct way to do this is to change your while loop to while (std::getline(file, temp)); this will terminate the loop when it reaches the end of the file, after the third read.

This loop is executing more iterations than is expected

I am having problems with the following code. What I expect is for the do-while loop to execute 4 times, once for each line of the text file it is reading in, but in reality is it executing five time, which is resulting in a segfault later in the program. What am I doing wrong here that's causing it to execute the extra iteration? I've tried replacing he do-while with a simple while loop but the result is the same.
int count = 0;
string devices[4];
string line;
ifstream DeviceList;
DeviceList.open("devices/device_list.txt");
do
{
getline(DeviceList, line);
devices[count] = line;
count ++;
} while(!DeviceList.eof());
device_list.txt contains the following:
WirelessAdaptor
GPU
CPU
Display
I think your loop should probably look more like this:
Edit: Added check to ignore empty lines
while (getline(DeviceList, line))
{
if (line.length() > 0)
{
devices[count] = line;
++count;
}
}
eof() doesn't return true until getline consumes the end. It doesn't do this until the getline call after reading the last line. You need to check if eof is true immediately after your getline call:
while(true)
{
getline(DeviceList, line);
if(DeviceList.eof())
break;
}
eof() won't return true until you attempt to read more data than there is left.
Above the line getline(DeviceList, line); insert cout << line.length() << endl; and tell us what happens.
Your text file probably contains a line feed after the last line, so getline reads an empty string before the loop actually ends.

C++ reading from a file blocks any further writing. Why?

I am implementing a very simple file database. I have 2 basic operations:
void Insert(const std::string & i_record)
{
//create or append to the file
m_fileStream.open(m_fileName.c_str(), std::ios::out | std::ios::app);
if (m_fileStream.is_open())
{
m_fileStream << i_record << "\n";
}
m_fileStream.flush();
m_fileStream.close();
}
/*
* Returns a list with all the items in the file.
*/
std::vector<std::string> SelectAll()
{
std::vector<std::string> results;
m_fileStream.open(m_fileName.c_str(), std::ios::in);
std::string line;
if (m_fileStream.is_open())
{
while (!m_fileStream.eof())
{
getline (m_fileStream, line);
results.push_back(line);
}
}
m_fileStream.close();
return results;
}
the class has m_fileStream and m_fileName as private members.
OK - here's the problem:
If I do something like:
db->Insert("a");
db->SelectAll();
db->Insert("b");
The end result is that the file will contain only "a"; WHY?
NOTE: it seems that getline() will set the fail bit. but why?
Change
while (!m_fileStream.eof())
{
getline (m_fileStream, line);
results.push_back(line);
}
to
while (getline (m_fileStream, line))
{
results.push_back(line);
}
Otherwise you will get one additional empty line at the end. eof() will return true only once you tried to read past the end of the file, and not if only the next read would be past the end of the file.
It sets the failbit because getline tries to extract characters from the stream. If there are no characters left (and no '\n' has been seen yet), stream.get(c) to a character will set the failbit. Then getline will set the eofbit and then .eof() will return true, and your loop exits.
If you don't want failbit set, then change your condition from !stream.eof() to stream.peek() != EOF (and make sure there is a trailing newline in your file).
This now is also the solution to your problem: .close() doesn't .clear() your stream, so the failbit still is set if you reopen your file. call stream.clear() after reading your stuff in, and then it works.
I think litb pretty much nailed it. But just to add my $0.02:
1) I always favored:
while ( stream && (stream.peek() != EOF) ) {...}
As [bad] events other than EOF can occur.
(And, as mentioned by litb, peek()!=EOF gets around the problem of stream not setting EOF until we try to read past the end.)
.
2) Since "m_fileStream" is open'ed, read/written/flushed, and closed in both these methods...
Why not declare it locally on the stack? Doing so assures that no previous state issues remain behind to mess you up. And yer accessing the disk, so efficiency may not be the largest concern...
Besides, you can be lazy:
ifstream stream ( m_fileName.c_str() );
ASSERT( stream, !=, NULL ); // Uses my own ASSERT macro && stream.operator().
while ( stream && (stream.peek() != EOF) ) {...}