1

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;
  }
Voltini
  • 45
  • 4
  • 4
    `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](https://stackoverflow.com/questions/5240789/scanf-leaves-the-new-line-char-in-the-buffer), so your function reads a newline left over by previous I/O operations. But without an MCVE ([MCVE]), we can't demonstrate that my hypothesis is accurate. – Jonathan Leffler Nov 03 '18 at 00:12
  • Is it possible to know why even after I modified my code the newline character is read – Voltini Nov 03 '18 at 00:22
  • 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; c = toupper(c); int junk; while ((junk = getchar()) != '\n' && junk != EOF /* && junk != '\0' */) ; return 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. – Jonathan Leffler Nov 03 '18 at 00:22
  • 2
    To answer "Is it possible to know why … the newline character is read?", the answer is "No; we don't have enough code to know what the actual problem is (or problems are)". This is why an MCVE is important; it allows us to help you reliably. Without it, all we can do is speculate — the speculation may be reasonably solid, but it is only speculation, and programmers (novice and experienced alike) do the weirdest things to make sure that speculators speculate incorrectly. – Jonathan Leffler Nov 03 '18 at 00:26
  • Thanks for the help, I think I figured out the problem, thanks to you. – Voltini Nov 03 '18 at 00:33

3 Answers3

3

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 a-z 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); 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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
1

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);
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • If the implementation is POSIX compliant, then the last [Line](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206) will terminate with a newline. – David C. Rankin Nov 03 '18 at 05:35
  • @DavidC.Rankin True, Yet neither C nor this post require POSIX. – chux - Reinstate Monica Nov 03 '18 at 05:43
  • True, neither require POSIX, but if the questioner is asking about the use of `getchar()` it's a fair bet they lack understanding of both. I saw it as dovetailing with [C11 Standard - §7.21.2 Steams(p2)](http://port70.net/~nsz/c/c11/n1570.html#7.21.2) – David C. Rankin Nov 03 '18 at 05:47
  • @DavidC.Rankin If a line ends with a `'\n'` or just end-of-file, or if OP compiling with a POSIX compliant complier or not, does not change this answer's function. Dual compare `ch != '\n' && ch != EOF` is still needed. – chux - Reinstate Monica Nov 03 '18 at 05:52
  • No question about that. The follow-on was related to *"Whether the last line requires a terminating new-line character is implementation-defined."* simply pointing out that if the implementation is POSIX, then there should be a final newline. – David C. Rankin Nov 03 '18 at 05:57
0

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;
}
Voltini
  • 45
  • 4
  • 1
    That is often a good way to work. Note that you've 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 `a-z` and then newline. You have to decide what you want done with that – and a character gobbling loop is often a good idea: `int c; while ((c = getchar()) != EOF && c != '\n') ;` instead of your single `getchar()` call. – Jonathan Leffler Nov 03 '18 at 00:36
  • 1
    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". In the function with `char character; scanf(" %c", &character);` and no check, _[…continued…]_ – Jonathan Leffler Nov 03 '18 at 00:48
  • 1
    _[…continuation…]_ 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 — but it is quasi-random (indeterminate). That's bad. Further, in the code from the question, you have `while(getchar() != '\n' && getchar() != '\0');` and that loop would never end if the user indicates EOF. […continued…]_ – Jonathan Leffler Nov 03 '18 at 00:49
  • 1
    _[…continuation…]_ 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. – Jonathan Leffler Nov 03 '18 at 00:50
  • I'm not sure how and where to exactly implement this code in my code. (Again, sorry but I'm still learning to code). – Voltini Nov 03 '18 at 00:50
  • 1
    "I'm not sure how and where to … implement this … in my code" — there are two parts to that. One is in the `readChar()` function, which needs to return an `int` and not a `char`, or which needs an alternative interface such as `bool readChar(char *cp)` so that you can all `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()`. An awful lot of code in C is for error handling. – Jonathan Leffler Nov 03 '18 at 00:55
  • 3 weaknesses 1) `scanf(" %c", &character);` consumes leading white-spaces like `' '` and `'\n'` before saving a non-white-space into `character`. This is _not_ like `gertchar()`. 2) `getchar()` after `scanf(" %c", &character);` will consume a `'\n'` _as well as any following character_. 3) `character = toupper(character)` is undefined behavior (UB) when `character < 0`. – chux - Reinstate Monica Nov 03 '18 at 05:12
  • Does that mean I should not use toupper function in my code? – Voltini Nov 03 '18 at 15:54