Can't get fstream to read file input

Hello, I'm going through a simple C++ book trying to get fstream to display in command line from records.txt now I have made the file and put it in the correct directory but it just comes up blank and I get nothing :( Any help would be lovely! Ta!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <fstream>
#include <string>
#include <iostream>
using namespace std;

int main()
{
const int RANGE = 12 ;
string tab[ RANGE ] ;
int i = 0 , j = 0 ;
return 0;

ifstream reader( "records.txt" ) ;


if ( ! reader )
{
	cout << " Error opening input file" << endl ;
	return -1 ;
}

while ( ! reader.eof() )
{
	if ( ( i + 1 ) % 4 == 0 )
		getline( reader ,tab[ i++ ], '\n' ) ;
	else
		getline( reader, tab[ i++ ], 't' ) ;
}

reader.close();
i = 0 ;

while ( i < RANGE )
	cout << endl << "Record Number: " << ++j << endl ;
	cout << "Forename: " << tab[ i++ ] << endl ;
	cout << "Surname: " << tab[ i++ ] << endl ;
	cout << "Department: " << tab[ i++ ] << endl ;
	cout << "Telephone: " << tab[ i++ ] << endl ;

}
What does records.txt look like?
Did you mean for line 27 to be a tab (\t) character?
Nice spot fixed that but till nothing, looks like this:

John Smith Sales 555-1234
Mary Jones Wages 555-9876
Paul Haris Accts 555-4321
line 11: return 0; will end the program before the file access is begun.

There are missing braces around the last while loop. Your current code is like this:
1
2
3
4
5
6
7
    while ( i < RANGE )
        cout << endl << "Record Number: " << ++j << endl ;
    
    cout << "Forename: " << tab[ i++ ] << endl ;
    cout << "Surname: " << tab[ i++ ] << endl ;
    cout << "Department: " << tab[ i++ ] << endl ;
    cout << "Telephone: " << tab[ i++ ] << endl ;
Ah now that's better, this book isn't the best. That and my reading, I'm getting the file can't be accessed now which is great, means it's working. Where should I put the records.txt for it to work? (Show how bad the book is as it doesn't specify) Thank btw :)
records.txt should be in the same directory as your executable file.
Please don't return negative numbers from main().

Return 0 or EXIT_SUCCESS

else return 1 or EXIT_FAILURE

(If you must return other numbers, make them in [0, 255].)


I am also concerned about your looping on EOF. In this instance it is correct, but do you know why?

Your loops should always perform the following actions when attempting to read from file:

(1) attempt to read one or more data from file
(2) check for stream ok
(3) if not ok, exit loop
(4) otherwise perform loop operations (such as storing the stuff read into structs)
(5) goto 1

The standard method is overloaded to make it easy to do this:

while (f >> foo) ...
or
while (getline( f, foo )) ...

because what is really happening is that after the (1), the while does (2), just like your test on line 16:

if ( ! reader ) ...

which is the same as saying if (! reader.good() ) ...


Just to be sure you are aware...
Hope this helps.
Ah just saw your comment, I removed it I see why not, sorry if you mentioned why I'm very new to C++ what should I modify to fix this loop on EOF and why would it work? Don' t just want a fix, want to know why :) I wish I bought a different book lol
Last edited on
What you have is fine. I'm was simply curious if you knew why it was okay, and why it looked bad.

Every input loop should essentially do this:

1
2
3
4
5
6
7
while (true)
{
  f >> foo;       // attempt to read something from stream 'f'
  if (!f) break;  // if anything went wrong, quit now
  // if it gets this far, then foo has a valid value, and you can use it
  quux( foo );
}

In C++, the extraction operators return the stream, so the result of an expression like cin >> foo is... cin! That's right, you get back the stream you are using. Hence, you can test it immediately after the attempt to input something operation, all in one statement:

1
2
3
4
5
while (f >> foo)  // attempt to read; if anything goes wrong, quit
{
  // if it gets into the loop body here, then foo has a valid value...
  quux( foo );
}

Now, the phrase "if anything went wrong" doesn't just mean EOF. After your loop, you can check to see whether or not that "anything that went wrong" was EOF:

1
2
3
4
5
6
7
8
9
10
11
12
13
while (f >> foo)
{
  quux( foo );
}

// I am hoping f is at EOF right now, but if it quit 
// the loop for some other reason, I want to know.
if (!f.eof())  
{
  cerr << "Hey! There was something wrong with the data in the file!\n"
          "(Because I totally didn't get to EOF!)\n";
  return 1;  // ...and so I'm gonna quit. Nyaah!
}


After all that, let's think through the code you have:

1
2
3
4
5
6
7
while ( ! reader.eof() )
{
	if ( ( i + 1 ) % 4 == 0 )
		getline( reader ,tab[ i++ ], '\n' ) ;
	else
		getline( reader, tab[ i++ ], '\t' ) ;
}

The first time through, we test if at EOF. We're not, so that's fine. Then we try to get input into its final place. That will either succeed or fail. The very next thing we do is... loop! So the test for EOF (or other failure) comes immediately after the attempt to read. That's good!

The danger is that we have allowed other side effects to take place. In this case, the variable i has been incremented whether or not the attempt to get input succeeded.

Your program doesn't care. It ignores i's value after the input loop. The only real danger you face is that the input file may contain too many values (and overwrite the tab[] array's bounds).

It would also not produce a very nice result if the file contained too few items, of course, but in this program it will not matter.


This is the hard part about dealing with I/O that gets everyone by surprise. It is nice when things are what we'd like them to be. But our programs must be designed to handle the (all too often) reality that they are not, and they must handle it in a way that prevents things from breaking.

Had I written the example, without introducing stringstream or vector, and by storing records in a linear 'tabs' array, I would have done it this way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main()
{
  const int MAX_TABS = 12;
  string tab[ MAX_TABS ];
  int num_tabs = 0;
  char fs[] = "\t\t\t\n";

  ifstream reader( "records.txt" );

  if (!reader)
  {
    cout << "I could not open records.txt" << endl;
    return 1;
  }
  
  while (getline( reader, tab[ num_tabs ], fs[ num_tabs % 4 ] ))
  {
    num_tabs++;
    if (num_tabs >= MAX_TABS) break;
  }

  reader.close();

  for (int r = 0, i = 0; i < num_tabs; )
  {
    cout << endl << "Record Number: " << ++r << endl;
    cout << "Forename:   " << tab[ i++ ] << endl;
    cout << "Surname:    " << tab[ i++ ] << endl;
    cout << "Department: " << tab[ i++ ] << endl;
    cout << "Telephone:  " << tab[ i++ ] << endl;
  }
}

In this code, the most difficult thing to follow is how we index the fs[] array for the proper delimiter.

Had the records been read properly into a 2D array using a nested loop, we could have avoided all that unpleasantness...

In any case, I hope this helps.
Last edited on
I understand, loops are tricky for sure! Then again so is C++ haha. I understand your code more than my books, think I'll skip this page and buy myself the C++ 6th Edition book :) Would you recommend that? This isn't the first error in this book, there's also been typos -.-
If you find a book deficient, don't use it.

A good list of books can be found over at stackoverflow:
http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list

Hope this helps.
Agreed, C++ Primer 5th edition is known for being good. And is reccomended on the link so I think I'll get it instead. I've read my current book ( C++ in easy step ) is very weak on telling you on what things actually do. Which is true.
Topic archived. No new replies allowed.