Reading text from a file

Hello everybody.

I'm just about to finish up my first semester of learning C++, and I'm trying to figure out some things that have been confusing to me.

So my textbook first gives an example of how to pass an fstream object to a function. The lesson simply states that when doing this, you need to pass by reference. I'm a little foggy on exactly how fstream objects work, but maybe I don't need to know that right now.

So the program passes the fstream object and a name to a function that tests to see if the file exists. If it does, then the fstream object (alone) is passed to another function from main called "showContents" which looks like this:

1
2
3
4
5
6
7
8
9
void showContents(fstream &file)
{
     char line[MAX_LINE_SIZE] //which is 81

     while (file >> line)
     {
          cout << line << endl;
     }
}

I believe that this function is the source of my confusion. The problem I'm having is that the next lesson in the textbook explains how to read from a text file while including the proper spacing.

The file that the second lesson uses looks like this:

Jane Murphy
47 Jones Circle
Almond, NC 8639

And the output looks like this: Jane Murphy47JonesCircleAlmond,NC8639

The textbook explains how to use the getline member function on a filestream object.

So what has be scratching my head is that the first lesson's code didn't have this problem. The program was structured differently, but I can't figure out what the key difference is.

Sorry if I'm making this more confusing than it needs to be. I'm trying to avoid throwing the entire codes of both programs at you. If somebody could explain how that showContents function is working, I might be able to figure the rest out on my own.

Thank you very much.
There's not much to say about that function. The first statement just declares an array to hold each line of the file. The while loop iterates through each line of the file. This works because the >> operator returns it's left operand. In this case the operand is the fstream object file. If the file is in a valid state (that is neither the fail, bad or eof bits are set) then it will go to the next line which prints out the line.
streams use a pointer to manipulate the stream so passing a copy will not be able to update the stream in real-time. Passing by reference passes the stream pointers address so what you do to the stream is actually happening and not just to a copy of the streams pointer.

I may not be 100% correct here but Im close enough. lol
The example is showing you that functions can be created using object pointers as arguments.
Last edited on
You should switch to getline cuz the >> input operator seem to stop reading upon encounter newspace. Since your file input line has space in between, it will break.

1
2
3
4
5
char line[MAX_LINE_SIZE]; //which is 81

while (file.getline(line,81)) {
    cout << line << "\n";
}

Thanks for the responses. I feel like I should explain a little better just how that function was confusing me.

I couldn't figure out how that while-loop was being exited. I understood that some amount of the text within 'file' was being put into 'line'. I just didn't understand how much. Then, I had an "ah-ha!" moment when I realized that 'line' was getting all of the text from 'file'. So, it only had to run that while-loop once. Makes sense, I guess.

But then why did it have to be in a loop at all then? Hmm.

So, as sohguanh mentioned, 'line' gets some amount of the text within 'file' until it reaches a space. If that's how it works then each time the loop runs again, the file object must be keeping track of where the last line of text was taken. It also still doesn't explain how all of the spacing is maintained.

EDIT: Seriously? I just noticed the endl after the cout. I over-looked it before because I thought that 'file >> line' bit worked differently. I can hear your palms hitting your faces. :)
Last edited on
If you use while (file >> line) {....} you get below. The default >> operator seem to stop reading when a newspace is encountered.

Jane
Murphy
47
Jones
Circle
Almond,
NC
8639

If you use while(file.getline(line,81)) {...} you get the same as the input file. The getline method stops reading upon encountering a newline. Can be other delimiter but default is newline.

So both ways involve a while loop until there is no more input to be read from the input file.
Just to explain a little about this in case it is confusing you:
1
2
3
4
     while(file >> line)
     {
          cout << line << endl;
     }

The operator file >> line has a return value. It returns the file object itself. So I can do:
 
(file >> line).close();

That reads line from the file, but then that operation returns the file object (again) which I can then close() (or call any other std::fstream function).

This allows us to write things like this:
 
file >> value1 >> value2 >> value3;


Each >> operator returns the file object after it read its value so that the next >> operator can then be invoked on the same file object.

Now, on top of that, the while() construct expects a bool value (true or false). But file is a std::fstream object.

However a std::fstream object can be 'converted' into a bool and when the program asks for a bool from a std::fstream that is exactly what it does.

The std::fstream (essentially) returns true if the std::fstream has no errors and false if the std::fstream failed to read a value or reached the end of the file.

So while(file >> line) will keep reading a line from file until it encounters an error or it reaches the end of the file.

This can be a bit confusing but it just 'works' and you don't need to know the details.
Mmm, but that output example you gave would only come out that way if there was an 'endl' after the 'cout', correct? Otherwise, it would still output the text in a single line, it would just be reading each line rather than reading each segment of text divided by a space (an inconsequential difference).

So using the getline method, you could do something like this, yes?:
1
2
while (file.getline(line,81))
cout << line << endl; //the string 


The 'endl' is still necessasry because getline uses a newline as a delimiter.

If this is correct, it would be a much simpler way of doing this than what my textbook gives:

