fstream ?

Discussion in 'C++' started by JKop, Sep 28, 2004.

  1. JKop

    JKop Guest

    Before I resort to the "fopen" family, can some-one please direct me to a
    tutorial on the net about the "fstream" family.

    I've gone through about 10 references and they're all complete bullshit. I'm
    trying to find simple answers to simple questions.

    What exactly does the constructor do? How does the whole eof thing work?


    -JKop
     
    JKop, Sep 28, 2004
    #1
    1. Advertising

  2. JKop <> writes:

    > Before I resort to the "fopen" family, can some-one please direct me to a
    > tutorial on the net about the "fstream" family.


    Not a tutorial but a reference, maybe it helps:
    http://www.cppreference.com/cppio.html

    > I've gone through about 10 references and they're all complete bullshit. I'm
    > trying to find simple answers to simple questions.
    >
    > What exactly does the constructor do? How does the whole eof thing work?


    See the reference.

    HTH && Kind regrads,
    Nicolas

    --
    | Nicolas Pavlidis | Elvis Presly: |\ |__ |
    | Student of SE & KM | "Into the goto" | \|__| |
    | | ICQ #320057056 | |
    |-------------------University of Technology, Graz----------------|
     
    Nicolas Pavlidis, Sep 28, 2004
    #2
    1. Advertising

  3. JKop

    JKop Guest

    JKop, Sep 28, 2004
    #3
  4. JKop

    Fraser Ross Guest

    The eof flag is set after an attempt to read past the end. It won't be set
    after opening a file of size 0.

    Fraser.


    "JKop"
    >
    > Before I resort to the "fopen" family, can some-one please direct me to a
    > tutorial on the net about the "fstream" family.
    >
    > I've gone through about 10 references and they're all complete bullshit.

    I'm
    > trying to find simple answers to simple questions.
    >
    > What exactly does the constructor do? How does the whole eof thing work?
    >
    >
    > -JKop
     
    Fraser Ross, Sep 28, 2004
    #4
  5. "Fraser Ross" <fraserATmembers.v21.co.unitedkingdom> wrote in message
    news:...
    > The eof flag is set after an attempt to read past the end. It won't be

    set
    > after opening a file of size 0.
    >
    > Fraser.
    >


    int i;
    file >> i;

    This may successfully read an integer AND set the end of file. If the
    integer is the very last thing in the file then the end of file will be set
    because you have to read past the end of file to determine that the integer
    has been fully read. But if there is a space after the last integer then
    reading that space will tell you that the integer has been fully read and
    end of file will not be set.

    Same applies to getline and other similar read functions that have to read
    to some terminator.

    John
     
    John Harrison, Sep 28, 2004
    #5
  6. JKop wrote:
    >
    > Before I resort to the "fopen" family, can some-one please direct me to a
    > tutorial on the net about the "fstream" family.
    >
    > I've gone through about 10 references and they're all complete bullshit. I'm
    > trying to find simple answers to simple questions.


    And the simple answers are:
    The 'fstream' family works exactly like the 2 objects you already are
    familiar with: cin, cout

    The only difference is: cin and cout are predefined objects, while you
    have to create the file-stream objects on your own.

    >
    > What exactly does the constructor do?


    Creating the stream object. Depending on which constructor you use exactly,
    the constructor may also open the file and connect the stream with it. The
    stream is then immediatly ready to be read from or write to.


    > How does the whole eof thing work?


    eof() works different in C or C++, then it does in a lot of other
    languages. In C or C++ the runtime system does not try to tell the
    future. As a consequence, eof returns true only if you try *and* failed
    to read past the end of the stream.
    So eof() is typically used to figure out, why a previous read operation
    has failed. If it was because eof() then everything is ok, the file
    could have been read correctly till the end.

    As an example, study this code snippet, which
    reads a text file line by line and copies the
    read line into a new file (just to do something
    with the data)

    #include <iostream>
    #include <fstream>
    #include <string>

    using namespace std;

    int main()
    {
    ifstream InFile( "C:\\test.dat" ); // ifstream: input file stream
    ofstream OutFile( "C:\\Copy.dat" ); // ofstream: output file stream

    if( !InFile.is_open() ) {
    cout << "Could not open input file" << endl;
    return EXIT_FAILURE;
    }

    if( !OutFile.is_open() ) {
    cout << "Could not open output file" << endl;
    return EXIT_FAILURE;
    }

    string InLine;

    while( getline( InFile, InLine ) )
    OutFile << InLine;

    if( !InFile.eof() )
    cout << "Error during read from file" << endl;
    }

    Note also the typical read loop:

    while( data_can_be_read ) {
    process data
    }

    // figure out why the loop terminated.
    // if it was because eof, everything is fine
    if( !eof() )
    Process error during read


    --
    Karl Heinz Buchegger
     
    Karl Heinz Buchegger, Sep 28, 2004
    #6
  7. JKop

    JKop Guest

    Thanks Karl.

    Here's the gist of what I'm trying to do at the moment: Let's say I have a
    file "mammals.txt" and it contains:

    cat
    dog
    ape
    monkey
    horse
    pig

    What I want to do is remove a certain line, let's say "horse", so that after
    the program runs, "mammals.txt" will look like so:

    cat
    dog
    ape
    monkey
    pig

    Here's what I'm doing so far:

    std::ifstream mammal_file( "mammal.txt" );

    if ( !mammal_file.is_open() )
    {
    std::cout << "ERROR: Unable to open \"mammal.txt\"\n";
    throw AnException();
    }

    //The file is now open
    std::string one_line;


    What I'm intending to do is copy all the lines into a new *output* file
    stream, except "horse", and then overwrite the original. Would you be able
    to provide any tips at all please?


    -JKop
     
    JKop, Sep 30, 2004
    #7
  8. JKop wrote:
    >
    >
    > What I'm intending to do is copy all the lines into a new *output* file
    > stream, except "horse", and then overwrite the original. Would you be able
    > to provide any tips at all please?


    Sure.

    You are on the right track. When dealing with text files (except in some
    rare circumstances), then the only possible way to modify that file is
    be writing a new one and while writing that file apply all the intended
    modifications. The only exception is when the replacement takes up the
    same amount of bytes as the original, then you can simply seek to the
    position and overwrite. But those cases are rare.

    The next thing you should think about is:
    How do I read. I mean: Which functionality do I use.
    In your case you have 2 choices

    you could eg.

    std::string InWord;
    InFile >> InWord;

    This will work in your case, since each line contains only a single word.
    So the basic loop would look like this

    ...
    while( mammal_file >> InWord ) {
    if( InWord != "horse" )
    OutFile << InWord << "\n";
    }
    ...

    But I often feel that a different approach is simpler. What is
    the problem? The problem comes if the file format gets more complicated,
    saying you have 3 words on each line and there have to be exactly 3 words
    on each line or otherwise the record (the line) is in error. So how to do this?
    In cases like this it is often simpler to use getline (as I did in the
    previous post). You use getline to read in the whole line as a single string
    and then use eg. string functions to seperate that total string into the
    words. Or you could use a stringstream to do the same.
    So why is that often simpler. Think of the error case. By using getline
    you have read the whole record, so if you figure out that that record
    is bogous you don't need to do anything special with the input stream. You
    just don't do anything with the bogus record and continue reading (and
    emit an error message).
    But in the read loop using >>?
    Well, first of all you need to detect that the line is in error. >> ignores
    line breaks because they are considered as white space. So detecting that
    the number of words per line is wrong isn't that simple. Even then, lets say
    the file format asks for a number, but in the file there is a text string "abc".
    >> will fail for obvious reasons. But what then? In order to continue you still

    need to read until the end of that record is read (the line break) for finding
    the start of the next record. With the getline-version this is done all automatically.

    But there is one (minor) point with getline. getline reads *and* includes the terminator
    (in this case '\n'). So often one needs to get rid of that terminator are at least
    deal with it. In your specific case, that would eg. be

    while( getline( mammal_file, one_line ) ) {
    if( one_line != "horse\n" )
    OutFile << one_line;
    }

    Note: In the version using >> I had to write a '\n' on my own to the output stream, because
    >> has filtered it away. In the getline version I don't have to do that, because getline

    has placed a '\n' in one_line as last character. That is: if there was one in the file.
    The last line in the file may or may not contain a terminating '\n'.

    So much for the thoughts about how to get at the file content.
    Back to your program.
    Open the output file and write to it whatever loop you want to choose.
    Once you have finished, all that is left is

    * delete the original file
    you will want to use function remove() from <cstdio> for that
    * rename the newly created file to the original file name
    you will want to use function rename() from <cstdio> for that

    It is a good idea to actually close the streams before the deletion and
    renaming takes place.

    As for: How to name that temporary file.
    Often just a variation of the input file name is used. If the file system
    supports file extensions, then it is often the name of the input file with
    a different extension. But there is a second solution. As a leftover from
    C there is a function called tmpnam, which generates a filename to be guaranteed
    to not collide with any other file name in the directory. But look up the documentation
    for that function. Often there are some tricky interactions with environment variables.

    --
    Karl Heinz Buchegger
     
    Karl Heinz Buchegger, Sep 30, 2004
    #8
  9. JKop

    JKop Guest

    Here's how it's going so far:

    #include <fstream>
    #include <string>

    int main()
    {
    std::ifstream mammals_file( "mammals.txt" );

    if ( !mammals_file.is_open() )
    {
    std::cout << "ERROR: Unable to open \"mammals.txt\"\n";
    return -1;
    }

    //The file is now open

    {
    std::eek:fstream out_file;

    std::string one_line;

    do
    {
    std::getline(mammals_file, one_line);

    if ( !CompareWithoutRegardToCaseX( "horse", one_line.c_str() )
    ) out_file << one_line << '\n'; //Should I have that '\n' there?
    }
    while ( !mammals_file.eof() );

    //Now save the file, overwriting the original, but how . . . ?
    }
    }


    Any thoughts?


    -JKop
     
    JKop, Sep 30, 2004
    #9
  10. JKop

    JKop Guest

    Thanks.

    I'd written a reply and then two seconds later I wrote another

    > * delete the original file
    > you will want to use function remove() from <cstdio> for that
    > * rename the newly created file to the original file name
    > you will want to use function rename() from <cstdio> for that


    I was wondering if I'd have to name the file in the first place? Is there
    anyway to just have it reside in memory, and then at my discretion save it
    as a file?

    One thing also, I'll have to use "getline" because there could be spaces on
    the lines in these files.


    -JKop
     
    JKop, Sep 30, 2004
    #10
  11. JKop wrote:
    >
    > Here's how it's going so far:
    >
    > #include <fstream>
    > #include <string>
    >
    > int main()
    > {
    > std::ifstream mammals_file( "mammals.txt" );
    >
    > if ( !mammals_file.is_open() )
    > {
    > std::cout << "ERROR: Unable to open \"mammals.txt\"\n";
    > return -1;
    > }
    >
    > //The file is now open
    >
    > {
    > std::eek:fstream out_file;
    >


    You want to pass a file name to the ofstream constructor, such that it
    opens the file for you.

    > std::string one_line;
    >
    > do
    > {
    > std::getline(mammals_file, one_line);
    >
    > if ( !CompareWithoutRegardToCaseX( "horse", one_line.c_str() )
    > ) out_file << one_line << '\n'; //Should I have that '\n' there?


    You used getline. So getline will include the terminator '\n' in the read
    string. There is however one potential pitfall, the last line in the file
    may not be terminated with '\n', thus the string will not contain it.

    Best is often: read with getline, and look at the last character in the string.
    If it is a '\n', discard it.


    > }
    > while ( !mammals_file.eof() );
    >
    > //Now save the file, overwriting the original, but how . . . ?


    delete original file with remove()
    rename written file to original file name using rename()


    out_file << one_line
    > }
    > }
    >
    > Any thoughts?
    >
    > -JKop



    --
    Karl Heinz Buchegger
     
    Karl Heinz Buchegger, Sep 30, 2004
    #11
  12. JKop wrote:
    >
    > Thanks.
    >
    > I'd written a reply and then two seconds later I wrote another
    >
    > > * delete the original file
    > > you will want to use function remove() from <cstdio> for that
    > > * rename the newly created file to the original file name
    > > you will want to use function rename() from <cstdio> for that

    >
    > I was wondering if I'd have to name the file in the first place? Is there
    > anyway to just have it reside in memory, and then at my discretion save it
    > as a file?


    std::vector<std::string> will happily store all the strings until
    you are ready to

    * close the original file
    * open the same file for output (which will automatically truncate it
    to 0 size)
    * write the vector content to it

    But note: In a high secure environment that would not be acceptable.
    During the time period of opening the file for writing and the actual
    finishing of writing, the data exist in memory only. If the computer
    crashes during that time (eg. power outage) the data is lost forever.

    Using the other version of writing to a temporary and then deleting/renaming
    ensures that there is always one complete copy of the whole data set on
    the hard disk.

    >
    > One thing also, I'll have to use "getline" because there could be spaces on
    > the lines in these files.


    Oh. I wanted to mention this, but somehow I forgot :)

    >
    > -JKop



    --
    Karl Heinz Buchegger
     
    Karl Heinz Buchegger, Sep 30, 2004
    #12
  13. JKop

    Howard Guest

    "Karl Heinz Buchegger" <> wrote in message
    news:...
    >
    > Best is often: read with getline, and look at the last character in the

    string.
    > If it is a '\n', discard it.
    >


    I've got a question on comparing a char against '\n'. Since on some
    systems, '\n' is represented by #13#10 (a CR/LF pair), what happens when you
    compare a char with it? Is there some internal mechanism that actually
    compares two bytes instead of just one?

    -Howard
     
    Howard, Sep 30, 2004
    #13
  14. JKop

    Ron Natalie Guest

    "Howard" <> wrote in message news:d0X6d.460930$OB3.267267@bgtnsc05-
    >
    > I've got a question on comparing a char against '\n'. Since on some
    > systems, '\n' is represented by #13#10 (a CR/LF pair), what happens when you
    > compare a char with it? Is there some internal mechanism that actually
    > compares two bytes instead of just one?
    >

    '\n' is ALWAYS a single character. ALWAYS.

    The only thing that happens is that under certain circumstances an output
    operation may output two characters when an single '\n' character is
    fed to it (and similarly on input).
     
    Ron Natalie, Sep 30, 2004
    #14
  15. JKop

    Howard Guest

    "Ron Natalie" <> wrote in message
    news:415c442f$0$27746$...
    >
    > "Howard" <> wrote in message

    news:d0X6d.460930$OB3.267267@bgtnsc05-
    > >
    > > I've got a question on comparing a char against '\n'. Since on some
    > > systems, '\n' is represented by #13#10 (a CR/LF pair), what happens when

    you
    > > compare a char with it? Is there some internal mechanism that actually
    > > compares two bytes instead of just one?
    > >

    > '\n' is ALWAYS a single character. ALWAYS.
    >
    > The only thing that happens is that under certain circumstances an output
    > operation may output two characters when an single '\n' character is
    > fed to it (and similarly on input).
    >


    Ah, that makes sense. Thanks.
    -Howard
     
    Howard, Sep 30, 2004
    #15
  16. >> std::string one_line;
    >>
    >> do
    >> {
    >> std::getline(mammals_file, one_line);
    >>
    >> if ( !CompareWithoutRegardToCaseX( "horse",
    >> one_line.c_str() )
    >> ) out_file << one_line << '\n'; //Should I have that '\n' there?

    >
    > You used getline. So getline will include the terminator '\n' in the read
    > string. There is however one potential pitfall, the last line in the file
    > may not be terminated with '\n', thus the string will not contain it.
    >


    No it won't. getline reads the newline but does not include it in the read
    string.

    john
     
    John Harrison, Oct 1, 2004
    #16
  17. John Harrison wrote:
    >
    > >> std::string one_line;
    > >>
    > >> do
    > >> {
    > >> std::getline(mammals_file, one_line);
    > >>
    > >> if ( !CompareWithoutRegardToCaseX( "horse",
    > >> one_line.c_str() )
    > >> ) out_file << one_line << '\n'; //Should I have that '\n' there?

    > >
    > > You used getline. So getline will include the terminator '\n' in the read
    > > string. There is however one potential pitfall, the last line in the file
    > > may not be terminated with '\n', thus the string will not contain it.
    > >

    >
    > No it won't. getline reads the newline but does not include it in the read
    > string.


    Thanks for correcting.
    I somehow always mix this up with fgets() behaviour


    --
    Karl Heinz Buchegger
     
    Karl Heinz Buchegger, Oct 1, 2004
    #17
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Marc Schellens

    vector of fstream

    Marc Schellens, Jul 15, 2003, in forum: C++
    Replies:
    2
    Views:
    397
    John Harrison
    Jul 16, 2003
  2. Marc Schellens

    fstream.open( name, mode) fails

    Marc Schellens, Jul 15, 2003, in forum: C++
    Replies:
    0
    Views:
    1,471
    Marc Schellens
    Jul 15, 2003
  3. las

    fstream problem

    las, Jul 16, 2003, in forum: C++
    Replies:
    1
    Views:
    440
    Simon Saunders
    Jul 16, 2003
  4. gukn9700

    Weird thing with fstream

    gukn9700, Jul 17, 2003, in forum: C++
    Replies:
    0
    Views:
    337
    gukn9700
    Jul 17, 2003
  5. Armando
    Replies:
    6
    Views:
    763
    Armando
    Jan 29, 2004
Loading...

Share This Page