简体   繁体   中英

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

I wanted to create a function that inputs a character in the console but the problem is that sometimes when I input 'a' it is considered as an empty char and the programm asks me to re-input a char. This is the function :

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

Converting a barrage of comments into an answer.

1 . Note that getchar() returns an int , not a char . And your loop needs to take into account EOF more than a null byte, though it's probably OK to detect null bytes.

My best guess about the trouble is that sometimes you do scanf("%d", &i) or something similar, and then call this function — but 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 ), we can't demonstrate that my hypothesis is accurate.

2 . Also, your 'eat the rest of the line' loop should only call getchar() once on each iteration; you call it twice. One option would be to use:

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 != '\0' */)
        ;
    return toupper(c);
}

This eats white space until it gets a non-white-space character, and then reads any junk characters up to the next newline. It would fix my hypothetical scenario. Beware EOF — take EOF into account always.

Based on reading the Q&A about scanf() not reading a newline, Voltini proposed a fix:

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

3 . That is often a good way to work. Note that it has still not dealt with EOF — you always have to worry about EOF. The getchar() after the scanf() will read the newline if the user typed a and newline , but not if they typed az and then newline . You have to decide what you want done with that – and a character gobbling loop is often a good idea instead of the single getchar() call:

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

And in response to a comment along the lines of:

Please explain the importance of handling EOF.

4 . If you don't ask, you won't necessarily learn about it! Input and output (I/O) and especially input, is fraught. Users don't type what you told them to type; they add spaces before or after what you told them to type; you expect something short like good and they type supercalifragilisticexpialidocious . And sometimes things go wrong and there is no more data available to be read — the state known as EOF or "end of file".

5 . In the function with char character; scanf(" %c", &character); 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 . That's bad. Further, in the code from the question, you have this loop, which would never end if the user indicates EOF.

while (getchar() != '\n' && getchar() != '\0')    // Should handle EOF!
    ;

6 . And, to add to the complexity, if plain char is an unsigned type, assigning to character and testing for EOF will always fail, and if plain char is a signed type, you will detect EOF on a valid character (often ÿ — small latin letter y with diaeresis in Unicode and 8859-1 or 8859-15 code sets). That's why my code uses int c; for character input. So, as you can see (I hope), there are solid reasons why you have to pay attention to EOF at all times. It can occur when you don't expect it, but your code shouldn't go into an infinite loop because of that.

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

7 . There are two parts to that. One is in the readChar() function, which needs to return an int and not a char (for the same reasons that getchar() returns an int and not a char ), or which needs an alternative interface such as:

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 != '\0' */)
        ;
    return true;
}

so that you can call:

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

With the function correctly detecting EOF, you then have your calling code to fix so that it handles an EOF (error indication) from readChar() .

Note that empty loop bodies are indicated by a semicolon indented on a line on its own. This is the way K&R (Kernighan and Ritchie in The C Programming Language — 1988) wrote loops with empty bodies, so you find it widely used.

You will find over time that an awful lot of the code you write in C is for error handling.

Do not read potentially twice per loop as with while(getchar() != '\\n' && getchar() != '\\0'); @Jonathan Leffler


To read a single and first character from a line of user input:

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. Use getchar() . It returns an int in the range of unsigned char or EOF .

  2. Read the rest of the line .

Sample code, a bit like OP's.

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);
}

Apparently, I forgot to take care of the newline character stored in the buffer (correct me if I'm wrong).

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM