在 C 语言中将 EOF 读取为字符

Reading EOF as a char in C

提问人:drain_ 提问时间:9/14/2022 最后编辑:Rachid K.drain_ 更新时间:9/19/2022 访问量:525

问:

我知道这听起来可能很愚蠢,但我怎样才能退出这个循环?

#include <stdio.h>
#include <stdlib.h>

int main(){
    char c;
    do {
        c = getchar();
        printf("%c", c);
    }while(c != EOF);

    return 0;
}

我正在读一本书,并尝试做以下练习: “验证表达式 getchar() != EOF 是否为 0 或 1” 如果我读取存储在整数值中的EOF值,它将等于-1,但是如果我试图将-1捕获为char,那就太他妈的了。据我了解,EOF 是一个未分配给任何其他字符的值。

有人可以帮忙吗?

编辑1: 我知道 c 应该是一个整数......我故意把它读成一个字符。

编辑2:

int main(){
    int c;
    while((c = getchar()) != EOF)
    {
        printf("%d\n", c);
    }
    return 0;
}

----->

int main(){
    int c;
    int i = 0;
    char str[2];
    while((c = getchar()) != EOF)
    {
        str[i] = c;
        ++i;
        if(i > 1) i = 0;
        if(str[0]=='-'&&str[1]=='1')
        {
            c = EOF; // doens't exit loop
        }
        else printf("%d\n", c);


    }
    return 0;
}

为什么我不明白这一点。

c eof getchar

评论

11赞 Ted Lyngmo 9/14/2022
getchar()返回 因此,您应该更改为 。在打印之前检查它是否不是也是一个好主意。 -或intchar cint cEOFint c; while((c = getchar()) != EOF) { printf("%c", c); }for(int c; (c = getchar()) != EOF;) { printf("%c", c); }
7赞 Steve Summit 9/14/2022
是的,是一个不等于任何字符的值。因此,根据定义,它不会“适合”类型的变量。如果你试图把它塞进一个大小的插槽(就像一个变量),一些位会被刮掉,它会(错误地)与某个字符值进行比较——也许是ÿ——而重点是它不能与任何字符值进行比较。EOFcharcharchar c;
2赞 Weather Vane 9/14/2022
初学者的陷阱是认为“字符”意味着“数字”意味着整数。请注意,可以传递 to 格式规范(for ,但不能传递指针 )。当您将 (传递给可变参数函数) 时,无论如何都会将其提升。charint%cprintfchar*scanfcharint
2赞 Eric Postpischil 9/14/2022
@SteveSummit:C标准不要求具有与任何值不同的值。这是一个好主意,但不是必需的。C 实现可以支持没有“字符”的文本文件具有与 EOF 相同值的代码(例如,255 个支持的字符,包括 null 终止符),而二进制文件则与不需要的函数一起使用。几年前,在 Stack Overflow 上就讨论过 C 实现是否符合标准,这是其中的一部分。EOFcharEOFsizeof (int) == sizeof (char)
2赞 Eric Postpischil 9/14/2022
@SteveSummit:实际上,在常见的 C 实现中,EOF 确实有一个 char。回想一下,以 或 字符值的形式返回 ,而不是 .getcharEOFunsigned charchar

答:

5赞 0___________ 9/14/2022 #1
  1. c一定不是.这在未签名的实现中尤为重要。 由 0xffffffff(32 位整数,二进制补码)表示,并作为0xff分配给 char。相比之下0xff总是不相等的。这就是为什么你应该使用 not .intcharchar-1-1intchar
  2. 打印前测试EOF。更适合的是循环while(...) {}
int main(){
    int c;
    while((c = getchar()) != EOF)
    {
        printf("%c", c);
    }
}

https://godbolt.org/z/6sqa98bnq

这里有一个示例,如果 is 和 is unsigns,会发生什么:https://godbolt.org/z/bjfGv56z3ccharchar

enter image description here

评论

0赞 drain_ 9/14/2022
这段代码做的与另一个带有 char 的代码完全相同......即使您插入 -1 -->程序也不会退出。
0赞 0___________ 9/14/2022
@drain_没有 godbolt.org/z/WjWxYvs3T。这两者之间有一个非常重要的区别。
6赞 Steve Summit 9/14/2022 #2

