std :: ofstream-没有大于1023的缓冲字符串(即时刷新)

时间:2019-07-02 09:49:24

标签: c++ buffer fstream ofstream

当我使用ofstream更改pubsetbuf(...)缓冲区的大小时,一切工作正常,除非当我将ofstream的单个字符串放置的时间长于1023时(在下面的代码中) 。是正确的行为还是我做错了什么?

int main(){
    std::vector<char> rawBuf;
    std::ofstream stream;

    rawBuf.resize(20000);
    stream.rdbuf()->pubsetbuf(&rawBuf[0], 20000);

    stream.open("file.txt", std::ios_base::app);

    std::string data(1499, 'b');

    for(int i = 0; i < 10; i++)
    {   
        stream << data.substr(0, 1024) << "\n"; //1023-length string works great
        sleep(1);
    }
    stream.flush();
    stream.close();

    return 0;
}

当长度为1024的字符串strace ./program显示如下内容:

writev(3, [{iov_base=NULL, iov_len=0}, {iov_base="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"..., iov_len=1024}], 2) = 1024
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffcf3889ac0) = 0
writev(3, [{iov_base="\n", iov_len=1}, {iov_base="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"..., iov_len=1024}], 2) = 1025
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffcf3889ac0) = 0
... and so on 10x

当字符串长度为1023时,一切正常:

nanosleep({tv_sec=1, tv_nsec=0}, 0x7fff8e13a980) = 0
nanosleep({tv_sec=1, tv_nsec=0}, 0x7fff8e13a980) = 0
... 10x

然后:

write(3, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"..., 10240) = 10240

为什么这里是一次写入,而以前不是?

编辑:

gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3)

1 个答案:

答案 0 :(得分:0)

[filebuf.virtuals]/12

basic_streambuf* setbuf(char_type* s, streamsize n) override;
     

效果:如果在流上发生任何I / O之前在流上调用setbuf(0, 0),则该流将变为无缓冲。否则   结果是实现定义的。 “无缓冲”表示pbase()   和pptr()始终返回null并显示输出到文件   尽快。

“实施定义”包括“工作正常”和“只有一次写入”等。实际上,这就是libstdc ++ 7.3.0 says

  

首先,您确定您了解缓冲吗?特别是   事实上C ++可能与它没有任何关系?

     

缓冲的规则可能有点奇怪,但并非如此   与C语言不同(也许这就是为什么它们可能有些奇怪的原因。)   许多人认为在输出流中编写换行符   自动刷新输出缓冲区。仅当   实际上,输出流是终端,而不是文件或其他文件   设备-甚至可能不正确,因为C ++没有说明   文件或终端。所有这些都取决于系统。 (   “仅在终端上发生换行缓冲区刷新”的事情主要是   但是在Unix系统上为true。)

     

有些人还认为,仅向输出流发送endl   写换行符。这是不正确的。编写换行符后,   缓冲区也被刷新。也许这就是您想要的效果   写入屏幕-尽快删除文字,等等-   但在对文件执行此操作时,缓冲会大大浪费:

output << "a line of text" << endl;
output << some_data_variable << endl;
output << "another line of text" << endl; 
     

在这种情况下,应该做的正确的事情是只写出数据并让   库和系统担心缓冲。如果您需要   换行符,只需写一个换行符:

output << "a line of text\n"
 << some_data_variable << '\n'
 << "another line of text\n"; 
     

我也将输出语句合并为一个语句。您   通过将单个换行符移到开头可以使代码更漂亮   例如,最后一行上引用的文字。

     

如果确实需要刷新上面的缓冲区,则可以发送endl   您还需要换行符,或者自己刷新缓冲区:

output << ...... << flush;    // can use std::flush manipulator
output.flush();               // or call a member fn 
     

另一方面,有时应该将文件写入   喜欢写标准错误;不应进行缓冲,因为   数据需要快速显示(一个主要的示例是   与安全相关的信息)。这样做的方法只是关闭   在完成任何I / O操作之前进行缓冲(注意   该打开算作一个I / O操作):

std::ofstream    os;
std::ifstream    is;
int   i;

os.rdbuf()->pubsetbuf(0,0);
is.rdbuf()->pubsetbuf(0,0);

os.open("/foo/bar/baz");
is.open("/qux/quux/quuux");
...
os << "this data is written immediately\n";
is >> i;   // and this will probably cause a disk read 
     

由于缓冲的所有方面都由streambuf派生   成员,必须与rdbuf()取得该成员。然后   可以调用setbuf的公共版本。参数是相同的   与标准C I / O库功能(缓冲区)相同   然后是它的大小)。

     

其中很大一部分取决于实现。例如,   streambuf未为其自己的setbuf()-ish指定任何操作   功能;从streambuf派生的类各自定义行为   对于该类“有意义”:(0,0)的自变量已关闭   为filebuf缓冲,但对它的同胞什么也不做   stringbufstrstreambuf,并指定除   (0,0)具有不同的效果。用户定义的类派生自   streambuf可以做任何他们想做的事。 (对于filebuf和参数   对于(p,s)(而不是零),libstdc ++可以满足您的期望:   s的前p个字节用作缓冲区,您必须分配该缓冲区   并取消分配。)

     

最后提醒:通常涉及的缓冲区不仅仅是   语言/图书馆级别的人员。内核缓冲区,磁盘缓冲区和   一样也会有效果。检查和更改这些是   与系统有关。