42

我正在阅读 The C Programming Language 并且到目前为止已经理解了所有内容。但是,当我遇到getchar()and时putchar(),我无法理解它们的用途,更具体地说,是以下代码的作用。

main()
{
    int c;
    while ((c = getchar()) != EOF)
       putchar(c);
}

我理解main()函数、整数的声明cwhile循环。然而,我对while循环内部的条件感到困惑。此 C 代码中的输入是什么,输出是什么。

抱歉,如果这是一个基本而愚蠢的问题,但在我继续阅读本书并变得更加困惑之前,我只是在寻找一个简单的解释。

4

8 回答 8

37

这段代码可以更清楚地写成:

main()
{
    int c;
    while (1) {
        c = getchar();            // Get one character from the input
        if (c == EOF) { break; }  // Exit the loop if we receive EOF ("end of file")
        putchar(c);               // Put the character to the output
    }
}

EOF没有更多输入时接收字符。在从真实文件读取输入而不是用户输入(这是文件的特殊情况)的情况下,该名称更有意义。


[顺便说一句,通常该main函数应写为int main(void)。]

于 2012-05-23T13:23:22.147 回答
25

getchar()是一个从标准输入读取字符的函数。EOFC中使用的一个特殊字符,表示已到达END OF FILE 。

通常,当您的标准输入不是控制台(即文件)时,您会得到一个EOF字符返回。getchar()

如果你像这样在 unix 中运行你的程序:

$ cat somefile | ./your_program

然后,您将在结束getchar()时返回每个字符。somefileEOFsomefile

如果你像这样运行你的程序:

$ ./your_program

并通过控制台发送一个EOF(通过CTRL+D在 Unix 中点击或在 Windows 中按 CTRL+Z),然后getchar()也将返回EOF并且执行将结束。

于 2012-05-23T13:24:34.090 回答
7

用当前 C 标准编写的代码应该是

#include <stdio.h>

int main(void)
{
    int c;
    while ((c = getchar()) != EOF)
       putchar(c);
}

循环可以重写为

int c;
while (1) {
    c = getchar();
    if (c != EOF)
        putchar(c);
    else
        break;
}

这读作

  • 永远重复
    • 标准输入中获取输入的下一个字符(“字节”)并将其存储到c
    • 如果在读取所述字符时没有发生异常情况
      • 然后将存储的字符输出c标准输出中
    • 别的
      • 打破循环

许多编程语言通过引发破坏正常程序流程的异常来处理异常情况。C没有这样的事情。相反,可能失败的函数有一个返回值,任何异常情况都由一个特殊的返回值发出信号,您需要从给定函数的文档中检查该返回值。如果是getchar,来自 C11 标准的文档说(C11 7.21.7.6p3):

  1. getchar函数从 指向的输入流中返回下一个字符stdin。如果流处于文件末尾,则设置流的文件结束指示符并getchar返回EOF. 如果发生读取错误,则设置流的错误指示符并getchar返回EOF

在别处声明它EOF是一个小于 0 的整数常量,并且任何普通的返回值都是 >= 0 -unsigned char零扩展为int.

位于文件末尾的流意味着所有输入都已被使用。对于标准输入,可以通过在 Unix/Linux 终端上键入+和在 Windows 控制台窗口中键入Ctrl+来从键盘输入。另一种可能性是程序从文件或管道而不是从键盘接收输入 - 然后只要输入被完全消耗,就会发出文件结束信号,即DCtrlZ

cat file | ./myprogram

或者

./myprogram < file

正如上面的片段所说,实际上有两种不同的条件会导致getcharreturn EOF:到达文件末尾,或者发生了实际错误。这不能仅从返回值推断出来。相反,您必须使用函数feofferrorfeof(stdin)如果在标准输入上到达文件结尾,将返回一个真值。ferror(stdin)如果发生错误,将返回 true。

如果发生实际错误,则errno定义的变量<errno.h>将包含错误代码;该函数perror可用于自动显示带有前缀的人类可读错误消息。因此,我们可以将示例扩展为