如果您像这样更改程序,它可能会帮助您了解发生了什么:

#include <stdio.h>

int main(){
    int c;
    do {
        c = getchar();
        printf("%d\n", c);
    } while(c != EOF);
}

你会注意到我有:

  1. 声明为cint
  2. 打印它%d

如果我运行这个程序并键入“abc”,然后点击然后,这就是我看到的:EnterCTRL-D

97
98
99
10
-1

97、98 和 99 是 、 和 的 ASCII 代码。10 是换行符的代码,又名 .然后 -1 是我键入时生成的 EOF。(如果您使用的是 Windows,则会改用 And Another 代替。abc\nCTRL-DCTRL-ZEnter

在这个程序中,虽然是一个变量,但这并不意味着它不包含字符!在 C 中,字符由小整数表示,这些整数是它们在机器字符集中的代码。下面是演示这一点的修改:cint

int c;
int nch = 0;
char string[100];
do {
    c = getchar();
    printf("%d", c)
    if(c >= 32 && c < 127) {
        printf(" = '%c'", c);
        string[nch++] = c;
    }
    printf("\n");
} while(c != EOF);
string[nch] = '\0';
printf("You typed \"%s\"\n", string);

现在它打印

97 = 'a'
98 = 'b'
99 = 'c'
10
-1
You typed "abc"

打电话没有问题

printf(" = '%c'", c);

即使是一个 int 并且用于打印字符。
分配没有问题
c%c

string[nch++] = c;

即使是一个 int 并且是字符数组。cstring

评论

0赞 drain_ 9/14/2022
我有没有机会添加 -1 来表示像“-1”这样的 2 个字符序列,这样我就可以通过连续输入 2 个字符来退出循环?或者是否有可能以某种方式用新值“重载”EOF 值。
0赞 Ted Lyngmo 9/14/2022
@drain_ 你开始不清楚你真正想做什么。为什么不像其他人一样使用它呢?
1赞 Steve Summit 9/14/2022
@TedLyngmo 对不起,在充实答案时丢失了您花哨的键帽。随意重新插入。
1赞 Steve Summit 9/14/2022
@drain_ 是的,您可以编写代码以退出特定两个字符组合的循环。这将是不寻常的,并且(在我看来)不是特别有用。 是一种非常通用的机制,当程序的输入被重定向到从文件中读取时,它将正常工作(这意味着没有人键入 control-D 或 control-Z)。EOF
1赞 Steve Summit 9/14/2022
@drain_ ASCII 中字符 capital-A 的值为 65。我希望你不要想象控制 D 的值是 -1。control-D(或 Windows 上的 control-Z)最终映射到 EOF 的方式相当复杂。我可能有时间稍后解释。我真的想知道为什么你想使用除 control-D/control-Z 以外的东西作为键盘文件末尾。真的,其他人都觉得它们已经足够了。getchar
4赞 chux - Reinstate Monica 9/14/2022 #3

getchar()返回 [ ... ] 范围内的 an 或 。int0UCHAR_MAXEOF

要很好地区分这些典型的 257 个不同值,请保存在 .int

如果保存在有符号的 a 中,则保存典型值为 -1 的字符,但某些字符也会保存,可能值为 255。然后,循环在以下 2 种条件之一下结束。charEOF

如果保存在无符号中,则典型值为 -1 的 将保存为 255,并且永远不会等同于导致无限循环。charEOFEOF

做正确的事。保存在 .int

打印前进行比较,否则返回时,打印结果可能与读取带有 255 的字符相同。EOF

// char c;
int c;
while ((c = getchar()) != EOF)) {
    printf("%c", c);
}

据我了解,EOF 是一个未分配给任何其他字符的值

这并不完全正确。 为负数,字符(不是字符)与值一起读取。EOFunsigned char

EOF可以是 -1,a 在有符号时也可以具有 -1 的值。关键是最好将章程最初视为无符号值,即使出人意料地保存在有符号的 .charchar

