Finding end of file while reading from it - c++

void graph::fillTable()
{
ifstream fin;
char X;
int slot=0;
fin.open("data.txt");
while(fin.good()){
fin>>Gtable[slot].Name;
fin>>Gtable[slot].Out;
cout<<Gtable[slot].Name<<endl;
for(int i=0; i<=Gtable[slot].Out-1;i++)
{
**//cant get here**
fin>>X;
cout<<X<<endl;
Gtable[slot].AdjacentOnes.addFront(X);
}
slot++;
}
fin.close();
}
That's my code, basically it does exactly what I want it to but it keeps reading when the file is not good anymore. It'll input and output all the things I'm looking for, and then when the file is at an end, fin.good() apparently isn't returning false. Here is the text file.
A 2 B F
B 2 C G
C 1 H
H 2 G I
I 3 A G E
F 2 I E
and here is the output
A
B
F
B
C
G
C
H
H
G
I
I
A
G
E
F
I
E
Segmentation fault
-
Here's is Gtable's type.
struct Gvertex:public slist
{
char Name;
int VisitNum;
int Out;
slist AdjacentOnes;
//linked list from slist
};
I'm expecting it to stop after outputting 'E' which is the last char in the file. The program never gets into the for loop again after reading the last char. I can't figure out why the while isn't breaking.

