当一个基本问题出现时,我最近一直在阅读APUE。 我的代码如下所示
#include <apue.h>
#define BUFFSIZE 4096
int main()
{
int n;
char buf[BUFFSIZE];
while((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
{
printf("n is %d\n", n); //this line is added by me for testing
if(write(STDOUT_FILENO, buf, n) != n)
err_sys("write error"); //functions defined by the book to print error message
}
if(n < 0)
err_sys("read error");
exit(0);
}
编译后,当我运行如下所示的程序时
> $ ./mycat
123456[enter]
n is 7
123456
1234[enter]
n is 5
1234
它似乎根据我的代码结构工作。我不太了解' 输入 '的功能。每当我按下“ 输入 ”时,读取函数终止并将包含由'enter'生成的'\ n'的字符传递给write函数。所以它进入循环内部,首先打印读取的字符数。
但是,以下测试似乎违背了上述内容和代码结构。
> $ ./mycat > data
123456[enter]
1234[enter]
^D
> $ cat data
123456
1234
n is 7
n is 5
似乎程序首先将所有字符写入文件然后打印'n'的值,但是根据我的理解,它应首先打印如下
n is 7
123456
n is 5
1234
我一次又一次地想,只是想不出来。你能帮我吗?
答案 0 :(得分:3)
write()
不缓冲。 printf()
stdout
缓存stdout
,但在某种程度上取决于输出的位置。
如果write()
的输出进入控制台它的行缓冲,如果不是完全缓冲,在第二个例子中导致在程序结束时刷新,而输出形成对man stdio
的呼叫立即出去。
来自{{3}}:
[...]标准输入和输出流是完全的 当且仅当流不引用交互式时才缓冲 设备
引用终端设备的输出流始终是行 默认缓冲;
答案 1 :(得分:2)
首先是一个解决方案,将read
和write
更改为fread
and fwrite
:
#include <apue.h>
#define BUFFSIZE 4096
int main()
{
int n;
char buf[BUFFSIZE];
while((n = fread(buf, 1, BUFFSIZE, stdin)) > 0)
{
printf("n is %d\n", n); //this line is added by me for testing
if(fwrite(buf, 1, n, stdout) != n) {
// note: if err_sys depend on errno, it may print wrong error
err_sys("write error");
}
}
if(ferror(stdin)) {
// note: if err_sys depend on errno, it may print wrong error
err_sys("read error");
}
exit(0);
}
有关代码的注意事项:
fread
是可选的,因为您不会从stdio
读取。fread
and fwrite
获取元素大小和元素数量,以确定应该写多少。部分元素将不会被读取,因此元素大小 1
(不计数1)是文本通常需要的。errno
设置定义不明确,有关详情,请参阅here。最后简短解释: stdio 输入和输出被缓冲。低级文件描述符IO(open
和close
,read
和write
等)未缓冲,并完全绕过 stdio 缓冲。 这些不应该在同一个文件上混合使用,因为即使你尝试这样做也很容易混淆缓冲细节,因此它应该&#34;应该&#34;工作。即使你让它在你的操作系统上工作,它也可能在为不同的操作系统和库编译时中断。所以不要这样做,而是使用其中一个用于同一个打开的文件。
答案 2 :(得分:1)
有几种缓冲正在进行中。程序的输入由伪终端设备的线路缓冲规程缓冲。在输出端,有一个文件系统缓存(操作系统中用于整个文件的缓冲区),以及在打印到FILE *
类型时C程序中的额外缓冲。但是read
和write
绕过FILE *
缓冲并将数据或多或少地直接移入/移出文件系统缓存。
因此,当所有输出都转到终端时,您的stdout
缓冲区会自动刷新,但重定向到文件时则不会。所以我建议添加一个电话
fflush(stdout);
printf
致电后的。这应该显式刷新缓冲区(并强制执行所需的输出顺序)。
需要注意的重要一点是,当您使用FILE *
时,这是由库函数(如fopen
)操纵的C级结构,以及当您使用FILE
时。重新使用原始文件描述符(它只是一个整数,但指的是基础操作系统文件)。 FILE
数据类型是这个较低级Unix实现细节的包装器。 {{1}}函数实现了额外的缓冲层,因此较低级别可以在较大的块上运行,并且您可以有效地执行字节类型处理,而无需进行大量的I / O握手。