为什么从stdin读取()在换行符上停止?

3

如果以下代码

ssize_t len = read(0, buf, BUF_SIZE);
perror("read()");
printf ("%i '%s'\n", (int) len, buf);

执行从终端读取时,read() 在换行符(按 Enter 键)处终止输入而不显示任何错误,即逐行输入。但是,当我将其重定向为使用 shell 从文件输入时,它会忽略换行符并继续读取整个缓冲区或直到 EOF。

根据文档,后一种行为更为预期。那么,为什么终端输入会在换行符上终止?这是否意味着此输入默认情况下是非阻塞的?检查是否达到了 EOF(Ctrl-D)或某些其他条件导致返回不完整的输入的正确方法是什么?


这是很多问题。我会在有时间的时候写一个详细的答案。但你可以在APIUE书的第3章和第18章中找到所有的答案。 - Haris
2个回答

2

在您的情况下,标准输入是阻塞的。 read 会一直阻塞,直到至少有一个字节可用,然后可以返回任意大小的块。您不能对它将读取多少做出任何假设,它甚至可能总是返回单个字节。

在大多数情况下,它将读取当前可用的所有内容并返回。默认情况下,终端处于所谓的“规范模式”,并按行提供输入。您可以在 termios(3) 手册页中了解更多信息。因此,read 在您按回车键之前无法获取任何字节。然后该行变为可用状态,read 获取整行并立即返回而不等待更多数据。

如果您想禁用规范模式并在用户在终端中键入字节时立即接收它们,则有一个相关问题 如何在按键后立即读取终端的输入缓冲区

检查是否达到 EOF(Ctrl-D)或某些其他条件导致返回不完整的输入的正确方法是什么?

在 EOF 条件下,read 函数返回 0,在 man 手册中有记录。如果你想读取整个文件,需要在循环中调用 read 直到它返回 0。如果 read 返回 -1,则表示出现错误。
如果你想逐行读取文件,则需要在应用程序中实现缓冲区。不断地调用 read,直到缓冲区中有换行符为止。然后处理该行内容,保留剩余部分并重新开始读取。或者,使用 stdio.h 函数(如 fgets),它们已经为你实现了缓冲区。

1

read() 系统调用默认以非阻塞模式从终端读取输入,因此在读取所有可用输入后立即返回,而无需等待更多的输入。这就是为什么回车符被用于结束终端输入的原因。

当从文件中读取时,您可以检查 read() 的返回值,以查看是否已到达 EOF 。如果 read() 返回 0,则已到达文件结尾(EOF)。如果 read() 提供的值小于要求读取的字节数,这意味着其他情况导致它返回不完整的输入。您可以使用 perror() 打印错误消息,详细说明发生了特定的错误。


1
perror()只应在read()返回-1时调用,因为它仅在这种情况下设置errno。如果返回值为0(EOF)或正数(读取的一些字节可能少于请求的字节),则没有错误。 - tla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接