#include <stdio.h>
#include <errno.h> // for the definition of errno
#include <stdlib.h> // for exit()
int main(void)
{
    int c;
    while ((c = getchar()) != EOF)
       putchar(c);

    if (feof(stdin)) {
        printf("end-of-file reached\n");
        exit(0);
    }
    else if (ferror(stdin)) {
        printf("An error occurred. errno set to %d\n", errno);
        perror("Human readable explanation");
        exit(1);
    }
    else {
        printf("This should never happen...\n");
        exit('?');
    }
}

要触发文件结尾,可以^D在 Linux 上的新行上使用 Ctrl+D(此处显示为 ):

% ./a.out
Hello world
Hello world
^D
end-of-file reached

(注意这里的输入是如何被行缓冲的,所以输入不会与输出交错在行内)。

同样,我们可以通过使用管道来获得相同的效果。

% echo Hello world | ./a.out
Hello world
end-of-file reached

触发错误有点棘手。在bashzshshell 中,标准输入可以关闭,这样它就不会来自任何地方,方法是附加<&-到命令行:

% ./a.out <&-
An error occurred. errno set to 9
Human readable explanation: Bad file descriptor

Bad file descriptor,或EBADF表示标准输入- 文件描述符编号 0 无效,因为它根本没有打开。

另一种产生错误的有趣方法是从目录中读取标准输入——这会导致EISDIR在 Linux 上将 errno 设置为:

% ./a.out < / 
An error occurred. errno set to 21
Human readable explanation: Is a directory

putchar实际上,也应该检查的返回值- 它同样会EOF在错误或写入的字符时返回:

while ((c = getchar()) != EOF) {
    if (putchar(c) == EOF) {
        perror("putchar failed");
        exit(1);
    }
}

现在我们可以通过将标准输出重定向到/dev/full- 但是有一个陷阱 - 因为标准输出是缓冲的,我们需要写入足够的内容以使缓冲区立即刷新,而不是在程序结束时刷新。我们从 得到无限的零字节/dev/zero

 % ./a.out < /dev/zero > /dev/full
 putchar failed: No space left on device

PS始终使用类型的变量int来存储的返回值非常重要getchar()。即使它读取一个字符使用signed// unsignedplainchar总是错误的。

于 2017-08-08T22:39:12.830 回答
4

也许您对在命令行中输入 -1 并不会结束您的程序这一事实感到困惑?因为getchar()将其读取为两个字符,- 和 1。在对 c 的赋值中,该字符被转换为 ASCII 数值。这个数值存储在某个内存位置,由 c 访问。

然后putchar(c)检索该值,查找 ASCII 表并转换回字符,然后打印。

我猜想在 ASCII 表中找到十进制值 -1 是不可能的,因为该表从 0 开始。因此getchar()必须考虑不同平台上的不同解决方案。也许getchar()每个平台都有一个版本?

我只是觉得奇怪的是这个 EOF 不在常规的 ascii 中。它可能是第一个不可打印的字符。例如,行尾是 ASCII。

如果将文件从 windows 传输到 linux 会发生什么?EOF 文件字符会自动更新吗?

于 2012-10-21T11:37:15.280 回答
4

getchar() function reads a character from the keyboard (ie, stdin)

In the condition inside the given while loop, getchar() is called before each iteration and the received value is assigned to the integer c.

Now, it must be understood that in C, the standard input (stdin) is like a file. ie, the input is buffered. Input will stay in the buffer till it is actually consumed. stdin is actually the standard input stream.

getchar() returns the the next available value in the input buffer.

The program essentially displays whatever that was read from the keyboard; including white space like \n (newline), space, etc.

ie, the input is the input that the user provides via the keyboard (stdin usually means keyboard). And the output is whatever we provide as input.

The input that we provide is read character by character & treated as characters even if we give them as numbers.

getchar() will return EOF only if the end of file is reached. The ‘file’ that we are concerned with here is the stdin itself (standard input).

