tags:

views:

87

answers:

6

I'm trying to write a simple program that asks a user to choose from a menu in a loop. I use getchar() to get the input, however i've noticed that when I enter a char and press 'Enter' the program makes two loops (as if i pressed twice) one the char as an input and another for 'Enter' as an input.

How do I fix this?

Thanx.

A: 

How about

#include <stdio.h>

/*! getline() reads one line from standard input and copies it to line array
 * (but no more than max chars).
 * It does not place the terminating \n in line array.
 * Returns line length, or 0 for empty line, or EOF for end-of-file.
 */
int getline(char line[], int max)
{
  int nch = 0;
  int c;
  max = max - 1;            /* leave room for '\0' */

  while ((c = getchar()) != EOF) {
    if (c == '\n')
      break;

    if (nch < max) {
      line[nch] = c;
      nch = nch + 1;
    }
  }

  if (c == EOF && nch == 0)
    return EOF;

  line[nch] = '\0';
  return nch;
}

Source

ohadsc
I must not use string or char arrays(its a work for a course)
SnapDragon
+3  A: 

You need to read about canonical vs. non-canonical input. The following Stack Overflow question addresses this:

canonical-vs-non-canonical-terminal-input

Michael Goldshteyn
+1 - You can set the terminal into raw (non-canonical) mode by using [tcsetattr()](http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html) to manipulate the **termios** structure.
jschmier
Thanx, will keep this in mind.
SnapDragon
+4  A: 

Add a getchar() after the getchar() :P

Prasoon Saurav
I guess It would work, but its quiet anaesthetic.
SnapDragon
+1  A: 

The easiest way is to filter out the enter key as the return value from getchar

char c = getchar();
if ( c != '\n' ) {
  ...
}
JaredPar
I guess you meant getchar()? if so getchar() returns int.
Nyan
@Nyan, `getchar` does indeed return `int` but it's legal to assign to `char` via conversions. Reference http://www.cplusplus.com/reference/clibrary/cstdio/getchar/
JaredPar
@Jared: yes, but char is limited to (tipically) 256 values, and you need (tipically) 257 values to identify **ALL** characters **AND** EOF. That's why `getchar()` returns an int
pmg
@pmg, I'm aware of that. I'm merely demonstrating what the OP asked for (how to check for newline).
JaredPar
Ok, but you can demonstrate with `int c` too :-)
pmg
I declared it as int as linux man states for getchar() return value.
SnapDragon
A: 

getchar() returns the first character in the input buffer, and removes it from the input buffer. But other characters are still in the input buffer (\n in your example). You need to clear the input buffer before calling getchar() again:

void clearInputBuffer() // works only if the input buffer is not empty
{
    do 
    {
        c = getchar();
    } while (c != '\n' && c != EOF);
}
Samuel_xL
A: 

You've kind of answered your own question; you have to deal with the newline character somehow.

There are several options. If your menu options are numbered, you can use scanf() to read in an integer value and switch based on that:

printf("Pick an option: ");
fflush(stdout);
scanf("%d", &option);
switch(option)
{
  case 0 : do_something(); break;
  case 1 : do_something_else(); break;
  ...
  default: bad_option(); break;
}

The advantage of this option is that the %d conversion specifier skips over any leading whitespace, including newline characters, so you don't have to worry about any unread \n clogging up the input stream (in fact, most of the conversion specifiers skip leading whitespace; %c doesn't, making it behave a lot like getchar()).

The disadvantage of this option is that if someone fat-fingers a non-digit character in their input, it won't be read with the %d conversion specifier, and will stay stuck in the input stream until a call to getchar() or scanf() with a %s or %c conversion specifier.

A better option is to read all input as character strings using fgets(), then parse and validate as necessary.

/**
 * Prints a prompt to stdout and reads an input response, writing
 * the input value to option.  
 *
 * @param prompt [in]  - prompt written to stdout
 * @param option [out] - option entered by user
 *
 * @return - 1 on success, 0 on failure.  If return value is 0, then option
 * is not changed.
 */
int getOption(const char *prompt, char *option)
{
  char input[3]; // option char + newline + 0 terminator
  int result = 0;

  printf("%s: ", prompt);  
  fflush(stdout);

  if (fgets(input, sizeof input, stdin))
  {
    /**
     * Search for a newline character in the input buffer; if it's not
     * present, then the user entered more characters than the input buffer 
     * can store.  Reject the input, and continue to read from stdin until
     * we see a newline character; that way we don't leave junk in the
     * input stream to mess up a future read.
     */
    char *newline = strchr(input, '\n');
    if (!newline)
    {
      printf("Input string is too long and will be rejected\n");
      /**
       * Continue reading from stdin until we find the newline
       * character
       */
      while (!newline && fgets(input, sizeof input, stdin))
        newline = strchr(input, '\n');
    }
    else
    {
      *option = input[0];
      result = 1;
    }
  }
  else
    printf("Received error or EOF on read\n");

  return result;
}

Yes, that's a lot of work to read in one stupid menu option, and that's the simple version. Welcome to the wonderful world of interactive input processing in C.

John Bode