getting input from stdin in C

I am running a program in C that takes input from the stdin . I did this
char input[2];
scanf("%s",input);
The user is able to enter more than 2 characters into input even though the size of the array is 2 .
What is wrong with scanf? I want to be able to restrict the number of characters only to 2 .
Thank You .
webusernameAsked:
Who is Participating?
 
ddunleaConnect With a Mentor Commented:
Hi webusername,

scanf is a bit of a pain that way. It won't do bounds checking. Try this:

fgets(input,2,stdin);

Also, note that a string is null-terminated, so they can really only enter one character if you define input to be 2 characters long. The above won't stop them typing more than one character, but it will stop you reading the extra characters.
0
 
Kent OlsenConnect With a Mentor Data Warehouse Architect / DBACommented:
Hi webusername,

C doesn't know about the length of things like strings, buffers, memory blocks, etc.  PASCAL, C++, and other languages do, but not C.

In your example you declare input[2].  You could just as easily have declared input[200000].  The compiler knows the size of the block and generates an object file that the linker uses to reserve the requested space.

But at run time, there is no way to know how big that buffer is.  You need to manage this yourself by calling a function that understands limited size buffers.

#define BUFFER_SIZE 2

char input[BUFFER_SIZE+2];  /*  The number of characters plus 1 for new-line plus 1 for end-of-line  */

  fgets (input, BUFFER_SIZE+2, stdin);  /*  Read up to two characters, new-line, and end-of-line  */


or you can use fscanf:

#define BUFFER_SIZE 2

char input[BUFFER_SIZE+1];  /*  The number of characters plus 1 for end-of-line  */

  fscanf (stdin, "%2s", input);  /*  Read up to two characters and end-of-line  */



There's lots of choices, but the terminal i/o routines (scanf(), gets(), etc) are poor ones.  Unfortunately, most classes start by teaching them.


Good Luck,
Kent

0
 
sunnycoderCommented:
Hi Kent,

>There's lots of choices, but the terminal i/o routines (scanf(), gets(), etc) are poor ones
This should work
scanf("%2s",input);

cheers
sunnycoder
0
Live webcast with Pinal Dave

Pinal Dave will teach you tricks to help identify the real root cause of database problems rather than red herrings. Attendees will learn scripts that they can use in their environment to immediately figure out their performance Blame Shifters and fix them quickly.

 
Julian HansenConnect With a Mentor Commented:
There are some issues with the solutions above if you want to read more than one data entry from the user. Consider the following code

#include "stdio.h"

int main(int argc, char* argv[])
{
      char s[3] ;
      int d ;

      scanf("%2s", s ) ;
      printf ( "%s\n", s ) ;
      scanf("%2d", &d ) ;
      printf ( "%d\n", d ) ;
      return 0;
}

Because you are only reading 2 characters of the input the next scanf will start reading from position 3 of the input. So if the user enteres

ab10 as the first input the program will complete and output

ab
10

And will never prompt for the second input.

There are a number of ways around this.

1. Declare a big buffer and read the whole line in using either gets or scanf.
2. Flush stdin before the next read

i.e.
   scanf ( "%2s", s);
   fflush(stdin)

Julian
0
 
sunnycoderConnect With a Mentor Commented:
Hi Julian,

>1. Declare a big buffer and read the whole line in using either gets or scanf.
gets is really unsafe and should be avoided at all costs ... from gets man page
[quote]
Never use gets(). Because it is impossible to tell without knowing the data in advance how many characters gets() will read, and because gets() will continue to store characters past the end of the buffer, it is extremely dangerous to use. It has been used to break computer security. Use fgets() instead.
[/quote]

>2. Flush stdin before the next read
According to ANSI C, fflush'ing stdin is incorrect
http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1052863818&id=1043284351
http://www.eskimo.com/~scs/C-faq/q12.26.html

>There are some issues with the solutions above if you want to read more than one data entry from the user.
The best solution will always depend on the particular programming problem. Reading into a string buffer in a loop and parsing the input is generally acceptable though

cheers
sunnycoder
0
 
Julian HansenCommented:
Thanks for the info sunnycoder,

Here is somre additional ifno

Gets - agree I was actually referring to the fgets solution used earlier on - left the f off - there you have control over how much you read in.

In terms of fflush - this is what MSDN has to say about fflush

The fflush function flushes a stream. If the file associated with stream is open for output, fflush writes to that file the contents of the buffer associated with the stream. If the stream is open for input, fflush clears the contents of the buffer. fflush negates the effect of any prior call to ungetc against stream. Also, fflush(NULL) flushes all streams opened for output. The stream remains open after the call. fflush has no effect on an unbuffered stream.

So, the solution does have validity within MS environments.

However, I do agree with the principle that it should not be used if it is not a standard across all environments - the information in your posts was news to me - you learn something new everyday.
0
 
Kent OlsenData Warehouse Architect / DBACommented:
Hi Sunny,

>>There's lots of choices, but the terminal i/o routines (scanf(), gets(), etc) are poor ones
>This should work
>scanf("%2s",input);

In this limit scope, that's quite true.  But it tends to hide problems when program changes are made.  I'm a supporter of defining the length of the object (#define OBJECT_LENGTH 10) and using the length as a parameter to the function.  When things change later, you're less likely to get bit.

(Heading you off at the pass....)  You can certainly use these constants with scanf(), but it produces some ugly code.

scanf ("%*s", OBJECT_LENGTH, input);

Try THAT with 10 items to decode.  :)


Kent
0
 
stuartdehaanCommented:
Hi, webusername

