Columned Text grabbing in c++ - c++

I have a file that looks like the following
61101
test
3 69.7139 65.3935 22.2632
3 69.7708 65.6131 21.467
2 69.8974 66.0987 20.7391
I am trying to have it so that the first two lines are skipped and I average the last three columns as long as the first column is not 4.
this is what I am trying at the moment, but it doesn't really seem to be working.
getline(frames_file,tempS);
getline(frames_file,tempS);
while(frames_file.good())
{
if(typePart != 4)
{
frames_file >> typePart >> posPart[0] >> posPart[1] >> posPart[2];
numLipid++;
aPos[0] = aPos[0] + posPart[0];
aPos[1] = aPos[1] + posPart[1];
aPos[2] = aPos[2] + posPart[2];
}
}
aPos[0] = aPos[0]/numLipid;
aPos[1] = aPos[1]/numLipid;
aPos[2] = aPos[2]/numLipid;
cout << " " << aPos[0] << " " << aPos[1] << " " << aPos[2];
this did not seem to grab any values

I see multiple problems here.
getline(frames_file,tempS);
getline(frames_file,tempS);
while(frames_file.good())
Suppose the file only contained two lines. The expected result here is to just read those two lines, and finish.
Unfortunately, good() will still be true here, so the rest of the code will go off the rails. good(), generally speaking, indicates that the input stream's prior operations succeeded. Well, here the prior operations succeeded. You read the two lines successfully.
good() should be checked after attempting the next read operation, to determine if it succeeded, or failed.
if(typePart != 4)
{
frames_file >> typePart >> posPart[0] >> posPart[1] >> posPart[2];
Can you explain to your rubber duck how you expect typePart to be set to 4 before you read the next line in question? Based on your question, as I undestood it, you should be reading every line, and only performing calculations when the first column is 4. Here, you mysteriously expect typePart to be set to 4 even before you read the line in question.
To summarize:
Use std::getline() to read every line in your file, not just the first two. This is in order for you to next step correctly:
AFTER each call to std::getline(), check either good(), or eof() to determine if std::getline() failed because you reached the end of the file. If so, stop at this point, otherwise:
Stuff your read line into a std::istringstream, and use it to actually extract the pieces of the line using operator>>, if that's how you prefer to parse it. Or, use any other approach to parsig that you prefer.
Check if typePart is 4, and then perform all of your calculations AFTER you extracted the parts of the line, not before.

Related

C++ Why diff EOF checks recommended for text vs numeric?

My textbook recommends using the member accessor method iStreamVar.eof() when dealing with textual data and while (iStreamVar) when dealing with numeric data.
Can someone please explain why it would matter?
Quote from book:
Using the function eof to determine the end-of-file status works best if the input is text. The earlier method of determining the end-of-file status works best if the input consists of numeric data.
That is the only thing mentioned on the topic. After this, it just explains how the process works.
Which method you use for determining the end of data depends on how you use it. My guess is, both methods which your textbook mentions are used wrong, so they fail in different situations. That's why it recommends using different methods in different situations.
The correct method is not trivial, and it depends on how important error resilience is for you.
If you want to read a space-delimited stream with numbers in it, and you are sure the file contains no errors, the code is simplest:
int value;
while (iStreamVar >> value)
{
...
}
Note that it's not any of the two original options.
If your file contains space-delimited textual data, and you are sure there are no errors, use the same code (but declare the temporary variable as string instead of int).
If you want to detect and recover from errors, use more elaborate code. But I cannot recommend you any specific code structure - it depends on what exactly you want to do in case of errors. Also:
Are text records delimited by space or newline?
What if the input text-file contains an empty line?
Numbers - floating-point or not?
Numbers - if there is a stray character like a among number data, what to do?
So there is no single correct recipe for doing proper input with error resilience.
Unless there is something significant in the context that isn't shown in the question, that quote is nonsense.
The way to read from a file and check for success is to read from the file:
int data;
if (std::cin >> data)
std::cout << "read succeeded, value is " << data << '\n';
std::string data;
if (std::cin >> data)
std::cout << "read succeeded, value is " << data << '\n';
std::string data;
if (std::getline(std::cin, data)
std::cout << "read succeeded, value is " << data << '\n';
If an attempted read fails you can call .eof() to find out whether the failure was because the input was at the end of the file. Contrary to what some beginners expect (and what some languages do), if .eof() returns false it does not mean that there is data remaining in the input stream. The stream might be at the end of the file after a successful read consumed the remaining input. .eof() will return false, but the next attempted read will fail, and after that, .eof() will return true.
std::stringstream input("1234");
int data;
input >> data; // succeeds
std::cout << input.eof() << '\n'; // outputs 0, no failure
input >> data; // fails, no more input
std::cout << input.eof() << '\n'; // outputs 1, failed because at end of file

c++ ifstream Skipping Data

I'm trying to get my program to read two lines out of a six line data file (the other four are two sets of two that are meant to be read to other objects). However, I can only get it to read one -- either the first or the second depending on how I manipulate the code.
Here's what's in my data file:
Mustang Sally
123.45
George Porge
11.99
J. D. Rockerfeller
56321.3
And here's the section of the code where I need to read said data:
void account::readAccount(ifstream &inFile)
{
while (getline(inFile, name))
{
inFile.ignore();
inFile >> savings;
}
}
The code above is only reading in the second line.
I think I'm having a phantom newline problem, which I can't seem to resolve, but I also feel that there's another problem on top of that, which I can't comprehend with my current level of experience regarding file streams.
The code above is only reading in the second line.
Yes because you tell it to ignore. I don't know what exactly two lines you want to get from these, but based on the codes, I'm assuming that you want to read the values at line 2,4. The following code will print out those two lines.
float savings = 0.0f;
while(getline(inFile,line))
{
if(savings > 0.0f) cout << savings << endl;
inFile >> savings;
inFile.ignore(1000, '\n' );
}

How exactly does the extract>> operator works in C++

I am a computer science student, an so do not have much experience with the C++ language (considering it is my first semester using this language,) or coding for that matter.
I was given an assignment to read integers from a text file in the simple form of:
19 3 -2 9 14 4
5 -9 -10 3
.
.
.
This sent me of on a journey to understand I/O operators better, since I am required to do certain things with this stream (duh.)
I was looking everywhere and could not find a simple explanation as to how does the extract>> operator works internally. Let me clarify my question:
I know that the extractor>> operator would extract one continues element until it hits space, tab, or newline. What I try to figure out is, where would the pointer(?) or read-location(?) be AFTER it extracts an element. Will it be on the last char of the element just removed or was it removed and therefore gone? will it be on the space/tab/'\n' character itself? Perhaps the beginning of the next element to extract?
I hope I was clear enough. I lack all the appropriate jargon to describe my problem clearer.
Here is why I need to know this: (in case anyone is wondering...)
One of the requirements is to sum all integers in each line separately.
I have created a loop to extract all integers one-by-one until it reaches the end of the file. However, I soon learned that the extract>> operator ignores space/tab/newline. What I want to try is to extract>> an element, and then use inputFile.get() to get the space/tab/newline. Then, if it's a newline, do what I gotta do.
This will only work if the stream pointer will be in a good position to extract the space/tab/newline after the last extraction>>.
In my previous question, I tried to solve it using getline() and an sstring.
SOLUTION:
For the sake of answering my specific question, of how operator>> works, I had to accept Ben Voigt's answer as the best one.
I have used the other solutions suggested here (using an sstring for each line) and they did work! (you can see it in my previous question's link) However, I implemented another solution using Ben's answer and it also worked:
.
.
.
if(readFile.is_open()) {
while (readFile >> newInput) {
char isNewLine = readFile.get(); //get() the next char after extraction
if(isNewLine == '\n') //This is just a test!
cout << isNewLine; //If it's a newline, feed a newline.
else
cout << "X" << isNewLine; //Else, show X & feed a space or tab
lineSum += newInput;
allSum += newInput;
intCounter++;
minInt = min(minInt, newInput);
maxInt = max(maxInt, newInput);
if(isNewLine == '\n') {
lineCounter++;
statFile << "The sum of line " << lineCounter
<< " is: " << lineSum << endl;
lineSum = 0;
}
}
.
.
.
With no regards to my numerical values, the form is correct! Both spaces and '\n's were catched:
Thank you Ben Voigt :)
Nonetheless, this solution is very format dependent and is very fragile. If any of the lines has anything else before '\n' (like space or tab), the code will miss the newline char. Therefore, the other solution, using getline() and sstrings, is much more reliable.
After extraction, the stream pointer will be placed on the whitespace that caused extraction to terminate (or other illegal character, in which case the failbit will also be set).
This doesn't really matter though, since you aren't responsible for skipping over that whitespace. The next extraction will ignore whitespaces until it finds valid data.
In summary:
leading whitespace is ignored
trailing whitespace is left in the stream
There's also the noskipws modifier which can be used to change the default behavior.
The operator>> leaves the current position in the file one
character beyond the last character extracted (which may be at
end of file). Which doesn't necessarily help with your problem;
there can be spaces or tabs after the last value in a line. You
could skip forward reading each character and checking whether
it is a white space other than '\n', but a far more idiomatic
way of reading line oriented input is to use std::getline to
read the line, then initialize an std::istringstream to
extract the integers from the line:
std::string line;
while ( std::getline( source, line ) ) {
std::istringstream values( line );
// ...
}
This also ensures that in case of a format error in the line,
the error state of the main input is unaffected, and you can
continue with the next line.
According to cppreference.com the standard operator>> delegates the work to std::num_get::get. This takes an input iterator. One of the properties of an input iterator is that you can dereference it multiple times without advancing it. Thus when a non-numeric character is detected, the iterator will be left pointing to that character.
In general, the behavior of an istream is not set in stone. There exist multiple flags to change how any istream behaves, which you can read about here. In general, you should not really care where the internal pointer is; that's why you are using a stream in the first place. Otherwise you'd just dump the whole file into a string or equivalent and manually inspect it.
Anyway, going back to your problem, a possible approach is to use the getline method provided by istream to extract a string. From the string, you can either manually read it, or convert it into a stringstream and extract tokens from there.
Example:
std::ifstream ifs("myFile");
std::string str;
while ( std::getline(ifs, str) ) {
std::stringstream ss( str );
double sum = 0.0, value;
while ( ss >> value ) sum += value;
// Process sum
}

C++ istream operator>> bad-data handling

Every time I ask a question here on SO, it turns out to be some very dumb mistake (check my history if you don't believe me), so bear with me if you can here.
It feels like my question should be very popular, but I couldn't find anything about it and I've run out of ideas to try.
Anyway, without further ado:
I'm trying to overload the input operator>>. It's supposed to read one integer at a time from a file, skipping invalid data such as chars, floats, etc.
Naturally, I'm checking if(in >> inNum) to both get() the next token and check for successful get().
If successful, not much to say there.
If it fails, however, I assume that one of two things happened:
It stumbled upon a non-integer
It reached the eof
Here's how I tried to deal with it:
istream& operator>> (istream& in, SortSetArray& setB) {
bool eof = false;
int inNum = -1;
while(!eof) {
if(in >> inNum) {
cout << "DEBUG SUCCESS: inNum = " << inNum << endl;
setB.insert(inNum);
}
else {
// check eof, using peek()
// 1. clear all flags since peek() returns eof regardless of what
// flag is raised, even if it's not `eof`
in.clear();
cout << "DEBUG FAIL: inNum = " << inNum << endl;
// 2. then check eof with peek()
eof = (in.peek() == std::char_traits<char>::eof());
}
}
return in;
}
The file contains [1 2 3 4 a 5 6 7], and the program naturally goes into infinite loop.
Okay, easy guess, peek() doesn't consume the char 'a', and maybe in >> inNum also failed to consume it somehow. No biggie, I'll just try something that does.
And that's pretty much where I've been for the last 2 hours. I tried istream::ignore(), istream::get(), ios::rdstate to check eof, double and string instead of char in the file, just in case char is read numerically.
Nothing works and I'm desperate.
Weirdly enough, the approach above worked for a previous program where I had to read a triplet of data entries on a line of the format: string int int
The only difference is I used an ifstream object for that one, and an istream object for this one.
Bonus Question: inNum has the value of 0 when the hiccup occurs. I'm guessing it's something that istream::operator>> does?
Implementation description
try to read an int
if successful;
insert the read value to setB
next iteration
else;
clear error flags
check so that we haven't reached the end of the file
still more data? next iteration.
The above is the logic description of your function, but there's something missing...
In case we try to read a value, but fail, std::istream's handle these cases by setting the approriate error flags, but it will not discard any data.
The problem with your implementation is that upon trying to read invalid data, you will just try to read the same invalid data again.. over, and over, and over, inf.
Solution
After clearing the error flags you can use std::istream::ignore to discard any data from the stream.
The function's 1st argument is the max number of potential chars to ignore, and the 2nd is the "if you hit this char, don't ignore any more*.
Let's ignore the maximum amount of characters, or until we hit ' ' (space):
#include <limits> // std::numeric_limits
in.ignore (std::numeric_limits<std::streamsize>::max(), ' ');

Can't get ios::beg to go back to the beginning of the file

It always seems to be the things that should be no problem that cause problems for me. I don't get it. :/
So I'm trying to make sure that I understand how to manipulate text files. I've got two files, "infile.txt" and "outfile.txt". "infile.txt" has six numbers in it and nothing else. Here is the code I used to manipulate the files.
#include<fstream>
using std::ifstream;
using std::ofstream;
using std::fstream;
using std::endl;
using std::ios;
int main()
{
ifstream inStream;
ofstream outStream;//create streams
inStream.open("infile.txt", ios::in | ios::out);
outStream.open("outfile.txt");//attach files
int first, second, third;
inStream >> first >> second >> third;
outStream << "The sum of the first 3 nums is " << (first+second+third) << endl;
//make two operations on the 6 numbers
inStream >> first >> second >> third;
outStream << "The sum of the second 3 nums is " << (first+second+third) << endl;
inStream.seekg(0); //4 different ways to force the program to go back to the beginning of the file
//2. inStream.seekg(0, ios::beg);
//3. inStream.seekg(0, inStream.beg);
//4. inStream.close(); inStream.open("infile.txt");
//I have tried all four of these lines and only #4 works.
//There has got to be a more natural option than just
//closing and reopening the file. Right?
inStream >> first >> second >> third;
outStream << "And again, the sum of the first 3 nums is " << (first+second+third) << endl;
inStream.close();
outStream.close();
return 0;
}
Maybe I don't understand quite how the stream works, but I've seen a few sources that said that seekg(0) should move the index back to the start of the file. Instead, this is what I get out of it.
The sum of the first 3 nums is 8
The sum of the second 3 nums is 14
And again, the sum of the first 3 nums is 14
It went back, but not nearly in the way I would have hoped. Any idea why this happened? Why did my first three attempts fail?
As Bo Persson states, it may be because your input has
encountered end of file; it shouldn't, because in C++, a text
file is defined as being terminated by a '\n', but practically
speaking, if you're working under Windows, a lot of ways of
generating a file will omit this final '\n'—although it
is formally required, practical considerations will mean that
you'll make sure that it works even if the final '\n' is
missing. And I can't think of any other reason off hand why the
seekg's wouldn't work. inStream.seekg( 0 ) is, of course,
undefined behavior, but in practice, it will work pretty much
everywhere. inStream.seekg( 0, ios::beg ) is guaranteed to
work if inStream.good(), and is, IMHO, preferable to the
first form. (The single argument form of seekg is normally
only used with the results of a tellg as an argument.) And of
course, it only works if the actual input source supports
seeking: it won't work if you're reading from a keyboard or
a pipe (but presumably, "infile.txt" is neither).
In general, you should check the status of inStream after each
read, before using the results. But if the only problem is that
the file doesn't end with '\n', it's probable that the status
will be OK (!fail()) after the final read, even if you've
encountered end of file. In which case, you'll need clear()
anyway.
Note that the above comments are valid for C++-03 and precedent.
C++11 has changed the specification of the single argument form
of seekg, and requires it to reset eofbit before anything
else. (Why is this change only for the single argument form of
seekg, and not the two argument form? Oversight?)
The second input reaches end-of-file for the stream. That state sticks until you call inStream.clear() to clear its state (in addition to the seek).
With a C++11 compliant compiler, option 4 should also work as close and reopen will now clear the previous state. Older compilers might not do that.
Try:
inStream.seekg(0, ios_base::beg);