Imagine a file existing where the input that we provide via keyboard is being stored. That’s stdin. This ‘file’ is like an infinite file. So no EOF.

If we provide more input than that getchar() can handle at a time (before giving it as input by pressing enter), the extra values will still be stored in the input buffer unconsumed. The getchar() will read the first character from the input, store it in c and printcwithputchar(c)`.

During the next iteration of the while loop, the extra characters given during the previous iteration which are still in stdin are taken during while ((c = getchar()) != EOF) with the c=getchar() part. Now the same process is repeated till there is nothing left in the input buffer.

This makes it look as if putchar() is returning a string instead of a single character at a time if more than one character is given as input during an iteration.

Eg: if input was
abcdefghijkl
the output would’ve been the same
abcdefghijkl

If you don’t want this behaviour, you can add fflush(stdin); right after the putchar(c);. This will cause the loop to print only the first character in the input provided during each iteration.

Eg: if input was
adgbad
only a will be printed.

The input is sent to stdin only after you press enter.

putchar() is the opposite of getchar(). It writes the output to the standard output stream (stdout, usually the monitor).

EOF is not a character present in the file. It’s something returned by the function as an error code.

You probably won’t be able to exit from the give while loop normally though. The input buffer will emptied (for displaying to the output) as soon as something comes into it via keyboard and the stdin won't give EOF.

For manually exiting the loop, EOF can be sent using keyboard by pressing ctrl+D in Linux and
ctrl+Z in Windows

eg:

while ((c = getchar()) != EOF)
{

   putchar(c);
   fflush(stdin);
}
printf("\nGot past!");

If you press the key combination to give EOF, the message Got past! will be displayed before exiting the program.

If stdin is not already empty, you will have to press this key combination twice. Once to clear this buffer and then to simuate EOF.

EDIT: The extra pair of parenthesis around c = getchar() in while ((c = getchar()) != EOF) is to make sure that the value returned by getchar() is first assigned to c before that value is compared with EOF.

If this extra parenthesis were not there, the expression would effectively have been while (c = (getchar() != EOF) ) which would've meant that c could have either of 2 values: 1 (for true) or 0 (for false) which is obviously not what is intended.

于 2017-08-08T14:07:44.197 回答
1
 getchar()

从输入中获取一个字符。

 c = getchar()

这个赋值的值是赋值后左边的值,或者是被读取的字符的值。EOF默认值为-1

 ((c = getchar()) != EOF)

只要值保持不变,EOF或者换句话说,只要条件保持为真,循环就会继续迭代。一旦该值变为EOF整个条件的值,0它将打破循环。

周围的附加括号c = getchar()用于编译器,以强调我们确实想在条件内进行赋值,因为它通常假定您要键入==并警告您。

 main() {
     int c;
     while ((c = getchar()) != EOF)
         putchar(c);
 }

所以整个代码实际上回显了您输入的内容。它将字符的值分配给c条件内部,然后将其输出回循环体,仅在检测到文件结尾时结束。

于 2016-12-29T19:12:21.923 回答
0

以与 | 类似的方式 上面的管道命令你可以在你的系统上使用重定向来利用上面的代码来显示文件的所有字符内容,直到它到达通常由 CTRL-Z 或 CTRL-D 表示的结尾(EOF)。

在控制台中: ProgramName < FileName1.txt

要创建从 FileName1 读取的内容的副本,您可以: ProgramName < FileName1.txt > CopyOfInput.txt

这以多种方式演示了您的程序,希望有助于您的理解。

- 希望有帮助。

于 2012-05-24T06:02:18.707 回答
0
main(){
int c;
while ((c = getchar()) != EOF)
   putchar(c);
}

实际上 c=getchar() 提供了用户在控制台上输入的字符,并且该值使用代表 End Of File 的 EOF 检查。在文件的最后遇到 EOF。(c = getchar()) != EOF 等价于 c != EOF 。现在我认为这要容易得多。如果您有任何进一步的疑问,请告诉我。

于 2016-06-19T14:25:05.870 回答