Tech Off Thread

20 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

C++ binary IO

Back to Forum: Tech Off
  • User profile image
    bigthegeek

    Hi everyone!

    I am currently learning C++, and just learned how to output to a file in binary mode. I wrote a program that prompts the user for a filename then it asks him wether he wants to read or write to the file. The problem is that, even though the code compiles, it never manages to create the file, and I don't know why.

    Here's the code for you to check out:

    #include <iostream>
    #include <fstream>
    #include <string.h>
    using namespace std;
    
    int get_int(int default_value);
    char name[20];
    int age;
    
    int main() 
    {
        char filename[81];
        int n;
        int menu;   // Integer used to select a menu command.
        int recsize =  sizeof(name) + sizeof(int);
        
        cout << "Enter file name: ";
        cin.getline(filename, 80);
    
        // Open file for binary read-write access.
    
        fstream fbin(filename, ios::in | ios::out | ios::binary);
        if (!fbin) 
        {
            cout << "Could not open file " << filename << endl;
            return -1;
        }
    
        while(1) 
        {
            // Prompt for a command and then get a menu number
            cout << "Enter a choice: 1. Write Record, 2. Read Record, 3. Exit. ";
            menu = get_int(3);
    
            if (menu == 1) 
            {                       // Menu choice #1. Write
                cout << "Enter a record number: ";    // Get record #
                  n = get_int(0);
                fbin.seekp(n * recsize);
    
                cout << "Enter name: ";               // Get data to write
                cin.getline(name, 19);
                cout << "Enter age: ";
                age = get_int(0);
    
                // Write data to the file.
    
                fbin.write(name, 20);                 // Write to file
                fbin.write(reinterpret_cast<char*>(&age), sizeof(int));
    
            } 
            
            else if (menu == 2) 
            {               // Menu choice #2. Read
                cout << "Enter a record number: ";      // Get record #
                n = get_int(0);
                fbin.seekp(n * recsize);
    
                // Read data from the file.
    
                fbin.read(name, 20);                    // Read data from file
                fbin.read(reinterpret_cast<char*>(&age), sizeof(int));
    
                // Display the data and close.
        
                cout << "The name is: " << name << endl;  // Display the data
                cout << "The age is: " << age << endl;
    
             } 
        
            else                               // Other menu choices: Exit.
                break;
        }
        fbin.close();
        return 0;
    }
    
    // Get integer function
    // Get an integer from keyboard; return default
    //  value if user enters 0-length string.
    
    int get_int(int default_value) 
    {
        char s[81];
    
        cin.getline(s, 80);
        if (strlen(s) == 0)
             return default_value;
        return atoi(s);
    }
    

     

  • User profile image
    spivonious

    @bigthegeek: I believe you need to flush the file buffer. It's been a long time since I've written any C++, but it should be something like fbin.flush().

  • User profile image
    bigthegeek

    Hi,

    First of all, thanks for answering.

    Now, It's the first time I've heard of that, where do I put it? I tried putting that right before the if statement that checks if the file was sucessfully opened, but it still didn't work Perplexed

     

  • User profile image
    Tokter

    Well there is nothing in the buffer right after you open the stream. So flushing it does not do anything. Put it right before you close the stream.

  • User profile image
    bigthegeek

    Still nothing.. Don't know why the file isn't created, so the programs exits early, in here:

    if (!fbin)     {        cout << "Could not open file " << filename << endl;        return -1;    }

  • User profile image
    Tokter

    It seems if you use ios::in then the file has to exist or the stream does not get created.

  • User profile image
    bigthegeek

    Tokter, I did what you said, and indeed the file is created, I can run the program and write and read from it but if I exit the program, re-run it and open the previously created file, it seems that all the data is lost, the file is empty. What can I do to solve this?

    Thanks for the help so far Wink

  • User profile image
    Sven Groot

    First try to open the file with ios::in | ios::out, and if that fails, try to open it with ios::out only.

    Flushing the file manually before close isn't necessary, as closing it will automatically flush it.

  • User profile image
    MasterPi

    EDIT: nvm

     

  • User profile image
    Sven Groot

    Just a couple of other general comments, since you said you're starting with C++.

    1. Rather than <string.h>, you should use <cstring>. They are basically the same, but <cstring> puts its contents in namespace std, and is therefore preferred in C++. Basically, ever C standard library header that ends in .h has a C++ equivelant that starts with c and doesn't end in .h.
    2. The if/else if/else for your menu is probably better replaced by a switch, especially if you want to add more options.
    3. It's not necessary to do a manual conversion in your get_int function. Simply doing "cin >> n;" (where n is an int) will work.
  • User profile image
    bigthegeek

    Hey, thank you guys so far.

    First of all I tried doing what Sven said by trying to open the file with ios::in | ios::out and if it doesn't work, opening it with just ios::out, like this:

    // Open file for binary read-write access.
        fstream fbin(filename, ios::in | ios::out | ios::binary);
        if (!fbin) 
        {
            fstream fbin(filename, ios::out | ios::binary);
            if (!fbin)
            {
                cout << "Could not open file " << filename << endl;
                return -1;
            }
        }

    This way the file is created if it doesn't exist, but it doesn't get written to, only when you open an already existing file. It's weird, ins't it supposed to work? I read that by combining ios::out and ios::in the file is created if it doesn't exist..

  • User profile image
    Sven Groot

    In that code you are creating a new variable named fbin that's scoped to the if block only and which hides the fbin that's scoped to the function. Once the if block ends, the second fbin goes out of scope and is destructed, closing the file. The original fbin is left in an error state so none of the other calls work.

    Instead of declaring a new variable in the if block, just call fbin.open on the original variable.

  • User profile image
    bigthegeek

    Just did that, and it worked great! Thanks! But one more thing, as I said, I read that by combining ios::in and ios::out it should work to, I have a couple of C++ books, and they both use that. Why isn't it working that way then?

  • User profile image
    Sven Groot

    Actually, your books are incorrect then.

    The ISO C++ standard, paragraph 27.8.1.3, item 2 states that combining the in and out flags will cause the open method to call std::fopen with "r+" as the file mode, which is defined in the ISO C99 standard, paragraph 7.19.5.3, item 3 as indicating that an existing file should be opened for reading and writing, but no file should be created if none exists.

    There may be some implementations that allow you create a file with ios::in | ios::out, but from what I can see in the standard it's not guaranteed behaviour.

     

  • User profile image
    bigthegeek

    Oh, ok, that explains it all. Thanks a lot for your help everyone! Wink

  • User profile image
    MasterPi

    Stylistically (some might disagree), you might consider using #define to declare constants that you'll use later on in your program. Like the 80 and 20 character limits...you might do yourself a favor by just simply defining #define MAXNAME 20. You'd have one place to change the size this way.

  • User profile image
    Sven Groot

    Don't use #define for constants in C++, use const. I do agree with using named constants though.

  • User profile image
    MasterPi

    Yeah, I've been doing a lot of C lately...haven't touched C++ for a while.

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.