当我使用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)
答案 0 :(得分:0)
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
缓冲,但对它的同胞什么也不做stringbuf
和strstreambuf
,并指定除(0,0)
具有不同的效果。用户定义的类派生自streambuf
可以做任何他们想做的事。 (对于filebuf
和参数 对于(p,s)
(而不是零),libstdc ++可以满足您的期望:s
的前p
个字节用作缓冲区,您必须分配该缓冲区 并取消分配。)最后提醒:通常涉及的缓冲区不仅仅是 语言/图书馆级别的人员。内核缓冲区,磁盘缓冲区和 一样也会有效果。检查和更改这些是 与系统有关。