std::fstream error - c++

I'm having some trouble parsing a file
The last two lines of the file I want to parse are:
f 814/866/896 1035/1100/989 817/965/898
[nothing, effect from \n]
This is how I read the file:
while(!inFile.eof())
{
inFile>>sCommand;
if(sCommand == L"#")
{}
else if(sCommand == L"f")
{
int iPos, iTex, iNorm;
iPos=iTex=iNorm = -1;
for(auto face=0; face<3; ++face)
{
inFile>>iPos;
--iPos;
if(inFile.peek() == L'/')
{
inFile.ignore();
inFile>>iTex;
--iTex;
if(inFile.peek() == L'/')
{
inFile.ignore();
inFile>>iNorm;
--iNorm;
}
}
objVertexIndex iObj;
iObj.iPos=iPos;
iObj.iTex=iTex;
iObj.iNorm=iNorm;
this->AddVertex(iObj);
}
m_MaterialIndices_C.push_back(m_CurrentMaterial);
} //END IF
inFile.ignore( 1000, '\n' );
} //END WHILE
inFile.close();
However, I have some trouble with that last line of the file that contains nothing.
Before the last line of the file, inFile.ignore( 1000, '\n' ); will happen and I would expect std::fstream::eof() to be detected, but for some reason it's not.
And apparently sCommand will still be the same command from the previous line if there is nothing on a line, which is giving me some trouble.
Is there a way to check for this? And if yes, how?

