std :: ofstream-没有比1023(即时冲洗)更长的缓冲字符串

std::ofstream - no buffering string longer than 1023 (instant flush)

本文关键字:字符串 缓冲 ofstream- 1023 std      更新时间:2023-10-16

当我用 pubsetbuf(...)更改ofstream缓冲区的大小时,一切都很好,除非我将单字符串放到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)

per [filebuf.virtuals]/12:

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

效果:如果在该流上发生任何I/O之前在流上调用setbuf(0, 0),则该流未掩盖。否则 结果是实施定义的。"无封闭"表示pbase() pptr()始终将null和输出返回到文件应显示 尽快。

"实现定义"包括"工作正常"answers"只有单个写作"和其他内容。实际上,这是libstdc 7.3.0说的:

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

缓冲规则可能有些奇怪,但它们不是任何 与C的不同。(也许这就是为什么它们有点奇怪。( 许多人认为为输出流编写新线 自动冲洗输出缓冲区。只有当 实际上,输出流是终端,而不是文件或其他一些 设备 - 这甚至可能是不正确的,因为C 什么也没说 文件或终端。所有这些都是依赖于系统的。(这 "仅发生在终端上的newline-buffer燃烧"主要是 不过,在Unix系统上是正确的。(

有些人还认为仅发送端口仅向下发送输出流 写一个新线。这是不正确的;编写新线后, 缓冲区也被冲洗。也许这是您想要的效果 写在屏幕上 - 尽快将文本删除,等等 - 但是,在将其执行到文件时,缓冲大部分是浪费的:

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

在这种情况下,要做的适当的事情只是将数据写出来,然后让 库和系统担心缓冲。如果您需要一个 newline,只需写一个newline:

output << "a line of textn"
 << some_data_variable << 'n'
 << "another line of textn"; 

我也将输出语句加入了一个语句。你 可以通过将单个新线移至"开始"来使代码更漂亮 例如,最后一行的引用文本。

如果您确实需要冲洗上方的缓冲区,则可以发送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 immediatelyn";
is >> i;   // and this will probably cause a disk read 

由于缓冲的所有方面均由streambuf衍生 成员,有必要使用rdbuf()获得该成员。然后 可以调用setbuf的公共版本。论点是相同的 作为标准C I/O库功能(缓冲区区域( 其次是大小(。

其中很大程度上取决于实现。例如, streambuf没有为其自己的setbuf()指定任何动作 功能;从streambuf派生的类定义行为 对于该类别的"有意义":(0,0)的论点关闭 filebuf的缓冲,但对其兄弟姐妹一无所有 stringbufstrstreambuf,并指定除 (0,0)具有不同的效果。从用户定义的类派生的类 streambuf可以做任何他们想做的事情。(对于filebuf和参数 对于(p,s),除了零以外,Libstdc 可以做您期望的事情: p的第一个s字节用作缓冲区,您必须分配 和DealLocate。(

最后提醒:通常涉及的缓冲区不仅仅是 那些在语言/图书馆级别。内核缓冲区,磁盘缓冲区和 类似也会产生效果。检查和更改这些是 依赖系统。