Thread: C++ I/O problem

    #1
  1. No Profile Picture
    Contributing User
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Feb 2001
    Posts
    1,481
    Rep Power
    15

    C++ file I/O problem


    Hi,

    I'm having a slight problem with file I/O. I'm writing a sentence to a file, and then reading it back character by character and displaying the characters immediately after reading them. Therefore, the sentence in the file and the sentence displayed to the screen should be the same. However, the sentence displayed to the screen has two periods. Why?

    Code:
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    int main()
    {
    	
    	const char* filename = "C:\\TestData\\input.txt";
    	//associate an fstream object with my data file:
    	fstream fileStream(filename);
    	
    	//write the sentence to the data file:
    	fileStream<<"The man is running.";
    	
    	//reset the internal pointer to the beginning of the data file:
    	fileStream.seekg(0);
    	
    	char temp;
    	while(!fileStream.eof())
    	{	
    		fileStream.get(temp); //read one character from the data file
    		cout<<temp; //display the character
    	}
    	cout<<endl;
    	return 0;
    }
    data file output>>The man is running.
    displayed to screen>>The man is running..
    Last edited by 7stud; March 13th, 2003 at 01:08 AM.
  2. #2
  3. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,255
    Rep Power
    2222
    I've been doing embedded for several years and precious little file I/O, but I seem to recall that using eof() can be tricky.

    eof() reflects the EOF state of the file stream. That EOF state does not get set until the end of the file has been detected by having a read fail. First an attempt to read must fail, then eof() will do what we expect.

    In your code segment --
    Code:
    char temp;
    while(!fileStream.eof())
    {	
          fileStream.get(temp); //read one character from the data file
          cout<<temp; //display the character
    }
    -- , when your get(temp) fails at the end of file, you go ahead and output it anyway. Since the value of temp did not get changed, it still contains a '.'.

    eof() needs to be tested before you output what you've read. I would rewrite that segment as:
    Code:
    char temp;
    fileStream.get(temp); //read first character from the data file
    while(!fileStream.eof())
    {	
            cout<<temp; //display the character
            fileStream.get(temp); //read one character from the data file
    }
    I haven't tested it, but I think that should work.
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Feb 2001
    Posts
    1,481
    Rep Power
    15
    "eof() reflects the EOF state of the file stream. That EOF state does not get set until the end of the file has been detected by having a read fail."

    Thanks for the response. Your code works, however, I think your explanation may be erroneous because my book says: "After the last value has been read from the file, the eof() member will return true, because the current file position will be at the end of the file." It's my understanding that after you read a character, the internal file pointer moves to the next position in the file. So, after I read in the last character, the period in my case, the internal file pointer should be pointing at the end of the file, and therfore eof() will return true and !fileStream.eof() will return false causing the while loop to terminate.

    I suspect my problem has to do with there being another character in the stream after the period, and the code you suggested just throws out that last character. My book says that in text mode there isn't necessarily a one for one representation when writing to a file i.e. some characters will be represented by more than one byte in the output file. Could that be what's happening?
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Feb 2001
    Posts
    1,481
    Rep Power
    15
    Here's a short program that demonstrates the way I am using eof() isn't the problem:

    Code:
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    int main()
    {
    	
    	const char* filename = "C:\\TestData\\input.txt";
    	//associate an fstream object with my data file:
    	fstream fileStream(filename);
    	
    	//write two numbers to the data file:
    	fileStream<<100<<" "<<300;
    	
    	//reset the internal pointer to the beginning of the data file:
    	fileStream.seekg(0);
    	
    	int number;
    	while(!fileStream.eof())
    	{
    		
    		fileStream>>number; //read one number from the data file
    		 
    		cout<<number<<endl;//display the number
    	}
    	return 0;
    }
    output to file>> 100 300
    output to screen>>
    100
    300
    Last edited by 7stud; March 13th, 2003 at 04:28 PM.
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2002
    Posts
    272
    Rep Power
    19
    Unfortunately, your example is an apples and oranges kind of thing. Reading into a number is different than getting a char at a time. Your original code does attempt to read from the file after it gets the period. This last read fails, leaving temp alone so there is still a period in it. I do not know why, but this is what is happening.
  10. #6
  11. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Sep 2001
    Location
    ISRAEL
    Posts
    35
    Rep Power
    14
    when I use eof I do as dwise1_aol described and i think that is the right explnation.
    I the program you demonstrated you used number which works a but differently. I'm not sure exactly how but it reads characters until the next white space (blank, tab, new-line, eof etc.)
    so it isn't the case with reading char.
    "Gravitation can NOT be responsible for people falling in Love"
    (one of the most significant characters in the history, can you guess?)

    Gmorph.
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Feb 2001
    Posts
    1,481
    Rep Power
    15
    Thanks for taking the time to respond.

    Could it be there's a \0 at the end of the string literal I write to the file that is causing that? I thought that was only for arrays.
  14. #8
  15. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2002
    Posts
    272
    Rep Power
    19
    The null terminator is not written to the stream. The period is the last byte in the file. I think that the explanation given earlier is correct. eof is not set until a read past the last character is attempted. This read will fail, but in your code you don't check for that before outputting temp to the screen.
  16. #9
  17. No Profile Picture
    Contributing User
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Feb 2001
    Posts
    1,481
    Rep Power
    15
    "I think that the explanation given earlier is correct."

    Yes, but I was troubled by not being able to reconcile the differences in eof when dealing with numbers and char types. However, I think I came up with a sample program that demonstrates that what dwise1_aol said about eof() is true for both numbers and char types.

    Code:
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    int main()
    {
    	
    	char* filename = "C:\\TestData\\input.txt";
    	//associate an fstream object with my data file:
    	fstream fileStream(filename);
    	
    	//write the numbers to file ENDING W/ A SPACE:
    	fileStream<<100<<" "<<300<<" ";
    	
    	//reset the internal pointer to the beginning of the data file:
    	fileStream.seekg(0);
    	
    	int number;
    	while(!fileStream.eof())
    	{
    		
    		fileStream>>number; //read one number from the data file
    		 
    		cout<<number<<endl; //display the number
    	}
    	
    	return 0;
    }
    output to data file>>100 300
    output to screen>>
    100
    300
    300

    What was confusing to me was proposition that numbers and char types are different when it comes to eof(), and I didn't believe that could be the case. The rules for when a read from the stream is accomplished for numbers and char types are different, but I didn't believe eof could be interpreted differently, and I believe my sample programs show that eof is treated the same. My conclusion is: if there isn't a delimiter, like a space, following the last number in a file, the read that gets that number from the stream also reads eof. If a space is tacked on the end, the read stops at the space delimiter, and then it takes one more read to get eof, just like with the char type.

    Thanks everybody.
    Last edited by 7stud; March 14th, 2003 at 03:43 AM.
  18. #10
  19. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Sep 2001
    Location
    ISRAEL
    Posts
    35
    Rep Power
    14
    i have to disagree.
    in the last case you showed you actually added more chars.
    but in the first case with the sentence you didn't add anything.

    i think that doesn't exaplin the problem....
    "Gravitation can NOT be responsible for people falling in Love"
    (one of the most significant characters in the history, can you guess?)

    Gmorph.
  20. #11
  21. No Profile Picture
    Contributing User
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Feb 2001
    Posts
    1,481
    Rep Power
    15
    I'm not sure I understand. With a char type, a read stops after every character, and after reading the last character in the sentence, one more read is required to read eof.

    With the 2nd sample program, there are two numbers separated by a delimiter, the space, and there was no duplication in the output. So, I believed the question presented was: why wasn't one more read required to read in eof in that case?

    I believe the third sample program--the one that has a space tacked on at the end of the last number in the file--shows that when reading numbers, the read ends only when you hit a number delimiter. So, the program hit the space delimiter after reading the last number and that caused eof not to be read in, so it took one more read to read eof, and therefore the same duplication seen in the first program occurred. Note that the first program also hit a delimiter--the end of 1 byte-- before it could read in eof, and that is what caused the duplication there. In the second sample program, the last read did not encounter a delimiter, so it read in eof too on the last read which caused the loop to fail and no duplication.

    Therefore, I believe eof is treated consistently: when reading the last piece of data in your file if you hit a delimiter before reading eof, one more read is required to read in eof.
    Last edited by 7stud; March 14th, 2003 at 04:28 AM.
  22. #12
  23. No Profile Picture
    Contributing User
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Feb 2001
    Posts
    1,481
    Rep Power
    15
    Well, if anyone cares, I figured out how to make the first program with the sentence work using a method that was a little cleaner than dwise1_aol's method: I used peek() in the loop control. peek() reads the character in the stream the internal file pointer is pointing to, but the character is left in the stream i.e the file pointer doesn't move. That allowed me to take a look at what's located at the current position before reading it, and skip the loop if I was going to read in EOF(which I guess is a defined character of some sort).

    Code:
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    int main()
    {
    	
    	char* filename = "C:\\TestData\\input.txt";
    	//associate an fstream object with my data file:
    	fstream fileStream(filename);
    	
    	//write the sentence to the data file:
    	fileStream<<"The man is running.";
    	
    	//reset the internal pointer to the beginning of the data file:
    	fileStream.seekg(0);
    	
    	char text;
    	while(fileStream.peek() != EOF)
    	{
    		
    		fileStream.get(text); //read one character from the data file
    		 
    		cout<<text; //display the character
    	}
    	cout<<endl;
    	
    	return 0;
    }
    Last edited by 7stud; March 14th, 2003 at 04:52 AM.
  24. #13
  25. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Sep 2001
    Location
    ISRAEL
    Posts
    35
    Rep Power
    14
    first of all thank for the knowledge of Peek.
    But it isn't right to do it using Peek(). the reason is Efficiency!

    when using Peek you actually reading from the file twice for each char - reading from a file is relatively slow.

    hence, you should use the solution described earlier (reading the first char befor the loop).

    using the quick watch of VC++ (Shift+F9) you can that immediatly after trying to read the char next to the point you gat the eof set.

    it isn't "ugly" to do read the first char before the loop - it is the way it is done.

    all in all, i think you should use the API function (CreateFile, ReadFile, WriteFile etc.)
    "Gravitation can NOT be responsible for people falling in Love"
    (one of the most significant characters in the history, can you guess?)

    Gmorph.
  26. #14
  27. No Profile Picture
    Contributing User
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Feb 2001
    Posts
    1,481
    Rep Power
    15
    "when using Peek you actually reading from the file twice for each char - reading from a file is relatively slow.

    hence, you should use the solution described earlier (reading the first char befor the loop)."

    Ok. Thanks for the tip!
  28. #15
  29. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2003
    Posts
    6
    Rep Power
    0
    I wonder what compiler you are using? I seem to recall a similar issue due to a bug in borlands compiler (5.0 I think). The next version addressed the issue.

IMN logo majestic logo threadwatch logo seochat tools logo