1
2
3
4
5
6
file.getline(line, SIZE);
while (!file.eof())
{
     cout << line << endl;
     file.getline(line, SIZE))
}
Galik, I feel like that's exactly what I said in my first post :/
Galik, I didn't see your post before making my last post. Thank you for explaining that in such detail.

ascii, I see what you mean but when I first read it, I didn't really get it.

I can finally move on in my reading. You guys are awesome.
This has happened to me many times: I want to copy an example code from the textbook so that I can make little changes to see what happens. I figure it's a good way to learn. What often happens though is I'll copy out the code and run it (just to make sure that it works properly in it's original state) only to find that it does it. I'll check over and over back-and-fourth between my book and my code, looking for differences. I'll over-look something really simple many times before discovering it.

That might be happening again, but I re-wrote the entire code and I'm having the same problem.

The file that I'm trying to access looks like this:

Jayne Murphy
47 Jones Cricle
Almond, NC 28702

But my code only outputs the first two lines. I tried changing the file so that the "Almond...." line was also on the second line and what ended up happening when I ran the program was that it only output the first line. When I added a fourth line to the file, the program output the first three lines but not the fourth.

:/

EDIT: I decided to just move along and the next example code that I came to and attempted has me even more convinced that my compiler is just working differently than how the textbook says it should.

The example is to show that you can enter a third argument into the getline function to change the delimiter to something other than \n, like this: dataFile.getline(input, SIZE, '$');

The file that it's reading looks like this:

Jayne Murphy$47 Jones, Circle$Almond, NC 28702\n$Bobbie Smith$
217 Halifax Drive$Canton, NC 28716\n$Bill Hammet$PO Box 121$
Springfield, NC 28357\n$

And the output is supposed to look like this:

Jayne Murphy
47 Jones, Circle
Almond, NC 28702

Bobbie Smith
217 Halifax Drive
Canton, NC 28716

Bill Hammet
PO Box 121
Springfield, NC 28357

The book says that the \n in the file should be read by the compiler and make the extra blank line to be printed. When I copy out the code, that's not what happens. There are new lines after the $ appears in the file and wherever an actual new line exists in the file. The \n is printed on screen like: NC 28702\n

My guess is that this also has something to do with why the first code (before the EDIT) doesn't work.
Last edited on
Show us your latest code.
Here is the code from the second example where the /n character is printed for some reason...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <fstream>
using namespace std;

int main()
{
	const int SIZE = 81;
	char input[SIZE];

	fstream dataFile("names2.txt", ios::in);

	dataFile.getline(input, SIZE, '$');
		while (!dataFile.eof())
		{
			cout << input << endl;
			dataFile.getline(input, SIZE, '$');
		}

		dataFile.close();
}


My previous post shows what the file looks like. This is what the output looks like (notice that it prints a new line where the file has a new line, instead of where the file has a \n):

Jayne Murphy
47 Jones, Circle
Almond, NC 28702\n
Bobbie Smith

217 Halifax Drive
Canton, NC 28716\n
PO Box 121

Springfield, NC 28357\n
For the miss reading offset by one line, just put the getline on top.

1
2
3
while (dataFile.getline(input, SIZE, '$')) {
  cout << input << endl;
}

Last edited on
You mean to change:
1
2
3
4
5
while (!dataFile.eof())
		{
			cout << input << endl;
			dataFile.getline(input, SIZE, '$');
		}


to:

1
2
3
4
5
while (!dataFile.eof())
		{
			dataFile.getline(input, SIZE, '$');
                        cout << input << endl;
		}


Right?

That gives the exact same output. I'm guessing though, that I'm misunderstanding your advice. You say to "put the getline on top", but your example uses a different condition in the while-loop than mine does. I tried doing it by changing the while-loop condition to dataFile.getline(input, SIZE, '$')), but it still doesn't work.

I just don't get why the compiler isn't converting the \n in the file into a newline. And also why it's reading the newlines in the file as newlines in the output. According to my textbook's example, this shouldn't happen.

Do compilers vary in this way? I'm using Windows Visual Studio 2010 Premium.
Last edited on
dataFile.getline(input, SIZE, '$')
this will read everything until it finds a $ character and put everything in input, even the newline chars is put into input.
Just a note.

This:
1
2
3
4
5
6
7

	dataFile.getline(input, SIZE, '$');
	while(!dataFile.eof())
	{
		cout << input << endl;
		dataFile.getline(input, SIZE, '$');
	}

Is more compactly coded:
1
2
3
4
	while(dataFile.getline(input, SIZE, '$'))
	{
		cout << input << endl;
	}

Also, the "\n" character is the newline character. So when your book says "\n" when describing the contents of a file it probably intends you to replace it with an actual newline rather than copying the \ and the n characters literally.

Furthermore it is much more usual to do this kind of reading using std::string. That would then use the std::getline() function:
1
2
3
4
5
	std::string input;
	while(std::getline(dataFile, input, '$'))
	{
		cout << input << endl;
	}

Topic archived. No new replies allowed.