更深层次:这是一个旧的 C 历史妥协,有签名或未签名。不过,字符处理最好按条件进行,字符被视为 。这会影响 、 和其他函数。charunsigned chargetchar()is...()strcmp()

2赞 Vlad from Moscow 9/14/2022 #4

根据 C 标准(7.21 输入/输出 <stdio.h>)

EOF

它扩展为一个整数常量表达式,类型为 int 和 负值,由多个函数返回以指示 文件末尾,即不再有来自流的输入;

正如你所看到的,没有说常量表达式有什么负值。

但是,通常它等于 -1。

do-while 语句的条件

}while(c != EOF);

将正确计算为 以及类型是否表现为类型 。在这种情况下,由于整数升级,类型的对象将隐式转换为条件中的类型。也就是说,存储在该类型的对象中的整数值将提升为该类型并保留其值。10charsigned charccharint-1ccharint-1

但是,根据编译器选项,该类型可以表现为类型 。在这种情况下,在此分配之后charunsigned char

c = getchar()

该变量将具有一个无符号值。例如,如果它被赋值为 that is equalto,则该变量将具有在整数提升后将保留的正值。 因此,条件cEOF-1c255

}while(c != EOF);

将始终计算为 因为不等于 。1-1255

因此,您应该始终将变量声明为具有 .在这种情况下,您的代码将不依赖于类型的行为方式:as 或 as 。cintcharsigned charunsigned char

2赞 Steve Summit 9/14/2022 #5

在您问题的附录中,您试图在用户键入“-1”时退出循环,您想知道为什么这不起作用。你犯了一两个错误。这是一个更正后的版本,您可以尝试:

int c;
int i = 0;
char str[20];
while((c = getchar()) != EOF)
{
    str[i] = c;
    ++i;
    if(str[0]=='-' && str[1]=='1')
    {
        break;        /* fix here: was "c = EOF" */
    }
    else printf("%d\n", c);
}

您的代码实际上确实识别了“-1”,但在响应中它设置为 ,但这什么也没做,因为接下来发生的事情是对循环顶部标头的另一次调用。cEOFc = getchar()while

在修改后的版本中,我在看到“-1”时调用,这有效。break

请注意,若要退出循环,必须键入两个字符“-”和“1”,并且必须在行的开头键入它们。

请注意,您在此处阅读的是字符。两个字符“-”和“1”被视为整数 -1 或 C 值。他们只是两个角色。如果将语句更改为EOFif

if(str[0]=='x' && str[1]=='y')

你最终会得到一个循环,当用户在一行的开头输入“xy”时,这个循环就停止了。

评论

0赞 drain_ 9/14/2022
我确实明白这一点......我知道这种情况意味着什么......我想说的是:系统如何知道我已经结束了一个文件?如果值为 -1,为什么它不起作用,以及为什么 EOF 可以有更多值,但系统知道发生了什么......读点东西也没问题..我需要理解这一点
1赞 Steve Summit 9/14/2022
@drain_ 您使用的是 Unix、Linux、MacOS、Windows 还是其他东西?
1赞 Steve Summit 9/14/2022
我问只是因为我厌倦了一直说“控制 D(或 Windows 上的控制 Z)”。现在,我假设你使用的是类 Unix 操作系统。
1赞 Steve Summit 9/14/2022
当您调用时,它会为您提供缓冲区中的下一个字符。如果缓冲区为空,stdio 代码会尝试重新填充它,通常通过调用 read 来填充它。如果返回 <= 0,则表示文件结束或错误,因此返回非字符值 EOF。getcharstdinreadgetchar
1赞 Steve Summit 9/14/2022
那么是什么原因导致返回 0 呢?如果从文件中读取,则当文件中没有更多字符时返回 0。如果从键盘读取,则在用户键入 control-D 时返回 0。终端驱动程序中有特殊代码来处理此问题。通常,如果键入某些字符,这些字符将在 的缓冲区中返回,并返回大于 0 的值。但是,如果您键入一个特殊的击键控件 D,则返回 0。(实际上它比这更复杂,但这给出了基本的想法。readreadreadreadreadread