basic input/output with files driving crazy

I finished reading my c++ book and the tutorial in this website. both had similar contents but none tackled the problems I am facing and am still struggling to make the I/O functions with files work as they should.


the example file "example.txt" has the following lines, size of file=47 bytes
1
2
3
first sentence
second sentence
third sentence


my code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    ofstream myfile("example.txt",ios::app);///no use of ios::trunc as without declaring it,
                                       // previous content is deleted & replaced by blank file///same
                                             ///effect by ios::ate
    cout<<myfile.tellp()<<endl;///result was '0'
    myfile.seekp(24, ios::end);
    cout<<myfile.tellp()<<endl;///result was '71'
    myfile<<"should add this text from 71 but text is added from 47, end of file ";//but still writes from 47 which is the
/// end of file while the tellp says should write from 71
}


question are in comment. still for better description:
1. if i just use ofstream myfile("example.txt");///no ios::app the previous content of file is deleted just by running this line, as if "ios::trunc" was its default parameter but c++ tutorial didn't say anything about it.

2. kind of similar effect with "ios::ate". tutorial said using it would set the writing position to the end of file but using it deleted previous content of file, same effect as in Q)1.

3. using the above code, using myfile.seekp(24, ios::end); changed the seek position to "71". but when i tried to add the text, it was added from end of file whose position was 47. how can i add add text to existing file from desired position without deleting previous contents? above code is my shot at doing it, it obviously didn't work out.



NOTE: i deliberately tried to be as much verbose as i could for better description of the problem, because i have finished a book and seriously this is the most confusing chapter in C++ that i have encountered. waiting for responses.
Last edited on
but c++ tutorial didn't say anything about it.

The C++ Standard isn't so clear on the matter, either. But it does have this table which shows the meaningful combinations of the open mode flags:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Table 92 File Open modes
//+------------------------------------------------------------+
//| ios_base Flag combination            stdio equivalent      |
//|binary  in  out  trunc  app                                 |
//+------------------------------------------------------------+
//|             +                        "w"                   |
//|             +           +            "a"                   |
//|             +     +                  "w"                   |
//|         +                            "r"                   |
//|         +   +                        "r+"                  |
//|         +   +     +                  "w+"                  |
//+------------------------------------------------------------+
//|   +         +                        "wb"                  |
//|   +         +           +            "ab"                  |
//|   +         +     +                  "wb"                  |
//|   +     +                            "rb"                  |
//|   +     +   +                        "r+b"                 |
//|   +     +   +     +                  "w+b"                 |
//+------------------------------------------------------------+ 

