C - 有没有办法读取用户输入的单个字符,而不让其余 ​​"pushed down" 进入下一个输入请求?

C - Is there a way to read a single character of user input, and not have the rest "pushed down" to the next request for input?

所以,我正在用 C 开发一个简单的刽子手游戏,我有函数 read_guess,如下所示。

void read_guess(char *guesses, char *p_current_guess)
{
    int valid_guess = 0;

    // Repeatedly takes input until guess is valid
    while (valid_guess == 0)
    {
        printf(">>> ");
        fgets(p_current_guess, 2, stdin);

        if (!isalpha(*p_current_guess)) printf("Guesses must be alphabetic. Please try again.\n\n");
        else
        {
            valid_guess = 1;

            // Iterates over array of guesses and checks if letter has already been guessed
            for (int i = 0; guesses[i] != '[=11=]'; i++)
            {
                if (guesses[i] == *p_current_guess)
                {
                    printf("You have already guessed this letter. Please try again.\n\n");
                    valid_guess = 0;
                    break;
                }
            }
        }
    }
}

我已经尝试了所有标准输入函数(包括 getchar),但是对于所有这些函数,当提供大于一个字符的输入时,而不是只获取第一个字符并继续(或再次询问) ,输入的其余部分是 "pushed back",下次请求输入时,无论是因为输入包含非字母字符还是下一轮开始,都会自动处理输入的其余部分。这对输入的每个字符重复。

我怎样才能避免这种情况?

您可以在 windows 上使用 getch() 函数来获取单个字符。这是 linux 等效的

What is the equivalent to getch() & getche() in Linux?

您可以丢弃所有输入,直到行尾,每次您想要请求输入时。

void skip_to_eol(FILE* f, int c)
{
    while (c != EOF && c != '\n')
        c = fgetc(f);
}
...
    char c = getchar(); // instead of fgets
    skip_to_eol(stdin, c);

您正在使用 fgets 这很好,但不幸的是不是正确的方法...

fgets 读到行尾 或至多比要求的字符数 少 1。当然剩下的字符留给下一次读取操作...

惯用的方法是确保读取到行尾,无论长度如何,或者至少达到更大的长度。

  1. 简单但输入超过 SIZE 个字符时可能会失败:

    #define SIZE 64
    ...
    
    void read_guess(char *guesses, char *p_current_guess)
    {
        char line[SIZE];
        int valid_guess = 0;
    
        // Repeatedly takes input until guess is valid
        while (valid_guess == 0)
        {
            printf(">>> ");
            fgets(line, SiZE, stdin);                // read a line of size at most SIZE-1
            p_current_guess[0] = line[0];            // keep first character
            p_current_guess[1] = '[=10=]';
            ...
    
  2. 稳健但稍微复杂一些

    /**
     * Read a line and only keep the first character
     *
     * Syntax: char * fgetfirst(dest, fd);
     *
     * Parameters:
     *  dest: points to a buffer of size at least 2 that will recieve the
     *         first character followed with a null
     *  fd  : FILE* from which to read
     *
     * Return value: dest if one character was successfully read, else NULL
     */
    char *readfirst(dest, fd) {
    #define SIZE 256              // may be adapted
        char buf[SIZE];
        char *cr = NULL;          // return value initialized to NULL if nothing can be read
        for (;;) {
            if(NULL == fgets(buff, sizeof(buff), fd)) return cr; // read error or end of file
            if (0 == strcspn(buff, "\n")) return cr;             // end of file
            if (cr == NULL) {                                    // first read:
                cr = dest;                        //  prepare to return first char
                dest[0] = buff[0];
                dest[1] = 0;
            }
        }
    }
    

    然后您可以在您的代码中简单地使用它:

    void read_guess(char *guesses, char *p_current_guess)
    {
        int valid_guess = 0;
    
        // Repeatedly takes input until guess is valid
        while (valid_guess == 0)
        {
            printf(">>> ");
            fgetfirst(p_current_guess, stdin);