最近出现基本问题时,我一直在阅读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'的功能。每次按下'enter',读取功能都会终止,并将包括'ent'产生的'\ n'在内的字符传递给写入功能。因此它进入循环,并首先打印读取的字符数。
但是,以下测试似乎违背了上述内容,并且违反了代码的结构。
> $ ./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
我想一遍又一遍,只是想不通。您能帮我吗?
有几种缓冲正在进行。程序的输入由伪终端设备的行缓冲规则进行缓冲。在输出端,有一个文件系统缓存(OS中整个文件的缓冲区),并且在打印为FILE *
类型时在C程序中有额外的缓冲区。但是read
和write
绕过FILE *
缓冲,或多或少直接将数据移入/移出文件系统缓存。
因此,似乎所有输出都输出到终端时,stdout
缓冲区会自动刷新,但重定向到文件时不会刷新。因此,我建议将通话添加到
fflush(stdout);
printf
通话后。这应该显式刷新缓冲区(并强制执行所需的输出顺序)。
[要注意的重要事项是,当您使用由库函数(如FILE *
)操纵的C级结构的fopen
时,以及当您使用原始文件描述符(即只是一个整数,但指的是基础操作系统文件)。 FILE
数据类型是此较低级别Unix实现细节的包装。 FILE
函数实现了附加的缓冲层,因此较低的级别可以在较大的块上运行,并且您可以高效地执行“字节一字型”处理,而无需进行大量的I / O握手。
首先是一个解决方案,将stdout
和write()
更改为write()
:
man stdio
有关代码的注意事项:
man stdio
是可选的,因为否则您不会从read
中读取。write
取元素大小和元素数量来确定应写入的数量。部分元素不会被读取,因此element size fread
and fwrite
(不算1)是文本通常需要的。fread
的设置不是很好定义,有关更多信息,请参见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
等)未缓冲,并且完全绕过这些不应混在同一个文件中