Your condition in the while loop is wrong. ios::eof() isn't
predictive; it will only be set once the stream has attempted
(internally) to read beyond end of file. You have to check after each
input.
The classical way of handling your case would be to define a >>
function for GTable, along the lines of:
std::istream&
operator>>( std::istream& source, GTable& dest )
{
std::string line;
while ( std::getline( source, line ) && line.empty() ) {
}
if ( source ) {
std::istringstream tmp( line );
std::string name;
int count;
if ( !(tmp >> name >> count) ) {
source.setstate( std::ios::failbit );
} else {
std::vector< char > adjactentOnes;
char ch;
while ( tmp >> ch ) {
adjactentOnes.push_back( ch );
}
if ( !tmp.eof() || adjactentOnes.size() != count ) {
source.setstate( std::ios::failbit );
} else {
dest.Name = name;
dest.Out = count;
for ( int i = 0; i < count; ++ i ) {
dest.AdjacentOnes.addFront( adjactentOnes[ i ] );
}
}
}
}
return source;
}
(This was written rather hastily. In real code, I'd almost certainly
factor the inner loop out into a separate function.)
Note that:
We read line by line, in order to verify the format (and to allow
resynchronization in case of error).
We set failbit in the source stream in case of an input error.
We skip empty lines (since your input apparently contains them).
We do not modify the target element until we are sure that the input
is correct.
One we have this, it is easy to loop over all of the elements:
int slot = 0;
while ( slot < GTable.size() && fin >> GTable[ slot ] ) {
++ slot;
}
if ( slot != GTable.size )
// ... error ...
EDIT:
I'll point this out explicitly, because the other people responding seem
to have missed it: it is absolutely imperative to ensure that you have
the place to read into before attempting the read.
EDIT 2:
Given the number of wrong answers this question is receiving, I would
like to stress:
Any use of fin.eof() before the input is known to fail is wrong.
Any use of fin.good(), period, is wrong.
Any use of one of the values read before having tested that the input
has succeeded is wrong. (This doesn't prevent things like fin >> a >>
b, as long as neither a or b are used before the success is
tested.)
Any attempt to read into Gtable[slot] without ensuring that slot
is in bounds is wrong.
With regards to eof() and good():
The base class of istream and ostream defines three
“error” bits: failbit, badbit and eofbit. It's
important to understand when these are set: badbit is set in case of a
non-recoverable hardward error (practically never, in fact, since most
implementations can't or don't detect such errors); and failbit is set in
any other case the input fails—either no data available (end of
file), or a format error ("abc" when inputting an int, etc.).
eofbit is set anytime the streambuf returns EOF, whether this
causes the input to fail or not! Thus, if you read an int, and the
stream contains "123", without trailing white space or newline,
eofbit will be set (since the stream must read ahead to know where the
int ends); if the stream contains "123\n", eofbit will not be set.
In both cases, however, the input succeeds, and failbit will not be
set.
To read these bits, there are the following functions (as code, since I
don't know how to get a table otherwise):
eof(): returns eofbit
bad(): returns badbit
fail(): returns failbit || badbit
good(): returns !failbit && !badbit && !eofbit
operator!(): returns fail()
operator void*(): returns fail() ? NULL : this
(typically---all that's guaranteed is that !fail() returns non-null.)
Given this: the first check must always be fail() or one of the
operator (which are based on fail). Once fail() returns true, we
can use the other functions to determine why:
if ( fin.bad() ) {
// Serious problem, disk read error or such.
} else if ( fin.eof() ) {
// End of file: there was no data there to read.
} else {
// Formatting error: something like "abc" for an int
}
Practically speaking, any other use is an error (and any use of good()
is an error—don't ask me why the function is there).

Slightly slower but cleaner approach:
void graph::fillTable()
{
ifstream fin("data.txt");
char X;
int slot=0;
std::string line;
while(std::getline(fin, line))
{
if (line.empty()) // skip empty lines
continue;
std::istringstream sin(line);
if (sin >> Gtable[slot].Name >> Gtable[slot].Out && Gtable[slot].Out > 0)
{
std::cout << Gtable[slot].Name << std::endl;
for(int i = 0; i < Gtable[slot].Out; ++i)
{
if (sin >> X)
{
std::cout << X << std::endl;
Gtable[slot].AdjacentOnes.addFront(X);
}
}
slot++;
}
}
}
If you still have issues, it's not with file reading...

The file won't fail until you actually read from past the end of file. This won't occur until the fin>>Gtable[slot].Name; line. Since your check is before this, good can still return true.
One solution would be to add additional checks for failure and break out of the loop if so.
fin>>Gtable[slot].Name;
fin>>Gtable[slot].Out;
if(!fin) break;
This still does not handle formatting errors in the input file very nicely; for that you should be reading line by line as mentioned in some of the other answers.

Try moving first two reads in the while condition:
// assuming Gtable has at least size of 1
while( fin>>Gtable[slot].Name && fin>>Gtable[slot].Out ) {
cout<<Gtable[slot].Name<<endl;
for(int i=0; i<=Gtable[slot].Out-1;i++) {
fin>>X;
cout<<X<<endl;
Gtable[slot].AdjacentOnes.addFront(X);
}
slot++;
//EDIT:
if (slot == table_size) break;
}
Edit: As per James Kanze's comment, you're taking an adress past the end of Gtable array, which is what causes segfault. You could pass the size of Gtable as argument to your fillTable() function (f.ex. void fillTable(int table_size)) and check slot is in bounds before each read.

*Edited in response to James' comment - the code now uses a good() check instead of a
!eof() check, which will allow it to catch most errors. I also threw in an is_open()
check to ensure the stream is associated with the file.*
Generally, you should try to structure your file reading in a loop as follows:
ifstream fin("file.txt");
char a = '\0';
int b = 0;
char c = '\0';
if (!fin.is_open())
return 1; // Failed to open file.
// Do an initial read. You have to attempt at least one read before you can
// reliably check for EOF.
fin >> a;
// Read until EOF
while (fin.good())
{
// Read the integer
fin >> b;
// Read the remaining characters (I'm just storing them in c in this example)
for (int i = 0; i < b; i++)
fin >> c;
// Begin to read the next line. Note that this will be the point at which
// fin will reach EOF. Since it is the last statement in the loop, the
// file stream check is done straight after and the loop is exited.
// Also note that if the file is empty, the loop will never be entered.
fin >> a;
}
fin.close();
This solution is desirable (in my opinion) because it does not rely on adding random
breaks inside the loop, and the loop condition is a simple good() check. This makes the
code easier to understand.

Related

A way to not accept float as input c++

I'm working on a project where I need to check for the correct input (n), I currently have a bit of code which won't allow string to be entered as it will ask again for a correct amount. I'm having trouble writing a code that won't allow float numbers to get through as it currently just ignores the float part of the input. I'm sorry if this is a simple question, but I haven't found a way to get around this yet.
for(int i=0; i<1; ++i)
{
string b1;
int e;
do
{
getline(cin,b1);
e=atoi(b1.c_str());
}
while(e==0 && b1!="0");
n=e; // where n is the user input
}
Assuming you consider anything with decimal point ('.') or using scientific notation with a negative exponent as a non-acceptable floating point number, just check if the entered string contains one of those:
std::string::iterator it;
if (b1.end() != std::find(b1.begin(), b1.end(), '.')
|| (b1.end() != (it = std::find_if(b1.begin(), b1.end(),
[](char c){ return c == 'e' || c == 'E'; })
&& it + 1 != b1.end()
&& '-' == it[1])) {
// deal with the string being a floating point number with a fractional part
}
Note, that this will consider, e.g., "10e-1" to be a bad value although it is actually just a fancy spelling of "1".
If you enter a float value then it will have a decimal point (.). As your input is a string hence you can do the following check :-
do
{
getline(cin,b1);
if(bi.find(".")!=b1.npos); // search if a decimal point is present or not
cout<<"wrong input";
else
e = stoi(b1); // stoi works similar to atoi but on strings
}
First thing you want to do is not repeat the code over and over ever time you want to read an integer. Make a function:
int getInt(std::istream & in)
This will take any input stream, cin, a file, a std::stringstream, whatever. Next we set up a few local variables we need.
{
std::string b1;
int e;
Now we build the input loop
while (std::getline(in, b1))
This will loop until the input stream fails. If it never fails and the user can't get their act togehter, we'll be here for a long long time. With Gilligan. The Skipper too. Maybe we can bum some money off of Mr. Howell for start-up seed capital, eh?
{
size_t pos;
Catch any exceptions thrown by the string -to-int conversion
try
{
Convert to int. pos will be updated with the character that ended the conversion. If it is not the end of the string, the string does not contain an int. If it does contain an int, we exit the function and return the int.
e = std::stoi(b1, &pos);
if (pos == b1.length())
{
return e;
}
}
We don't really need to do anything in the catch block. You could output a message to instruct or mock the user if you wish.
catch (...)
{
}
}
If we got here, the IO stream failed and we need to let folks know. Throw an exception.
// IO failure. throw exception
}
Usage is simple:
int value = getInt(cin);
You may wish to wrap the call in an try-catch block to catch the IO failure exception. cin's failure cases are pretty weird and usually fatal, though.
When calling getInt on a file you will want to handle things more carefully because end of file is a common occurrence.
All together without the running commentary:
int getInt(std::istream & in)
{
std::string b1;
int e;
while (std::getline(in, b1))
{
size_t pos;
try
{
e = std::stoi(b1, &pos);
if (pos == b1.length())
{
return e;
}
}
catch (...)
{
}
}
// IO failure. throw exception
}
You can use std::stringstream for this purpose :-
for(int i=0; i<1; ++i)
{
string b1;
char c=' ';
int e=0, check=0;
do
{
getline (cin, b1);
stringstream ss(b1);
ss >> check;
if(ss>>c)
cout << "bad input";
else
e=check;
}
while(e==0 && b1!="0");
n=e;
}

parsing stringstream iterating through content not working

I have the following code to retrieve double values from a streamstring but for some reason I can't iterate through the stream, it just loads the first value in the stream and then exits the while, in both cases (i and f) see below:
std::string pois_fija_lista = argv[11];
std::string pois_flotante_lista= argv[12];
std::vector<double> vector_pois_fija(8), vector_pois_flotante(8);
std::stringstream ss_fija(pois_fija_lista), ss_flotante(pois_flotante_lista);
int i=0;
while (ss_fija >> i || ss_fija.eof())
{
vector_pois_fija.push_back(i);
if (ss_fija.peek() == ';')
ss_fija.ignore();
}
int f=0;
while (ss_flotante >> f || ss_flotante.eof())
{
vector_pois_flotante.push_back(f);
if (ss_flotante.peek() == ';')
ss_flotante.ignore();
}
Just for reference, the values in the stringstream taken from the argv are:
-461.3175;-417.031983203125;-523.8393;-417.031983203125;-491.6311;-518.393083203125;-490.6838;-512.709283203125
and
-102.287501220703;-54.613701647949;-164.809301220703;-54.613701647949;-132.601101220703;-155.974801647949;-131.653801220703;-150.291001647949
You need to extract into a double instead of an int otherwise the streams failbit gets set.
You also want to terminate, not continue, on eof. The eof check isn't really needed anyway as converting the stream to a bool will give false on eof anyway.
double i = 0.0;
while (ss_fija >> i)
{
vector_pois_fija.push_back(i);
if (ss_fija.peek() == ';')
ss_fija.ignore();
}

Reading spaces from file

I have the following code which reads text from a file and stores the characters in a vector. However this code is not reading spaces and pushing them in the vector. I tried to use myRf>>noskipws but its not working.
int a;
int b;
int outp;
if (myRF.is_open())
{
while (!myRF.eof())
{
myRF >> a;
myRF >> b;
// myRf>>noskipws
for (int i=0; i<a; i++)
{
vector <char> col;
for (int j=0; j<b; j++)
{
myRF>>outp;
col.push_back(outp);
}
grid.push_back(col);
}
}
}
myRF.close();
When you enable std::noskipws leading whitespace isn't skipped. However, you try to read an int which can't start with a space! You should read a variable of type char to read, well, chars. That should just work.
Note that it is much faster to read chars using std::istreambuf_iterator<char>:
std::istream::kerberos(myRF);
if (kerberos) {
std::istreambuf_iterator<char> it(myRF, true), end;
while (it = end /* && other condition */) {
char c = *it;
++it;
// do other stuff
}
}
BTW, do not myRF.eof() to control the loop! That doesn't work because the stream cannot predict what you will try to read! The eof() member is only useful to determine why a read failed and distinguish between legit reason (have reached te end of the file) and broken input. Instead, read and check the result, e.g.
while (myRF >> a >> b) {
// ...
}
The problem is that the >> operator uses white space to determine when to end the stream extraction. If you want to grab every character from a file and store the separately then you would use something like this:
std::vector<char> letters;
std::ifstream fin ("someFile.txt");
char ch;
while (fin.get(ch))
letters.push_back(ch);

Strings in C++: Problems with good() and get()

I am trying to read from a file. The code I used is
ifstream is;char c;
is.open("text.txt");
while(is.good() && !isdigit(is.peek()))
{ is.get(c)
word+=c;
}
The problem is that the last character is read twice (why?)
For e.g. if the word in the file is pink
the value of word becomes pinkk after the loop
Please suggest a solution
You always want to check that input was successful after you tried to read it. You are checking first when the stream has no idea what kind of value is going to be read. If you want to use peek() you should probably test against std::char_traits<char>::eof() first, e.g.:
for (std::char_traits<char>::int_type c;
std::char_traits<char>::eof() != (c = in.peek())
&& !std::isdigit(static_cast<unsigned char>(c); ) {
...
}
In your setup I would personally use std::istreambuf_iterator<char> as it is a lot easier, actually:
for (std::istreambuf_iterator<char> it(in), end;
it != end && !std::isdigit(static_cast<unsigned char>(*it); ++it) {
word += *it;
}
Note that char may be unsigned but std::isdigit() requires a positive value. If char is signed using my second name typically causes undefined behavior. To avoid this problem the char pass to std::isdigit() should be cast to unsigned char first.
Use the get() once more inside the loop to check whether its some character or not:
here is the code
while(is.good() && !isdigit(is.peek()))
{
is.get(c);
word+=c;
if(is.get(c))
{
is.seekg(-1,ios::cur) //move back get pointer if its not end of file
}
}
How about:
#include<cctype>
#include<fstream>
#include<iostream>
#include<string>
int main() {
std::ifstream fp("text.txt");
std::string word;
char c;
// while I am able to read a char...
while(fp>>c) {
//... if the char is a digit, stop reading...
if(std::isdigit(c))
break;
//... otherwise append it to my word string
word += c;
}
// close your files (or learn about scope-based destruction)
fp.close();
// print the resulting word
std::cout<<word<<std::endl;
return 0;
}
Compile: g++ example.cpp
Sample input (text.txt):
a
b
c
d
e
f
8
Sample output:
abcdef
The problem is that is.good() doesn't become false until AFTER you've had a failed read. So after you read the last character, is.good() is still true and you loop again to read another character (which fails), so you append the same character again.
To avoid this, you need to call is.good() AFTER reading (or peeking) at the next character -- if it is false then, there is no next character:
ifstream is;char c;
is.open("text.txt");
while(!isdigit(is.peek()) && is.good())
{ is.get(c)
word+=c;
}
or the simpler and equivalent:
ifstream is;char c;
is.open("text.txt");
while (is >> c && !isdigit(c))
word+=c;

Why doesn't this for-loop execute?

I'm writing a program for an exercise that will read data from a file and format it to be readable. So far, I have a bit of code that will separate a header from the data that goes under it. Here it is:
int main() {
ifstream in("records.txt");
ofstream out("formatted_records.txt");
vector<string> temp;
vector<string> headers;
for (int i = 0; getline(in,temp[i]); ++i) {
static int k = -1;
if (str_isalpha(temp[i])) {
headers[++k] = temp[i];
temp.erase(temp.begin() + i);
}
else {
temp[i] += "," + headers[k];
}
}
}
(str_isalpha() is just a function that applies isalpha() to every character in a string.) Now, the for-loop in this program doesn't execute, and I can't figure out why. Does anybody know?
EDIT: As suggested, I changed it to
string line;
for (int i = 0; getline(in,line); ++i) {
temp.push_back(line);
Still skips the for-loop altogether.
vector<string> temp; makes an empty vector. When you then try to read into temp[0], that is undefined behavior. You should pass as getline's second argument a separate string variable, say string foo; before the loop, then temp.push_back(foo); as the first instruction in the loop's body.
If the loop still doesn't run after ensuring that you're reading into a valid string reference, then you should check that the stream you're reading from is valid. The stream will be invalid if the file doesn't exist or if you lack permission to read it, for instance. When the stream isn't valid, getline won't read anything. Its return value is the same stream, and when converted to bool, it evaluates as false. Check the stream's status before proceeding.
ifstream in("records.txt");
if (!in.is_open()) {
std::cerr << "Uh-oh.\n";
return EXIT_FAILURE;
}