Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
435 views
in Technique[技术] by (71.8m points)

c++ - std::ofstream with std::ate not opening at end

I am trying to open a file for output and append to it. After appending to it, I want to move my output position somewhere else in the file and overwrite existing data. As I understand it, std::ios_base::app will force all writes to be at the end of the file which is not what I want to do. As such, I believe std::ios_base::ate is the correct flag to pass to std::ofstream::open(). However, it seems to be not working as expected:

// g++ test.cpp
// clang++ test.cpp
// with and without -std=c++11
#include <iostream>
#include <fstream>

int main() {
    std::streampos fin, at;
    {
        std::ofstream initial;
        initial.open("test", std::ios_base::out | std::ios_base::binary);
        if ( not initial.good() ) {
            std::cerr << "initial bad open" << std::endl;
            return 1;
        }
        int b = 100;
        initial.write((char*)&b, sizeof(b));
        initial.flush();
        if ( not initial.good() ) {
            std::cerr << "initial write bad" << std::endl;
            return 1;
        }
        fin = initial.tellp();
    }
    {
        std::ofstream check;
        check.open("test", std::ios_base::out | std::ios_base::binary | std::ios_base::ate);
        if ( not check.good() ) {
            std::cerr << "check bad open" << std::endl;
            return 1;
        }
        at = check.tellp();
        if ( fin != at ) {
            std::cerr << "opened at wrong position!
fin:" << fin << "
" << "at:" << at << std::endl;
            return 1;
        }
        int bb = 200;
        check.write((char*)&bb, sizeof(bb));
        check.flush();
        if ( not check.good() ) {
            std::cerr << "check write bad" << std::endl;
            return 1;
        }
        at = check.tellp();
    }
    if ( (fin + std::streampos(sizeof(int))) != at ) {
        std::cerr << "overwrite?
fin:" << fin << "
" << "at:" << at << std::endl;
        return 1;
    }
    return 0;
}

In particular, it seems that std::ios_base::ate does not move the initial output pointer to the end with the example seen above. Obviously this would result in the first write overwriting the beginning of the file (which is what caused my trouble).

It seems that either the implementation is incorrect or else cplusplus.com is incorrect ("The output position starts at the end of the file.") and cppreference.com is ambiguous ("seek to the end of stream immediately after open": which stream?).

There is obviously an easy workaround: just use stream.seekp(0, std::ios_base::end).

So my question is thus: is my code incorrect? Is the implementation incorrect? Are the reference sites incorrect? Any insight would be appreciated.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

As you can see from the following chart in N4296 [filebuf.members]

file io

The combination binary | out will open the file in the stdio equivalent of "wb", which will truncate to zero length or create binary file for writing (N1570 7.21.5.2).

As counterintuitive as it sounds for an ofstream, you will need to add the in flag if you don't want your file to be truncated, or app if you want to avoid truncation and seek to the end of the file on each write.

Bonus tip: Unlike fstream, ifstream and ofstream will automatically or std::ios_base::in and std::ios_base::out respectively with any flags you provide to the constructor or to open. You can also use the object itself to access the flags:

std::ofstream check("test", check.in | check.binary | check.ate);

The checks for good can also be shortened to if (!initial) etc.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...