(To save typing I stole this from:
https://gcc.gnu.org/ml/libstdc++/2007-06/msg00012.html )


You can see that both out and out | trunc are equivalent to "w" in stdio terms. And according to:
http://www.cplusplus.com/reference/cstdio/fopen/

"w"

write: Create an empty file for output operations. If a file with the same name already exists, its contents are discarded and the file is treated as a new empty file.

So the behviour does hang together. trunc only really has an effect when used with in | out.

ate doesn't make the table. I assume this is because it's an instruction to set the stream pointer to the end of the file rather than an actual open mode(?) So while ate does set the pointer to the end of the file when you open with out, the file is still going to be empty.

Adding the text to the end of the existing file is exactly what's supposed to happen when a file is opened with mode app.

Andy
Last edited on
> using myfile.seekp(24, ios::end); changed the seek position to "71".

It resulted in undefined behaviour.

The behaviour of seek operations on a std::filebuf is defined in terms of std::fseek()
http://en.cppreference.com/w/cpp/io/c/fseek

To set the put position to 24 characters past the end of file, seek to the end (or open with std::app) and then write 24 filler characters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <fstream>

int main()
{
    const char* const path = "example.txt" ;

    // create file
    std::ofstream(path) << "first sentence\nsecond sentence\nthird sentence\n" ;

    // number of characters in file
    {
        std::ifstream file(path) ; // open as text
        char c ;
        std::streamoff cnt = 0 ;
        while( file.get(c) ) ++cnt ;
        std::cout << "file '" << path << "' contains " << cnt << " characters\n" ;
    }

    // number of bytes in file
    {
        std::ifstream file( path, std::ios::binary ) ; // open as binary
        char c ;
        std::streamoff cnt = 0 ;
        while( file.get(c) ) ++cnt ;
        std::cout << "file '" << path << "' contains " << cnt << " bytes\n" ;
    }

    {
        std::ofstream file( path, std::ios::in | std::ios::out ) ; // do not truncate
        file.seekp( 0, std::ios::end ) ; // seek to end
        std::cout << "after seek to end: " << std::streamoff( file.tellp() ) << '\n' ;

        for( int i = 0 ; i < 24 ; ++i ) file.put( '.' ) ; // write 24 dots
        std::streamoff offset( file.tellp() ) ;
        std::cout << "after writing 24 dots: " << offset << '\n' ;

        file << "should add this text from offset " << offset << '\n' ;
        std::cout << "after writing more stuff: " << std::streamoff( file.tellp() ) << '\n' ;
    }

    // dump the contents of the modified file
    std::cout << "\n====== contents of file '" << path << "' ==========\n\n"
              << std::ifstream(path).rdbuf() << "\n=========================\n" ;
}

Unix clone (escape sequence '\n' written as a single byte): http://coliru.stacked-crooked.com/a/1f6aceb57ea9ac5b
Windows (escape sequence '\n' written as a two-byte sequence): http://rextester.com/CUUUZ44619
@JLBorges
std::ofstream file( path, std::ios::in | std::ios::out ) ; // do not truncate
bingo! that's what i wanted, use of ios::in|ios::out in ofstream opened the file without losing the previous contents; that's what i wanted. but on the sad note, that's quite confusing. Use of ofstream makes the file strean available for output only, isn't it? so only ios::out should be valid, not ios::in. how does it work? nontheless, this line of code solved my problem. thnx.

@andy thanks for the table for combination of open mode flags. now have better idea of it.thnx.
BTW I tend to prefer to use std::fstream (rather than std::ofstream) when specifying the ios::in flag as it seems a bit weird having an out stream open for input.

Andy
> Use of ofstream makes the file strean available for output only, isn't it?

Yes.

> so only ios::out should be valid, not ios::in. how does it work?

For an output stream, std::ios::out is automatically added even if we don't specify it.
Constructor (2): First, performs the same steps as the default constructor, then associates the stream with a file by calling rdbuf()->open(filename, mode | std::ios_base::out)
http://en.cppreference.com/w/cpp/io/basic_ofstream/basic_ofstream


std::ofstream file( path, std::ios::in ) // do not truncate, fail if file does not exist and
std::ofstream file( path, std::ios::in | std::ios::out ) ; // do not truncate , fail if file does not exist
are equivalent.

It is somewhat confusing if one is unfamiliar with the architecture of the input/output library. The open mode std::ios::in | std::ios::out is passed to the open() function of the associated std::filebuf. The std::ofstream object wraps the stream buffer and does not provide any input operations.

It is naive to think that for performing only output operations on a file without truncating it, using std::fstream instead of a std::ofstream is a sound idea. It confers no advantage, and introduces the risk of a typo ( << instead of >> ) generating silent errors at run-time.

Suggestion: Use these open modes for output to files

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1. Open for output, truncate if file exists, create new otherwise
std::ofstream file(path) ;

// 2. Open for output, do not truncate, fail if file does not exist
std::ofstream file( path, std::ios_base::in | std::ios_base::out ) ;

// 3. Open for output, do not truncate, fail if file does not exist, seek to end after opening
std::ofstream file( path, std::ios_base::in | std::ios_base::out | std::ios_base::ate ) ;

// 4. Open for output, truncate if file exists, do not truncate if file exists, create new otherwise
//    seek to the end of stream before each write
std::ofstream file( path, std::ios_base::app ) ;

// 5. Open for output, do not truncate if file exists, fail if file does not exist
//    seek to the end of stream before each write
std::ofstream file( path, std::ios_base::in | std::ios_base::out | std::ios_base::app ) ;

Last edited on
@JLBorges thanks for suggestions at the end, clarifying the effect of combos of mode flags. though:
1
2
3
// 4. Open for output, truncate if file exists, create new otherwise
//    seek to the end of stream before each write
std::ofstream file( path, std::ios_base::app ) ;

in which truncate if file exists, i think it should be do not truncate.

> in which truncate if file exists, i think it should be do not truncate.

Yes, thank you, koopey! I've corrected it now.
Topic archived. No new replies allowed.