不知道如何正确使用getchar函数/C

Don't know how to use getchar function correctly / C

我想创建一个在控制台中输入字符的函数,但问题是有时当我输入 'a' 它被认为是一个空字符,程序要求我重新输入一个字符. 这是函数:

char readChar()
  {
    char character;
    character = getchar();
    character = toupper(character);
    while(getchar() != '\n' && getchar() != '[=10=]');
    return character;
  }

显然,我忘记处理存储在缓冲区中的换行符(如果我错了请纠正我)。

char readChar()
{
    char character;
    scanf(" %c", &character);
    getchar(); //I just added this line
    character = toupper(character);
    return character;
}

将一连串的评论转化为答案。

. Note that getchar() returns an int,不是 char。并且您的循环需要考虑 EOF 而不是空字节,尽管检测空字节可能没问题。

我对问题的最佳猜测是,有时您会执行 scanf("%d", &i) 或类似的操作,然后调用此函数 — 但 scanf() doesn't read the newline, so your function reads a newline left over by previous I/O operations. But without an MCVE (Minimal, Complete, and Verifiable Example),我们无法证明我的假设是准确的。

。此外,您的 'eat the rest of the line' 循环应该在每次迭代时只调用一次 getchar();你叫它两次。一种选择是使用:

int readChar(void)
{
    int c;
    while ((c = getchar()) != EOF && isspace(c))
        ;
    if (c == EOF)
        return EOF;
    int junk;
    while ((junk = getchar()) != '\n' && junk != EOF /* && junk != '[=10=]' */)
        ;
    return toupper(c);
}

这会吃掉白色的 space 直到它得到一个非白色的 space 字符,然后读取任何垃圾字符直到下一个换行符。它会解决我的假设场景。当心 EOF — 始终考虑 EOF。

基于阅读关于 scanf() 不阅读换行符的问答,Voltini 提出了一个修复:

char readChar()
{
    char character;
    scanf(" %c", &character);
    getchar(); //I just added this line
    character = toupper(character);
    return character;
}

。这通常是一种很好的工作方式。请注意,它仍然没有处理 EOF——你总是需要担心 EOF。如果用户键入 anewlinescanf() 之后的 getchar() 将读取换行符,但如果他们键入 a-z 和然后 换行。你必须决定你想用它做什么——角色狼吞虎咽的循环通常是一个好主意,而不是单一的 getchar() 调用:

int c;
while ((c = getchar()) != EOF && c != '\n')
    ;

并回应以下评论:

Please explain the importance of handling EOF.

。如果你不问,你不一定会了解它!输入和输出 (I/O),尤其是输入,充满了压力。用户不会输入您告诉他们输入的内容;他们在您告诉他们输入的内容之前或之后添加 space;你期待像 good 这样的短字,他们输入 supercalifragilisticexpialidocious。有时会出错,没有更多数据可供读取——这种状态称为 EOF 或 "end of file".

. In the function with char character; scanf(" %c", &character); and no check, if there is no input (the user types ^D on Unix or ^Z on Windows, or the data file ended), you have no idea what value is going to be in the variable — it is quasi-random (indeterminate), and using it invokes undefined behaviour。那很糟。此外,在问题的代码中,您有这个循环,如果用户指示 EOF,该循环将永远不会结束。

while (getchar() != '\n' && getchar() != '[=13=]')    // Should handle EOF!
    ;

。而且,为了增加复杂性,如果普通 char 是无符号类型,则分配给字符并测试 EOF 将始终失败,如果普通 char 是有符号类型,您将在有效字符上检测到 EOF(通常是 ÿ — 小拉丁语Unicode 和 8859-1 或 8859-15 代码集中带有分音符的字母 y)。这就是为什么我的代码使用 int c;用于字符输入。因此,如您所见(我希望如此),您必须始终关注 EOF 是有充分理由的。它可能会在您意想不到的时候发生,但您的代码不应因此进入无限循环。

I'm not sure how and where to … implement this … in my code.

。这有两个部分。一个在 readChar() 函数中,它需要 return 一个 int 而不是 char (出于与 getchar() returns 相同的原因int 而不是 char),或者需要一个替代接口,例如:

bool readChar(char *cp)
{
    int c;
    while ((c = getchar()) != EOF && isspace(c))
        ;
    if (c == EOF)
        return false;
    *cp = toupper(c);
    while ((c = getchar()) != '\n' && c != EOF /* && c != '[=14=]' */)
        ;
    return true;
}

这样您就可以调用:

if (readChar(&character))
{
    …process valid input…
}
else
{
    …EOF or other major problems — abandon hope all ye who enter here…
}

通过正确检测 EOF 的函数,您可以修复调用代码,以便它处理来自 readChar().

的 EOF(错误指示)

请注意,空循环体由单独一行缩进的分号表示。这就是 K&R(Kernighan 和 Ritchie in The C Programming Language — 1988)用空体编写循环的方式,所以你会发现它被广泛使用。

随着时间的推移,您会发​​现用 C 编写的大量代码都是用于错误处理的。

不要像 while(getchar() != '\n' && getchar() != '[=11=]');

那样每次循环读取两次

从用户输入的中读取第一个字符:

A text stream is an ordered sequence of characters composed into lines, each line consisting of zero or more characters plus a terminating new-line character. Whether the last line requires a terminating new-line character is implementation-defined. C11dr §7.21.2 2

  1. 使用getchar()。它 returns intunsigned charEOF 范围内。

  2. 阅读 行的其余部分

示例代码,有点像 OP 的。

int readChar_and_make_uppercase() {
  int ch = getchar();
  if (ch != '\n' && ch != EOF) {
    // read rest of line and throw it away
    int dummy;
    while ((dummy = getchar()) != '\n' && dummy != EOF) {
      ;
    }
  }
  return toupper(ch);
}