我正在尝试理解getchar()!= EOF

dced5bon  于 2023-06-28  发布在  其他
关注(0)|答案(9)|浏览(105)

我正在阅读C编程语言,到目前为止已经理解了一切。然而,当我遇到getchar()putchar()时,我无法理解它们的用途,更具体地说,下面的代码是做什么的。

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

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

bqucvtff

bqucvtff1#

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

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字符在没有更多输入时被接收。在从真实的文件读取输入而不是用户输入(这是文件的特殊情况)的情况下,该名称更有意义。
[As顺便说一句,通常main函数应该写为int main(void)

cig3rfwq

cig3rfwq2#

getchar()是从标准输入读取字符的函数。EOF是一个特殊字符,在C中用于声明已到达END OF FILE
通常,当您的标准输入不是控制台(即文件)时,您将从getchar()返回EOF字符。
如果你在unix中运行你的程序,像这样:

$ cat somefile | ./your_program

然后,getchar()将在somefile结束时返回somefileEOF中的每个字符。
如果你像这样运行你的程序:

$ ./your_program

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

t8e9dugd

t8e9dugd3#

使用当前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中获取输入的下一个字符(“byte”)并将其存储到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终端上键入Ctrl+D和在Windows控制台窗口中键入Ctrl+Z来从键盘上实现此操作。另一种可能性是程序从文件或管道接收输入,而不是从键盘接收输入,然后每当该输入被完全消耗时,文件结束将被发信号通知,即
cat file | ./myprogram

./myprogram < file

正如上面的片段所说,实际上有两种不同的条件可以导致getchar返回EOF:到达 * 文件结束 或 * 发生实际错误。这不能仅从返回值推导出来。相反,您必须使用函数feofferrorfeof(stdin)将返回一个true值,如果在标准输入上达到文件结束。ferror(stdin)如果发生错误,将返回true。
如果实际发生错误,<errno.h>定义的变量errno将包含错误代码;函数X1 M20 N1 X可用于自动显示具有前缀的人类可读错误消息。因此,我们可以将示例扩展到

#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

(注意这里的 input 是如何行缓冲的,所以输入不会在带有输出的行中交错)。
同样,我们也可以通过使用管道获得同样的效果。

% 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

u0njafvf

u0njafvf4#

getchar()函数从键盘读取字符(即stdin
在给定的while循环中,getchar()在每次迭代之前被调用,并且接收到的值被分配给整数c
现在,必须理解,在C中,标准输入(stdin)是like文件。即输入被缓冲。输入将保留在缓冲区中,直到实际使用。stdin实际上是standard input stream
getchar()返回输入缓冲区中的下一个可用值。
该程序基本上显示从键盘上读取的任何内容;包括白色,如\n(换行符)、空格等。
即输入是用户通过键盘提供的输入(stdin通常指键盘)。而输出就是我们提供的任何输入。
我们提供的输入是一个字符接一个字符地读取的&即使我们以数字的形式给予它们,也会被当作字符来处理。
getchar()将返回EOF只有当到达文件的结尾。我们在这里关注的“文件”是stdin本身(标准输入)。
想象一个文件存在于我们通过键盘提供的输入被存储的地方。就是stdin这个“文件”就像一个infinite file。所以没有EOF
如果我们提供的输入比getchar()一次可以处理的要多(在按下回车键将其作为输入之前),额外的值仍然会存储在输入缓冲区中。getchar()将从输入中读取第一个字符,将其存储在c and print c with putchar(c)中。 在while循环的下一次迭代中,上一次迭代中给出的仍在stdin中的额外字符将在while ((c = getchar()) != EOF)期间使用c=getchar()部分。现在,重复相同的过程,直到输入缓冲区中没有任何剩余。 这使得putchar()看起来像是在一次迭代中如果输入了多个字符,则一次返回一个字符串而不是单个字符。 例如:如果输入为abcdefghijkl输出也是一样的abcdefghijkl如果你不想要这种行为,你可以在putchar(c);之后添加[fflush(stdin);](https://stackoverflow.com/questions/18170410/what-is-the-use-of-fflushstdin-in-c-programming)。这将导致循环在每次迭代期间仅打印输入中的第一个字符。 例如:如果输入为adgbad将只打印a。 只有在按下回车键后,输入才会发送到stdin。 [putchar()](https://en.wikibooks.org/wiki/C_Programming/stdio.h/getchar)与getchar()相反。它将输出写入标准输出流(stdout,通常是监视器)。EOF不是文件中的字符。它是函数返回的错误代码。 但是,您可能无法正常退出给予while循环。输入缓冲区将清空(显示到输出),只要有东西进入它通过键盘和stdin不会给予EOF。 对于手动退出循环,可以使用键盘通过在Linux中按ctrl+D发送EOF`,并且
Windows中的Ctrl+Z
例如:

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

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

如果您按组合键给予EOF,退出程序前将显示消息Got past!
如果stdinnot已经为空,则必须按两次此组合键。一次清除此缓冲区,然后模拟EOF
编辑:while ((c = getchar()) != EOF)c = getchar()周围的额外一对括号是为了确保在that值与EOF进行比较之前,先将getchar()返回的值分配给c
如果没有这个额外的括号,表达式实际上是while (c = (getchar() != EOF) ),这意味着c可以有两个值之一:1(表示真)或0(表示假),这显然不是预期的结果。

qlzsbp2j

qlzsbp2j5#

也许你对在命令行中输入-1并不能结束程序这一事实感到困惑。因为getchar()将其读取为两个字符-和1。在对c的赋值中,字符被转换为ASCII数值。这个数值存储在某个内存位置,由c访问。
然后putchar(c)检索这个值,查找ASCII表并转换回字符,然后打印出来。
我想在ASCII表中找到十进制的-1值是不可能的,因为表从0开始。因此getchar()必须考虑不同平台上的不同解决方案。也许每个平台都有一个getchar()版本?
我只是觉得奇怪的是,这个EOF不是在常规的ascii。它可能是第一个字符之一,这是不可打印的。例如,End-of-line是ASCII。
如果你把你的文件从windows转移到linux会发生什么?EOF文件字符会自动更新吗?

ztyzrc3y

ztyzrc3y6#

getchar()

从输入中获取字符。

c = getchar()

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

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

只要该值不是EOF,或者换句话说,只要条件为真,循环就会继续迭代。一旦值变成EOF,整个条件的值将是0,它将打破循环。
c = getchar()周围的括号是为编译器准备的,以强调我们确实希望在条件内进行赋值,因为它通常假设您希望键入==并警告您。

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

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

xfyts7mz

xfyts7mz7#

以类似于|pipe命令,你可以在你的系统上使用重定向,利用上面的代码来显示文件的所有字符内容,直到它到达通常由CTRL-Z或CTRL-D表示的结尾(EOF)。
在控制台:ProgramName < FileName1.txt
要创建从FileName 1读取的内容的副本,您可以:ProgramName < FileName1.txt > CopyOfInput.txt
这以多种方式演示了您的程序,希望有助于您的理解。

  • 希望能帮上忙
j7dteeu8

j7dteeu88#

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

实际上,c=getchar()提供了用户在控制台上输入的字符,该值用EOF检查,EOF表示文件结束。文件的最后一个遇到EOF。(c = getchar())!= EOF等价于c!现在我觉得这就容易多了。如果你有任何进一步的疑问让我知道。

ovfsdjhp

ovfsdjhp9#

为什么还没有人说int getchar(void)在每次调用时返回下一个输入字符,或者在遇到文件结尾时返回EOF?这是事实,但它不是出了问题!

相关问题