我想了解getchar()!= EOF

时间:2012-05-23 13:21:03

标签: c eof getchar putchar

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

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

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

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

8 个答案:

答案 0 :(得分:28)

此代码可以更清晰地编写为:

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)。]

答案 1 :(得分:15)

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

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

如果您在unix中运行程序,请执行以下操作:

$ cat somefile | ./your_program

然后,只要getchar()结束,您的somefile就会返回EOFsomefile中的每个字符。

如果你运行这样的程序:

$ ./your_program

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

答案 2 :(得分:4)

也许你对在命令行输入-1不会结束程序这一事实感到困惑?因为getchar()将此读取为两个字符, - 和1.在赋值给c时,字符将转换为ASCII数字值。此数值存储在某个内存位置,由c。

访问

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

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

我觉得很奇怪,这个EOF不在常规的ascii中。它可能是第一个不可打印的字符之一。例如,End-of-line是ASCII格式。

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

答案 3 :(得分:3)

getchar()函数从键盘读取一个字符(即stdin

在给定while循环内的条件中,在每次迭代之前调用getchar(),并将接收的值分配给整数c

现在,必须要理解的是,在C中,标准输入(stdin)是like个文件。即,输入被缓冲。输入将保留在缓冲区中,直到实际消耗为止。 stdin实际上是standard input stream

getchar()返回输入缓冲区中的下一个可用值。

程序基本上显示从键盘读取的内容;包括像\n(换行符),空格等空格

即,输入是用户通过键盘提供的输入(stdin通常表示键盘)。 输出是我们提供的任何输入。

我们提供的输入是按字符和字符读取的。即使我们将它们作为数字,它们也被视为字符。

仅当到达文件末尾时,

getchar()才会返回EOF。我们在这里关注的'文件'是stdin本身(标准输入)。

想象一下存在我们通过键盘输入的输入存在的文件。那是stdin。 这个'文件'就像infinite file。所以没有EOF

如果我们提供的输入数量超过getchar()一次可以处理的数量(在按Enter键输入之前),则额外值仍将存储在未使用的输入缓冲区中。 getchar()将读取输入中的第一个字符,将其存储在c and print c with putchar(c)中。

while循环的下一次迭代期间,在stdin期间,在while ((c = getchar()) != EOF)期间使用c=getchar()部分获取在上一次迭代期间给出的额外字符。 现在重复相同的过程直到输入缓冲区中没有任何内容。

如果在迭代期间给出多个字符作为输入,那么看起来好像putchar()一次返回一个字符串而不是一个字符。

例如:如果输入是
abcdefghijkl
输出将是相同的 abcdefghijkl

如果您不想要此行为,可以在putchar(c);之后立即添加fflush(stdin);。 这将导致循环仅打印每次迭代期间提供的输入中的第一个字符。

例如:如果输入是
adgbad
只会打印a

只有在按Enter键后才会将输入发送到stdin

putchar()getchar()相反。它将输出写入标准输出流(stdout,通常是监视器)。

EOF不是文件中的字符。它是由函数返回的错误代码。

但是,您可能无法正常退出给定while循环。输入缓冲区将在通过键盘输入时立即清空(用于​​显示输出),而stdin将不会提供EOF

要手动退出循环,可以通过按键使用键盘发送EOF Linux中的 ctrl + D Windows中的 ctrl + Z

例如:

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

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

如果您按下组合键以提供EOF,则会在退出程序之前显示消息Got past!

如果stdin 已经为空,则必须按两次此组合键。一旦清除此缓冲区,然后模拟EOF

编辑:c = getchar()while ((c = getchar()) != EOF)周围的额外一对括号是为了确保{i}返回的值在之前首先分配给getchar() 将值与c进行比较。

如果不存在这个额外的括号,那么表达式实际上是EOF,这意味着while (c = (getchar() != EOF) )可能具有2个值之一:c(对于true)或1(假的)显然不是预期的。

答案 4 :(得分:3)

使用当前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;
}

读为

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

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

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

在其他地方陈述EOF是一个整数常数&lt; 0,任何普通的返回值都是> = 0 - unsigned char零扩展到int

位于文件结尾的流意味着已经消耗了所有输入。对于标准输入,可以通过在Unix / Linux终端上键入 Ctrl + D 并且 Ctrl + Z <来从键盘导致此操作/ kbd>在Windows控制台窗口中。另一种可能性是程序从文件或管道而不是键盘接收输入 - 然后只要输入被完全消耗就会发出文件结尾信号,即

cat file | ./myprogram

./myprogram < file

正如上面的片段所说,实际上有两种不同的条件会导致getchar返回EOF文件结尾已到达,发生了实际错误。这不能仅从返回值中推断出来。相反,您必须使用函数feofferror。如果在标准输入上达到文件结尾,feof(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('?');
    }
}

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

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

(注意输入在这里是如何进行行缓冲的,因此输入不会在输出行中交错)。

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

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

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

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

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

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

% ./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

P.S。始终使用 int 类型的变量来存储getchar()的返回值非常重要。即使它读取字符using signed/unsigned/plain char is always wrong

答案 5 :(得分:0)

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

在控制台中: ProgramName < FileName1.txt

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

这可以通过多种方式展示您的计划,以帮助您理解。

- 希望有所帮助。

答案 6 :(得分:0)

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

实际上c = getchar()提供了用户在控制台上输入的字符,并使用表示文件结尾的EOF检查该值。在文件的最后遇到EOF。 (c = getchar())!= EOF相当于c!= EOF。现在我认为这更容易。如果您有任何进一步的询问,请告诉我。

答案 7 :(得分:0)

 getchar()

从输入中获取一个字符。

 c = getchar()

此赋值的值是赋值后左侧的值,或者已读取的字符的值。 EOF的值默认为-1

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

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

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

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

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