为什么getchar()不能读取退格等字符?

8

这是一个非常基础的C语言问题,来自Kernighan和Ritchie的第18页。

我已经编译了这个非常简单的代码来计算从键盘输入的字符:

#include <stdio.h>

/* count characters in input; 1st version */
main()
{
  long nc;

  nc = 0;
  while (getchar() != EOF)
    ++nc;
  printf("%1d\n", nc);
}

这段代码可以顺利编译运行,并且基本符合预期。例如,当我输入 "Hello World" 时,在按下 CTRLD 输入EOF字符后,它返回值为11。
然而,如果我犯了错误,使用退格键删除字符并重新输入,那么它只返回终端显示的字符数量,在调用EOF时。
如果代码包括特殊字符在内计算每个字符,那么如果我输入四个字符,删除两个,然后再输入两个,那应该输出8个字符(4个字符+2个删除+2个字符),而不是4个?
我显然误解了C如何处理退格键以及代码何时增加变量“nc”的计数。

4
编辑由终端应用处理,因此 getchar 不会读取删除操作。 - Some programmer dude
注意:根据标准,应该使用 int main(void) 而不是 main() - Spikatrix
@Olaf Main可以根据C99跳过返回。在C90中不允许这样做。K&R尝试遵循C90的第二版,但经常失败。我已经写了一个包含参考文献的main()所有有效形式的摘要,在这里 - Lundin
@Lundin 好的,我误解了你的评论。无论如何,我认为我们都同意,非原型签名和省略 return 都是不好的风格,应该避免使用。 - too honest for this site
1
@Olaf 是的,这就是为什么不应该阅读《C程序设计语言(K&R)》。事实证明,编程世界中最有名的例子main() { printf("hello, world\n"); }是错误的...... 在C90中会引发未定义行为(没有返回类型),而在后续版本中甚至无法编译(不允许隐式int)。 - Lundin
显示剩余8条评论
3个回答

7
通常情况下,你的终端会话运行在“行模式”下,也就是说,只有当一行数据完整输入(例如,按下回车键等)时,才会将数据传递给你的程序。因此,你只能看到完整的一行(在你的程序接收到任何内容之前已经进行了任何编辑)。通常这是一个好事情,这样每个程序都不需要处理删除等操作。
在大多数系统上(例如基于Unix的系统等),可以将终端设置为“原始”模式——也就是说,每个字符被接收后立即传递给程序。例如,面向屏幕的文本编辑器通常会这样做。

6

getchar() 并不是不计算“删除”,而是在输入被终端驱动程序传递到你的程序之前,它甚至看不到输入。

当你输入一些内容时,它并不会立即到达你的 C 程序,直到你按下 \n 或发送 EOF(或 EOL)。这就是 POSIX 定义的规范模式输入处理 - 通常是默认模式。


好的,我明白了。所以这更多是我对终端工作方式的误解,而不是 C 代码本身的问题。 - Philip King

2

退格符通常用于在cooked tty模式下编辑输入(请参见BSD中的canonical input mode的tty(4)和Linux系统中的termios(3)),因此它们被tty驱动程序消耗,并且不会到达进程在那之后获得的输入。对于Ctrl-D作为文件结束或Ctrl-K作为删除输入字符,情况也是如此。驱动程序在幕后执行了几件事情,您的进程最终无法获得这些事情。这些指令旨在使用户和程序员的生活更轻松,因为您通常不希望在生活中看到已擦除的输入(这就是擦除它的原因),或者希望行结束符为\n而不是当您按[RETURN]键时tty通常生成的\r。但是,如果您从具有退格符的文件中读取,您将像正常输入一样获得它们,只需创建一个带有退格符的文件并尝试从中重定向输入,您将在输入中看到这些字符。

顺便说一句,如果您想在终端生成退格符,请在每个退格符前加上Ctrl-V字符(这也由tty驱动程序管理,当从文件中读取时不会发生),您将在文件中看到您的退格符作为正常输入(要发送Ctrl-V,只需将其重复两次)。


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