【问题标题】:read() from stdin doesn't ignore newline来自 stdin 的 read() 不会忽略换行符
【发布时间】:2010-11-17 06:19:46
【问题描述】:

我正在使用以下条件语句从标准输入中读取数据。

if ((n = read(0,buf,sizeof(buf))) != 0)

当从标准输入输入数据时,一般用户在完成后按回车键。但是read() 也将 '\n' 视为输入,在这种情况下n = 1 并且条件不会评估为假。除了检查 buf 的内容之外,当用户在标准输入上按 enter(不输入任何内容)时,有没有办法使条件评估为 false。除了read() 之外,我还有什么其他功能可以用于此目的吗?

就此而言,当输入来自标准输入 (stdin) 时,读取确定输入结束的方法是什么?

【问题讨论】:

  • 你想做什么?一次读一个字符?
  • 不,正如您从条件中看到的那样,我正在从标准输入读取到缓冲区(也许只是将其写回标准输出)。当用户没有输入任何内容并按回车键时,我想停止。
  • 啊,我明白了。我读错了。
  • 请解释为什么您正在寻找一种无需检查缓冲区即可确定空行的方法。我真的不明白它的原因。您始终可以编写自己的函数来检查缓冲区,但在文件结束时返回 0,在错误时返回 -1,在空行中返回 和 '\n'。如果您逐个字符地读取缓冲区,那么您可以开箱即用地为此目的使用标准函数;但这相当于检查您的缓冲区;-)。
  • 基本上,我想知道是否可以在不检查缓冲区的情况下从标准输入终止行尾。就像我从文件中读取一样,当 EOF 发生时,读取调用会自动终止。在这种情况下,无需检查缓冲区的内容。

标签: c input newline stdin


【解决方案1】:

你问:

当从标准输入输入数据时,一般用户在完成后按回车键。但是 read() 也将 '\n' 视为输入,在这种情况下 n = 1 并且条件不会评估为 false。

第一点当然是正确的。 enter 键等效于换行键,因此当用户按下 enter 时,键盘会生成一个换行符,因此 read() 函数会返回该字符。做到这一点至关重要。

因此,您的条件被误导了 - 空行将包含换行符,因此字节数将为 1。实际上,当标准输入是键盘时,只有一种方法可以让read() 调用返回 0,那就是键入“EOF”字符 - 通常在 Unix 上是 control-D,在 DOS 上是 control-Z。在 Unix 上,终端驱动程序将该字符解释为“即使还没有换行符,也将先前的输入数据发送到程序”。如果用户在该行没有输入任何其他内容,那么来自read() 的返回值将为零。如果输入来自文件,则在读取最后一个数据后,后续读取将返回 0 个字节。

如果输入来自管道,则在读取管道中的所有数据后,read() 调用将阻塞,直到最后一个可以写入管道的文件描述符关闭;如果该文件描述符在当前进程中,那么 read() 将永远挂起,即使挂起的进程将永远无法 write() 到文件描述符 - 当然假设是单线程进程。

【讨论】:

  • 很关键,您知道为什么 read()/recv() 的返回值 0 与空行不同。如果从 read()/recv() 中得到 0,则表示您处于所有输入的末尾(即已关闭,无法添加新输入)。
  • 注意:有些设备让read() 返回零并不意味着不能添加更多输入。展示我的古代 - 磁带设备就是其中之一。目前,终端是另一个。使用 open() 上的深奥选项(例如 O_NONBLOCK),您可以在其他设备上获得该行为。但是根据定义,空行是仅包含换行符的行 - 行尾标记(又名换行符)是数据的一部分。如果您不想在程序中使用它,则必须将其删除。只有 gets() 会为您删除换行符,但您应该永远在生产代码中使用它。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    您必须自己检查缓冲区。 例如

    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 
    }
    

    【讨论】:

      【解决方案4】:

      您最好将fgets() 用于您的任务(捕捉用户输入),但即使fgets() 在缓冲区中存储换行符。

      但是,如果有换行符,您可以确定它是字符串中的最后一个字符,因此很容易去除它。

      【讨论】:

      • 您不能确定缓冲区中的最后一个字符是换行符,因为 fgets() 只会在文件中有换行符时存储换行符。如果最后一行不包含一个,则可能不是。
      • @Inshalla 我的意思是如果换行符在缓冲区中,它肯定是最后一个字符。我将编辑我的帖子以使其明确。
      • 首先,我在这里使用文件描述符而不是文件指针。 fgets() 接受文件指针。而我想要做的是在用户按下回车时使条件评估为假。如果换行符是最后一个输入的字符,我也可以在这里确定。
      • 您说您正在从标准输入读取,所以无论您使用 read(0, ..) 还是 fread(..., stdin) 还是 fgets(..., stdin) .我的帖子是基于您在问题中提供的信息。如果你想在空行上停止,你可以像 "if ('\n' != *fgets(buf, sizeof(buf)-1, stdin))"
      • @qrdl,它当然也在检查缓冲区,但出于美学原因,它可能比其他解决方案更可取。
      【解决方案5】:

      我很肯定,如果不检查缓冲区内容,就无法做到这一点。甚至 readline() 也能做到这一点。你为什么反对呢?

      【讨论】:

        猜你喜欢
        • 2023-04-09
        • 2017-07-05
        • 2021-06-17
        • 2017-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-19
        • 2015-07-01
        相关资源
        最近更新 更多