Not really an answer but a comment (I don't know how to comment). If you have 2 \n after the last line with numbers eof will not trigger. I had similar problems using .eof() and might be better to check the content of what you read as a condition to keep reading or not.

Related

Storing the last value of a file from SD card using arduino

I am making a device that moves back and fourth and needs to store its last position so that upon power up, the last stored value can be grabbed from the last line of the file on an SD card, and it can resume operation. This file will then be destroyed and re-written. For this particular application homing and other methods can not be used because it must start in the spot it last was. Due to position tracking via encoder, there is no positional memory otherwise.The file is setup to be a single data column seperated by commas.
Currently I am successfully writing to the SD card as position changes, and reading the entire file to be printed on the Serial monitor. However, I really only need the last value. The length of the file will always be different do to system operation.
I have read a lot of different solutions but none of them seem to work for my application.
I can read the entire file using:
void read_file() {
// open the file for reading:
myFile = SD.open("test8.txt");
if (myFile) {
Serial.println("test8.txt:");
// read from the file until there's nothing else in it:
// read from the file until there's nothing else in it:
while (myFile.available()) {
String a = "";
for (int i = 0; i < 9; ++i)
{
int j;
char temp = myFile.read();
if (temp != ',' && temp != '\r')
{ //a=temp;
a += temp;
}
else if (temp == ',' || temp == '\r') {
j = a.toInt();
// Serial.println(a);
Serial.println(j);
break;
}
}
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test8.txt");
}
}
This gives me a stream of the values separated by 0 like this:
20050
0
20071
0
20092
0
20113
0
20133
0
Ideally I just need 20133 to be grabbed and stored as an int.
I have also tried:
void read_file_3() {
// open the file for reading:
myFile = SD.open("test8.txt");
if (myFile) {
Serial.println("test8.txt:");
// read from the file until there's nothing else in it:
Serial.println(myFile.seek(myFile.size()));
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
}
This only returns "1", which does not make any sense to me.
Update:
I have found a sketch that does what I want, however it is very slow due to the use of string class. Per post #6 here: https://forum.arduino.cc/index.php?topic=379209.0
This does grab the last stored value, however it takes quite awhile as the file gets bigger, and may blow up memory.
How could this be done without the string class?
void read_file() {
// open the file for reading:
myFile = SD.open("test8.txt");
if (myFile) {
while (myFile.available())
{
String line_str = myFile.readStringUntil(','); // string lavue reading from the stream - from , to , (coma to comma)
int line = line_str.toInt();
if (line != 0) // checking for the last NON-Zero value
{
line2 = line; // this really does the trick
}
// Serial.print(line2);
// delay(100);
}
Serial.print("Last line = ");
Serial.print(line2);
// close the file:
myFile.close();
// SD.remove("test3.txt");
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
}
Any help would be greatly appreciated!
seek returns true if it succesffuly goes to that position and false if it does not find anything there, like for instance if the file isn't that big. It does not give you the value at that position. That's why you see a 1, seek is returning true that it was able to go to the position (myFile.size()) and that's what you're printing.
Beyond that, you don't want to go to the end of the file, that would be after your number. You want to go to a position 5 characters before the end of the file if your number is 5 digits long.
Either way, once you seek that position, then you still need to use read just like you did in your first code to actually read the number. seek doesn't do that, it just takes you to that position in the file.
EDIT: Since you edited the post, I'll edit the answer to go along. You're going backwards. You had it right the first time. Use the same read method you started with, just seek the end of the file before you start reading so you don't have to read all the way through. You almost had it. The only thing you did wrong the first time was printing what you got back from seek instead of seeking the right position and then reading the file.
That thing you looked up with the String class is going backward from where you were. Forget you ever saw that. It's doing the same thing you were already doing in the first place only it's also wasting a lot of memory and code space in the process.
Use your original code and just add a seek to skip to the end of the file.
This assumes that it's always a 5 digit number. If not then you may need a little bit of tweaking:
void read_file() {
// open the file for reading:
myFile = SD.open("test8.txt");
if (myFile) {
Serial.println("test8.txt:");
/// ADDED THIS ONE LINE TO SKIP MOST OF THE FILE************
myFile.seek(myFile.size() - 5);
// read from the file until there's nothing else in it:
// read from the file until there's nothing else in it:
while (myFile.available()) {
String a = "";
for (int i = 0; i < 9; ++i)
{
int j;
char temp = myFile.read();
if (temp != ',' && temp != '\r')
{ //a=temp;
a += temp;
}
else if (temp == ',' || temp == '\r') {
j = a.toInt();
// Serial.println(a);
Serial.println(j);
break;
}
}
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test8.txt");
}
}
See, all I've done is take your original function and add a line to seek the end to it.

copying after a line has been found from a file from that position till the end of that file in c++

I have a file which holds protein coordinates as well as other information preceding it. My aim is to look for a certain line called "$PARAMETERS" and then copy from there every line succeeding it till the end of the file.
How can I get that done? This is the small code I wrote part of the entire program (that someone else wrote years ago, and I took over to upgrade his code for my research):
ifstream InFile;
InFile.open (DC_InFile.c_str(), ios::in);
while ( not InFile.eof() )
{
Line = NextLine (&InFile);
if (Line.find ("#") == 0) continue; // skip lines starting with # (comments)
if (Line.length() == 0) continue; // skip empty lines
size_t pos = Line.find("$PARAMETERS");
Line.copy(Line.begin("$PARAMETERS")+pos, Line.end("$END"));
&Line.copy >> x_1 >> y_2 >> z_3;
}
Bearing in mind that I defined Line as string
I guess you need to read data between $PARAMETERS and $END, not from $PARAMETERS until end of file. If so, you can use the following code:
string str;
while (getline(InFile, str))
{
if (str.find("#") == 0)
continue;
if (str.length() == 0)
continue;
if (str.find("$PARAMETERS") == 0)
{
double x_1, y_2, z_3; // you want to read numbers, i guess
while (getline(InFile, str))
{
if (str.find("$END") == 0)
break;
stringstream stream(str);
if (stream >> x_1 >> y_2 >> z_3)
{
// Do whatever you want with x_1, y_2 and z_3
}
}
}
}
This will handle multiple sections of data; not sure if you really want this behavior.
For example:
# comment
$PARAMETERS
1 2 3
4 5 6
$END
#unrelated data
100 200 300
$PARAMETERS
7 8 9
10 11 12
$END
I'm not sure what you want on the first line of the copied file but assuming you get that straight and you haven't read beyond the current line, you can copy the tail of the fike you are reading like this:
out << InFile.rdbuf();
Here out is the std::ostream you want to send the data to.
Note, that you should not use InFile.eof() to determine whether there is more data! Instead, you should read what you want to read and then check that the read was successful. You need to check after reading because the stream cannot know what you are trying to read before you have done so.
Following up on Dietmar's answer: it sounds to me like you
should be using std::getline until you find a line which
matches your pattern. If you want that line as part of your
output, then output it, then use Dietmar's solution to copy the
rest of the file. Something like:
while ( std::getline( in, line ) && ! isStartLine( line ) ) {
}
if ( in ) { // Since you might not have found the line
out << line << '\n'; // If you want the matching line
// You can also edit it here.
out << in.rdbuf();
}
And don't put all sorts of complicated parsing information,
with continue and break, in the loop. The results are both
unreadable and unmaintainable. Factor it out into a simple
function, as above: you'll also have a better chance of getting
it right. (In your case, should you match "$PARAMETERS #
xxx", or not?) In a separate function, it's much easier to get
it right.

Skip reading a line in a INI file if its length greater than n in C++

I want to skip reading a line in the INI file if has more than 1000 characters.This is the code i'm using:
#define MAX_LINE 1000
char buf[MAX_LINE];
CString strTemp;
str.Empty();
for(;;)
{
is.getline(buf,MAX_LINE);
strTemp=buf;
if(strTemp.IsEmpty()) break;
str+=strTemp;
if(str.Find("^")>-1)
{
str=str.Left( str.Find("^") );
do
{
is.get(buf,2);
} while(is.gcount()>0);
is.getline(buf,2);
}
else if(strTemp.GetLength()!=MAX_LINE-1) break;
}
//is.getline(buf,MAX_LINE);
return is;
...
The problem i'm facing is that if the characters exceed 1000 if seems to fall in a infinite loop(unable to read next line).How can i make the getline to skip that line and read the next line??
const std::size_t max_line = 1000; // not a macro, macros are disgusting
std::string line;
while (std::getline(is, line))
{
if (line.length() > max_line)
continue;
// else process the line ...
}
How abut checking the return value of getline and break if that fails?
..or if is is an istream, you could check for an eof() condition to break you out.
#define MAX_LINE 1000
char buf[MAX_LINE];
CString strTemp;
str.Empty();
while(is.eof() == false)
{
is.getline(buf,MAX_LINE);
strTemp=buf;
if(strTemp.IsEmpty()) break;
str+=strTemp;
if(str.Find("^")>-1)
{
str=str.Left( str.Find("^") );
do
{
is.get(buf,2);
} while((is.gcount()>0) && (is.eof() == false));
stillReading = is.getline(buf,2);
}
else if(strTemp.GetLength()!=MAX_LINE-1)
{
break;
}
}
return is;
For something completely different:
std::string strTemp;
str.Empty();
while(std::getline(is, strTemp)) {
if(strTemp.empty()) break;
str+=strTemp.c_str(); //don't need .c_str() if str is also a std::string
int pos = str.Find("^"); //extracted this for speed
if(pos>-1){
str=str.Left(pos);
//Did not translate this part since it was buggy
} else
//not sure of the intent here either
//it would stop reading if the line was less than 1000 characters.
}
return is;
This uses strings for ease of use, and no maximum limits on lines. It also uses the std::getline for the dynamic/magic everything, but I did not translate the bit in the middle since it seemed very buggy to me, and I couldn't interpret the intent.
The part in the middle simply reads two characters at a time until it reaches the end of the file, and then everything after that would have done bizarre stuff since you weren't checking return values. Since it was completely wrong, I didn't interpret it.

getline() reads an extra line

ifstream file("file.txt");
if(file.fail())
{
cout<<"Could not open the file";
exit(1);
}
else
{
while(file)
{
file.getline(line[l],80);
cout<<line[l++]<<"\n";
}
}
I am using a two dimensional character array to keep the text (more than one line) read from a file to count the number of lines and words in the file but the problem is that getline always reads an extra line.
Your code as I'm writing this:
ifstream file("file.txt");
if(file.fail())
{
cout<<"Could not open the file";
exit(1);
}
else
{
while(file)
{
file.getline(line[l],80);
cout<<line[l++]<<"\n";
}
}
The first time getline fails, you still increment the line counter and output the (non-existing) line.
Always check for an error.
extra advice: use std::string from the <string> header, and use its getline function.
cheers & hth.
The problem is when you're at the end of the file the test on file will still succeed because you have not yet read past the end of file. So you need to test the return from getline() as well.
Since you need to test the return from getline() to see if it succeeded, you may as well put it right in the while loop:
while (file.getline(line[l], 80))
cout << line[l++] << "\n";
This way you don't need a separate test on file and getline().
This will solve your problem:
ifstream file("file.txt");
if(!file.good())
{
cout<<"Could not open the file";
exit(1);
}
else
{
while(file)
{
file.getline(line[l],80);
if(!file.eof())
cout<<line[l++]<<"\n";
}
}
Its more robust
Does the file end with a newline? If it does, the EOF flag will not be triggered until one extra loop passes. For example, if the file is
abc\n
def\n
Then the loop will be run 3 times, the first time it will get abc, the second time it will get def and the third time it will get nothing. That's probably why you see an additional line.
Try checking the failbit on the stream AFTER the getline.
Only do the cout if file.good() is true. The extra line you're seeing comes from the last call to file.getline() which reads past the end of the file.

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.