Thread: File I/O Issue

  1. #1
    Registered User
    Join Date
    Jul 2009
    Posts
    36

    File I/O Issue

    While playing around a bit with File I/O I created a small test program. It displays a menu, from which the use can select two functions. One to write a string to a specified file and one to read a file. Now it all works if I call the functions within main. However if I call the functions from the menu function the do not function like they should, like skipping any user input. What exactly is going wrong? There are not compile errors btw. I suspect some variable scope issue but I can not find the error.


    Code:
    // File IO Test1.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    
    
    void printfile();
    void readfile();
    void menu();
    
    
    
    int main()	{
    
    	menu();
    
    
    
    
    	return 0;
    }
    
    
    
    
    
    
    void menu()	{
    	int x;
    
    	cout<<"<1> Print file function"<<endl;
    	cout<<"<2> Read file function"<<endl;
    	cout<<"<0> Exit program"<<endl;
    	cout<<"Select funtion and press <ENTER>): ";
    	cin>>x;
    
    	if (x < 0 || x > 2)	{
    		cout<<"<ERROR> Invalid menu selection. Exiting program..."<<endl;
    	}
    
    	else	{
    	switch (x)	
    	{
    		case 1:
    			printfile();
    			break;
    
    		case 2:
    			readfile();
    			break;
    
    		case 0:
    		cout<<"<0> Exiting...."<<endl;
    			break;
    	}
    	}
    }
    
    
    
    
    
    
    
    void readfile()	{
    	char filename[81];
    	char content[81];
    
    	cout<<"Enter a filename and press ENTER: ";
    	cin.getline(filename, 80);
    
    	ifstream file_in(filename);
    	file_in.getline(content, 80);
    	cout<<content<<endl;
    }
    
    
    
    
    
    
    void printfile ()	{
    	char filename[81];
    	char content[81];
    
    	cout<<"Enter the filename: ";
    	cin.getline(filename, 80);
    	ofstream file_out (filename);
    
    	cout<<"Enter text to write to file: ";
    	cin.getline(content, 80);
    	file_out<<content<<endl;
    	file_out.close();
    }
    So again in short: If I call the printfile or readfile function directly in main it works, but going from main > menu() > readfile() it starts acting weird.

  2. #2
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    The problem is the line:
    Code:
    cin>>x;
    leaves a newline character in the buffer. This newline character is then read in when you call cin.getline() as the user input. Change your code to:
    Code:
    cin>>x;
    cin.ignore(); // Remove newline from the input buffer
    bit∙hub [bit-huhb] n. A source and destination for information.

  3. #3
    Registered User
    Join Date
    Jul 2009
    Posts
    36
    Ok this appears to fix it. But why does cin>>x; mess up other functions of the program. I expected things in one function be "locked" inside it.


    cin>>x;
    As far as I understand this means store whatever the user types in into the variable x (everything up to pressing enter)


    cin.ignore(); // Remove newline from the input buffer
    What does this mean in simpler terms? I am not getting the meaning of it.
    Last edited by Jefff; 08-05-2009 at 11:10 PM.

  4. #4
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Jefff View Post
    Ok this appears to fix it. But why does cin>>x; mess up other functions of the program. I expected things in one function be "locked" inside it.
    So, if some function launches some nuclear missiles, and you return from this function, you expect the missiles to disappear?
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  5. #5
    Registered User
    Join Date
    Jul 2009
    Posts
    36
    Well I do not get why an input that was collected in menu() should mess up other functions. I mean it looks harmless, get user input, store it into a local x variable and be done with it. Why does this have any influence on input operations in other functions?

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Jefff View Post
    Well I do not get why an input that was collected in menu() should mess up other functions. I mean it looks harmless, get user input, store it into a local x variable and be done with it. Why does this have any influence on input operations in other functions?
    You've only got one keyboard, right? cin is shared among all the functions, and the fact that you typed extra stuff in one function means that that extra input is still waiting around the next time you look for keyboard input (whichever function that happens to be). If you like metaphors, your menu() function is a tenant that trashed the place when they got evicted, so when the next function "moves in" they have to deal with the mess left behind (a "nice" function would have cleaned up behind themselves, by leaving the input buffer clean).

  7. #7
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Jefff View Post
    Well I do not get why an input that was collected in menu() should mess up other functions. I mean it looks harmless, get user input, store it into a local x variable and be done with it. Why does this have any influence on input operations in other functions?
    Input is a stream of characters. By performing input, you are removing characters from this stream, which obviously will have some sort of effect on other code which is also trying to read characters from this stream.

    One solution is to "flush" the input buffer by using cin.ignore(). This discards characters until the next line. A far better approach, in my opinion, is to not mix line-based and entity-based input in the same program in the first place, as this problem only arises in that scenario.

    In other words, mixing this form of input:

    Code:
    cin >> foo
    With this form of input:

    Code:
    cin.getline();
    Will automatically open you up to this type of problem. Either use the '>>' everywhere, or use getline() everywhere, but do not mix them
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  8. #8
    Registered User
    Join Date
    Jul 2009
    Posts
    36
    Ok, so whenever cin>>somethings gets an input it floats around in the "buffer" until it is cleaned out. Which in this case messed up my program because the other functions skipped the input because there was something there already, correct?

    Considering this, would using

    Code:
    cin.ignore();
    after each cin>> operation be a good idea?


    EDIT: The above was written without seeing brewbucks last post.

    So in order to not mix up these types,
    Would
    Code:
    cin >> foo
    and
    Code:
    cin.getline(foo, 10)
    produce the same thing (I.e. store the complete user input in foo)?
    Last edited by Jefff; 08-05-2009 at 11:32 PM.

  9. #9
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Jefff View Post
    Considering this, would using

    Code:
    cin.ignore();
    after each cin>> operation be a good idea?
    No. cin.ignore() will discard ALL characters up until the next line break. That might include actual data instead of useless whitespace. So you would be incorrectly discarding real input. Suppose you are trying to read three integers, and the user enters them all on one line, as "1 2 3". If you are calling cin.ignore() after every extraction operation, then the "2" and the "3" will vanish into hyperspace

    EDIT: Is this problem really damn annoying? You bet. As I said, the fundamental cause of the problem is trying to mix line-based and extraction-based input. Stick to one or the other and this problem does not occur.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Just a clarification on brewbuck's statement: if you literally use cin.ignore(), you would only be discarding one character. If you supply it with certain arguments, e.g.,
    Code:
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
    it would then discard characters until the next line break (or when EOF is reached, whichever comes first). std::numeric_limits is available via the <limits> header; std::streamsize is available via the <ios> header.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    Registered User
    Join Date
    Jul 2009
    Posts
    36
    However avoiding
    Code:
    cin>>x;
    is not really an option as far as I know, because I need an an int for my switch case. So if I type
    Code:
    cin.ignore();
    it literally means remove 1 character/letter/number from the input stream, which in case the user only typed one number, would be sufficient?

  12. #12
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    >> it literally means remove 1 character/letter/number from the input stream, which in case the user only typed one number, would be sufficient?
    You are still misunderstanding. cin>>x will remove the numbers from the input stream. The problem is that in order for the user to add those numbers to the stream in the first place, they had to press the "Enter" key. This placed a '\n' character in the input stream after the numbers. The cin.ignore() statement is to ignore the '\n' character. Otherwise when you call cin.getline() it will read in the '\n' character as the input (which is just an empty line). That is why it originally appeared that this input was "skipped".
    bit∙hub [bit-huhb] n. A source and destination for information.

  13. #13
    Registered User
    Join Date
    Jul 2009
    Posts
    36
    I think I get it now. Following your explanation it goes like this:

    Code:
    cin>>x
    takes whatever the user types in and stores it into x.
    However since the user typed <number> and ENTER, this is put into the stream:

    <number>\n

    of which cin>>x "shaves off" the <number>. However \n remains in teh stream, which then gets picked up by cin.getline and causes the skip in the functions.
    cin.ignore removes that \n which also removes the skipping.
    I hope I got it correctly this time. I also assume that cin.getline takes a complete line and interprets it as a string? Because I tried cin.getline with an int variable and it produced errors when compiling.

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Jefff
    However \n remains in teh stream, which then gets picked up by cin.getline and causes the skip in the functions.
    cin.ignore removes that \n which also removes the skipping.
    Yes, that is the idea.

    Quote Originally Posted by Jefff
    I also assume that cin.getline takes a complete line and interprets it as a string? Because I tried cin.getline with an int variable and it produced errors when compiling.
    Yes, it stores what it reads into a character array as a null terminated string. If you want to store into a std::string object instead, you should use the non-member version, e.g., std::getline(std::cin, str);
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Newbie homework help
    By fossage in forum C Programming
    Replies: 3
    Last Post: 04-30-2009, 04:27 PM
  2. Need Help Fixing My C Program. Deals with File I/O
    By Matus in forum C Programming
    Replies: 7
    Last Post: 04-29-2008, 07:51 PM
  3. Game Pointer Trouble?
    By Drahcir in forum C Programming
    Replies: 8
    Last Post: 02-04-2006, 02:53 AM
  4. Unknown Memory Leak in Init() Function
    By CodeHacker in forum Windows Programming
    Replies: 3
    Last Post: 07-09-2004, 09:54 AM