read()函数从标准输入读取时不会忽略换行符。

5
我正在使用以下条件语句从标准输入中读取内容。
if ((n = read(0,buf,sizeof(buf))) != 0)

当从标准输入(input)中输入数据时,通常用户在完成后按回车键(enter)。但是read()也将'\n'视为输入,此时n = 1,条件不会评估为false。除了检查buf的内容之外,是否有一种方法可以使条件在用户在标准输入上按回车(没有输入任何内容)时评估为false?是否有任何其他函数可用于此目的,而不是read()
就这个问题而言,当输入来自标准输入(stdin)时,如何让read确定输入结束呢?

你在尝试做什么?一个一个地读取每个字符吗? - GManNickG
不,正如你从条件语句中所看到的,我正在将标准输入读入缓冲区(也许只是将其写回标准输出)。我希望当用户什么都不输入,只是按下回车键时停止。 - s_itbhu
啊,我现在明白了。我读错了。 - GManNickG
请解释一下为什么你要寻找一种在不检查缓冲区的情况下确定空行的方法。我真的看不出来这样做的原因。你总是可以编写自己的函数来检查缓冲区,但在文件结束时返回0,在错误时返回-1,在空行时返回'\n'。如果你逐个字符地读取缓冲区,那么你可以直接使用标准函数来实现此目的;但这等同于检查你的缓冲区 ;-). - Inshallah
基本上,我想知道是否有可能从标准输入终止行末而不检查缓冲区。就像当我从文件中读取时,当EOF发生时,读取调用会自动终止。在这种情况下,没有必要检查缓冲区的内容。 - s_itbhu
5个回答

9
你的问题是:
当从标准输入输入数据时,通常用户在完成后按回车键。但是read()也将'\n'视为输入,此时n = 1,条件不会评估为false。
第一个观点肯定是正确的。 回车键相当于换行键,因此当用户按下回车键时,键盘会生成一个换行符,因此read()函数返回该字符。这非常重要。
因此,你的条件是错误的——空行将包括换行符,因此字节数将为1。实际上,在标准输入为键盘时,只有一种方法可以使read()调用返回0,那就是键入EOF字符——在Unix上通常为Control-D,在DOS上为Control-Z。在Unix上,终端驱动程序将该字符解释为“即使没有换行符,也将先前的输入数据发送到程序”。如果用户在该行中什么都没有输入,则从read()返回的值将为零。如果输入来自文件,则在读取了最后一个数据之后,随后的读取将返回0字节。
如果输入来自管道,则在读取了管道中的所有数据之后,read()调用将阻塞,直到最后一个可以写入管道的文件描述符关闭;如果该文件描述符在当前进程中,则read()将永远挂起,即使挂起的进程永远无法向文件描述符写入——当然,假设是单线程进程。

非常重要的是,您需要解释为什么read()/recv()返回值为0与空行不同。如果从read()/recv()获取到0,则意味着您已经到达所有输入的末尾(即已关闭,无法添加新的输入)。 - Will
注意:有些设备在read()返回零并不意味着不能再添加输入。显示我的古老性 - 磁带设备就是其中之一。目前,终端是另一个。通过open()上的奇特选项(如O_NONBLOCK),您可以在其他设备上获得该行为。但是,空行按定义是仅包含换行符的行 - 行尾标记(也称为换行符)是数据的一部分。如果您不想在程序中使用它,请将其删除。只有gets()会为您删除换行符,但您应该永远不要在生产代码中使用它。 - Jonathan Leffler

2

只需输入>1而不是!=0

唯一的误报情况是单个字符响应后跟中断(EOF)


1
你必须自己检查缓冲区。 例如:

while((n = read(0,buf,sizeof(buf))) > 0) {
  if(buf[0] == '\n') // won't handle pressing 9 spaces and then enter
    continue;
  ... process input

}

或者使用fgets等函数,然后简单地剥离掉\n。
while(fgets(buf,sizeof buf,stdin) != NULL) {
  char *ptr;
  size_t len;

  if((ptr = strchr(buf,'\n')) != NULL) //strip newline
    *ptr = 0; 
  len = strlen(buf);
  if(len == 0)
   continue;
  ... process input 
}

0

你最好使用fgets()来处理用户输入的任务,但是即使fgets()将换行符存储在缓冲区中。

然而,如果有换行符存在,你可以确定它是字符串中的最后一个字符,因此很容易将其去除。


由于fgets()只会在文件中存在换行符时才存储换行符,因此您无法确定缓冲区中的最后一个字符是否为换行符。如果最后一行没有包含换行符,则可能不是这样。 - Inshallah
@Inshalla 我的意思是,如果缓冲区中有换行符,则它肯定是最后一个字符。我会编辑我的帖子以明确说明这一点。 - qrdl
首先,我在这里使用文件描述符而不是文件指针。fgets()需要文件指针。我想要做的是当用户按下回车键时使条件评估为false。当然,我也可以在这里确定换行符是否是最后一个输入的字符。 - s_itbhu
1
你说你正在从标准输入读取,所以使用read(0, ..)、fread(..., stdin)或fgets(..., stdin)都无所谓。我的帖子是基于你在问题中提供的信息。如果你想在空行上停止,可以这样做:"if ('\n' != *fgets(buf, sizeof(buf)-1, stdin))" - qrdl
@qrdl,当然也会检查缓冲区,但出于美观的考虑,这可能是比其他解决方案更可取的选择。 - Inshallah
@qrdl:你对测试*fgets()的建议不是一个好主意;如果fgets()检测到EOF,它会返回一个空指针,而解引用它将导致程序崩溃(可能性很大;严格来说,这会引发未定义行为,结果不好,无论如何)。 - Jonathan Leffler

0

我非常确定没有办法在不检查缓冲区内容的情况下完成它。即使是readline()也是这样做的。你为什么反对它呢?


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