It may be easyier to create a while loop: every cycle you call kbhit, this will return 1 if a key has been pressed and 0 otherwise. if a key has been pressed, you can use getch to read this char. if the char is enter or it's the second char typed, you abort the loop by calling break.
0
 
webusernameAuthor Commented:
kdo
i tried
char input[BUFFER_SIZE+2];  /*  The number of characters plus 1 for new-line plus 1 for end-of-line  */

  fgets (input, BUFFER_SIZE+2, stdin);  /*  Read up to two characters, new-line, and end-of-line  */


now, the program skips the first line and goes to the second question. i am pulling my hair...
0
 
Julian HansenCommented:
webusername,

My earlier post (if you are using MSVC) would work but as sunnycoder pointed out this is not portable.

Here is another option for you to try

#include <stdio.h>

int main(int argc, char* argv[])
{
      char s[20];
      int d;

      scanf("%2s",s ) ;
      fseek ( stdin, 0, SEEK_END ) ;
      printf ( "%s\n", s ) ;
      scanf ( "%d", &d ) ;
      printf ( "%d\n", d);
      return 0;
}

input: ab10
output: ab
input: 123
output: 123

A word on kdo's post. This is a good point but if you need to do type conversion then it makes it a bit easier i.e.

scanf ("%d", &number) as opposed to
fgets ( s, BUFFSIZE+1, stdin ) ;
number = atoi(s) ; etc

However, here is some code that does the above but with fgets

#include <stdio.h>

#define BUFFER_SIZE 2
int main(int argc, char* argv[])
{
      char s[BUFFER_SIZE+1];
      int d;

      fgets ( s,BUFFER_SIZE + 1, stdin ) ;
      fseek ( stdin, 0, SEEK_END ) ;
      printf ( "%s\n", s ) ;
      // the next line is to show that the extra chars input on the line are being dumped.
      scanf ( "%d", &d ) ;
      printf ( "%d\n", d);
      return 0;
}
0
 
fridomConnect With a Mentor Commented:
Well someone in comp.lang.c does prefer scanf over other ways of getting a "string" securly
The code probably will look like this:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>



int main(void){
  char str[3];
  int irval = 0;

  printf("give me a string : ");
  fflush(stdout);
  irval = scanf("%2[^\n]", str);
  if (irval == 1){
    printf("str = %s, strlen(str) = %d\n", str, strlen(str));
  } else {
    puts("has worked");
  }
 
    return 0;
}

It save to use it that way because the format directive says read exactly 2 character and we have enough space for two character and the preceeding \0. I think I like this way of scanf usage because it just discards everything which is too large try to get the same with
fgets and you see that this is not trivial!

This is well documented in the libc-documentation

However most C programmers use  a combination form fgets + sscanf.

Regards
Friedrich
0
 
Julian HansenCommented:
This still suffers from the problem that if more chars are entered the next scanf or fgets or gets will read the remaining characters from the previous entry.

i.e.

scanf ( "%2[^\n]", str ) ;

If you enter
ab10

str will contain "ab".

However the next scanf will read the 10 and return without prompting the user for any input.

so

scanf ( "%2[^\n]", str ) ;
printf ( "%s\n", str ) ;
scanf ("%d", &d) ;
printf( "%d\n", d ) ;

with an input of "ab10"

will output

ab
10

but only one prompt.
0
 
fridomCommented:
Ok so the problem of the to many read still persists. One can try to get away with
scanf("%2[^\n]%*s", str );
....


Regards
Friedrich
0
 
Kent OlsenData Warehouse Architect / DBACommented:

>kdo
>i tried
>char input[BUFFER_SIZE+2];  /*  The number of characters plus 1 for new-line plus 1 for end-of-line  */

>  fgets (input, BUFFER_SIZE+2, stdin);  /*  Read up to two characters, new-line, and end-of-line  */

>now, the program skips the first line and goes to the second question. i am pulling my hair...


There are a number of caveats with this approach.  For instance, if one character is entered before the new-line, you get just the character and the new-line.  If three characters are entered before the new-line, you get those three characters, but NOT the new-line, etc...

Kent
0
 
a_yurichCommented:
You may use this constuction
This code is slower but aviod buffer overflow(safe code)

#define sz 10

char input[sz];
for(int i=0;i<sz;i++)
{
      input[i]=getchar();
}

0
 
Killroy76Commented:
#include <stdio.h>
#include <string.h>

int main()
{
  char buf[BUFSIZ];
  char *p;
 
  printf ("Please enter a line of text, max %d characters\n", sizeof(buf));
 
  if (fgets(buf, sizeof(buf), stdin) != NULL)
  {
    printf ("Thank you, you entered >%s<\n", buf);
   
    /*
     *  Now test for, and remove that newline character
     */
    if ((p = strchr(buf, '\n')) != NULL)
      *p = '\0';
     
    printf ("And now it's >%s<\n", buf);
  }
 
  return 0;
}


/*
 Program output:

Please enter a line of text, max 512 characters
this is a test
Thank you, you entered >this is a test
<
And now it's >this is a test<
*/
0
 
ssnkumarConnect With a Mentor Commented:
Hi webusername,

   Looking at your question, the frank answer I can give you is "it is not possible with plain C"!
   But, you can do it in windows programming, where you can put such restrictions.
   You can even specify the column and row for the output/input.

   If you don't intend to do windows programming, then take the comments from others and modify your code.
   In this user will not be restricted to enter any number of characters. But, in your code, you can take only first two letters typed, after user presses "ENTER".

-ssnkumar
0
 
ssnkumarCommented:
Hi webusername,

   Is your question answered?
   If yes, please close this question.
   If not, please give feedback, so that we can continue the discussion.

-ssnkumar
0
All Courses

From novice to tech pro — start learning today.