How do I limit my user input - c

I've been creating a program requesting the user to input a char value, but if they enter more than one character it will move onto the next function and break the program. I've added in the second method which gets run when multiple inputs are entered.

Your problem is not one of limiting the number of characters written to cInput; the format specifier %1s does that already. Your problem is one of leaving unprocessed characters in the input buffer. You need to remove all characters from the buffer if subsequent input will not handle them. For example if you leave a alphabetic character in then buffer but later read with %d, the function will return immediately (because there is implicitly a newline also buffered), but the character will remain buffered because it is not a decimal. This will continue indefinitely if you never clear the buffer.
For a single character, you can check the character is not a newline, and then repeatedly get characters until a newline is found, as follows:
scanf("%c", &cInput ) ;
while( cInout != '\n' && getchar() != '\n' ) { } // flush buffered line
If you want to be sure the user only enters a single character, then you could modify the above thus:
scanf("%c", &cInput ) ; // Get a single character
if( cInput != '\n' && // If the input is not "empty",
getchar() != '\n' ) // but the first character entered was not
// immediately followed by a newline...
{
// Flush to the end of the entered line
while( getchar() != '\n' ) { }
// Invalidate the input to force retry
cInput = 0 ;
}
At least one character will be buffered - a newline at least. A valid answer will have two characters one in cInput and a newline. The if(...) condition above reads the second character if there is one (using short-circuit evaluation of cInput), and checks that it is the end of the input (newline). If it is not, it reads all buffered characters then invalidates cInput (in case say "No-way\n" were entered for example, so that cinput contained 'N'.
For numeric input, you simply read characters until the newline is found:
scanf("%d", &nValue);
while( getchar() != '\n' ) { } // flush buffered line
If trailing non-numeric characters should render the entire input invalid, you need to check that the following character is a newline.
int converted = scanf("%d", &nValue);
if( converted == 0 || getchar() != '\n' )
{
valid_input = false ;
while( getchar() != '\n' ) { } // flush buffered line
}
Note that there are other possible solutions. This is my preferred solution.
When applied to your functions (with other simplifications):
int intAskUser(void)
{
char cInput = 0 ;
while( cInput != 'N' && cInput != 'Y' )
{
printf("Do you want to enter a value? Y or N\n");
scanf("%c", &cInput ) ;
if( cInput != '\n' && getchar() != '\n' )
{
while( getchar() != '\n' ) { } // flush buffered line
cInput = 0 ;
}
else
{
cInput = toupper(cInput) ;
}
}
// Return answer code 0 to 1
return (cInput == 'N') ? 0 : 1 ;
}
int getValue(int nLower, int nUpper)
{
assert( nLower < nUpper ) ; // precondition check
int nValue = 0 ;
bool valid_input = false ;
while( !valid_input )
{
printf("Enter a value between %d and %d: ", nLower, nUpper ) ;
int converted = scanf("%d", &nValue);
if( converted == 0 || getchar() != '\n' )
{
valid_input = false ;
while( getchar() != '\n' ) { } // flush buffered line
}
valid_input = nValue >= nLower &&
nValue <= nUpper ;
if( !valid_input )
{
printf( "Please try again. " );
}
}
printf("Value: %d", nValue);
return nValue;
}
Note that toupper() requires ctype.h to be included, and the type bool requires stdbool.h.

If I understand your question correctly, you want the function to return if the user input is any of
y followed by newline
Y followed by newline
n followed by newline
N followed by newline
In all other cases you want to stay in the function and get new user input (e.g. nYnhhh followed by newline shall not return).
To my knowledge there is no standard function that can achieve that, so you have to write your own code.
When doing that there are two things to remember:
You must read at least 2 chars in order to check for "too long" input strings.
After unsuccessful inputs you need to empty the input buffer before reading new input. An unsuccessful input is one that doesn't have \n as last char.
Finally I recommend you use fgets() instead of scanf() as it is much easier to use. Don't use gets() though - it's dangerously vulnerable to buffer overrun.
Your code could look like:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int AskUser(void)
{
char input[3] = {'\0'}; // Make room for a string of 2 chars + terminating null
int c;
int len;
printf("Do you want to enter a value? Y or N\n");
do
{
if (!fgets(input, 3, stdin))
{
printf("Input error");
exit(1);
}
len = strlen(input);
if (len)
{
if (input[len-1] != '\n') // Check whether last char is a newline
{
// It isn't so empty the input buffer
while ((c = getchar()) != '\n' && c != EOF) { }
}
}
if (len == 2) // Check for exactly 2 chars in input
{
if(toupper(input[0]) == 'Y' && input[1] == '\n')
{
return 1;
}
if(toupper(input[0]) == 'N' && input[1] == '\n')
{
return 0;
}
}
printf("Please enter a valid input, Y or N: \n");
} while(1);
}
int main(void) {
printf("Answer is %d\n", AskUser());
return 0;
}

Related

Issue while creating a simple calculator in C

I am creating a very basic calculator in C but the output is not coming as desired.
#include<stdio.h>
int main(int argc, char const *argv[])
{
/* code */
char ch;
int a,b,p=0;
scanf("%d",&a);
while(1)
{
ch=getchar();
if(ch==';')
{
p=2;
break;
}
scanf("%d",&b);
if(ch=='+')
{
a+=b;
}
if(ch=='-')
{
a-=b;
}
if(ch=='*')
{
a*=b;
}
if(ch=='/' && b!=0)
{
a/=b;
}
if(ch=='/' && b==0)
{
printf("INVALID INPUT\n");
p=2;
break;
}
}
if(p!=0)
printf("%d",a);
return 0;
}
The Output is always coming as the initial value which has been assigned to "a".
Output which is coming-
4
+
5
;
4
Expected output -
4
+
5
;
9
Can you please help me with this issue of why the expression is not getting evaluated correctly?
The line
scanf("%d",&a);
will consume the first number from the input stream, but it will leave the newline character on the input stream.
Therefore, when you later call
ch=getchar();
it will read that newline character. It will not read the + character.
If you want to read the + character, then you can change that line to the following:
scanf( " %c", &ch );
This line will first discard all whitespace characters and will match the first non-whitespace character on the input stream.
Afterwards, your program will have the desired output:
4
+
5
;
9
An alternative solution would be to discard the rest of the line after every call to scanf that uses the %d format specifier. That way, calling getchar immediately afterwards should read the + character as intended.
You can discard the remainder of an input line using the following code:
//discard remainder of input line
{
int c;
do
{
c = getchar();
} while ( c != EOF && c != '\n' );
}
Here is a more compact way of writing the same thing:
//discard remainder of input line
for ( int c; (c=getchar()) != EOF && c != '\n'; )
;
The only problem is in scanning the inputs.
As a tab or a new line must seperate the value supplied to scanf.
In short, just add \n at the end of scanf.
scanf("%d\n", &a);
scanf("%d\n", &b);
this should do it.

read ints from standard input until \n is found

I'm trying to make a function that reads ints from stdin. it has to read until a certain amount of numbers is read (count in example below), or until it finds a '\n'.
Since as far as I am aware scanf (with %d format specifier) ignores newlines, I used getchar and converted the character into the number it should be.
this works but only for 1 digit numbers.
is there any better way to achieve this?
This is my code:
char num = getchar();
while (num != '\n' && count < 9) {
//boring operations that don't matter
num = getchar()
}
Reading via fgets() is better. Continue reading if your must use scanf().
To use scanf("%d",...), we need extra care to read a line. As "%d" consumes leading white-space, including '\n', we need more code to look for white-space and test if a '\n' is found.
int count = 0;
while (count < 9) {
// Read leading spaces
int ch;
while (isspace((c = getchar())) && c != '\n') {
;
}
if (c == '\n' || c == EOF) break; // We are done reading
ungetc(c, stdin); // put character back
int some_int;
if (scanf("%d", &some_int) == 1) {
printf("Integer found %d\n", some_int);
count++;
} else {
// Non-numeric input, consume at least 1 character.
getchar();
}
}
If numeric text is outside the range of int, the above use of "%d" is undefined behavior. For robust code, use fgets().
The %d conversion specifier only ignores leading whitespace. So you can do something like:
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
int n = argc > 1 ? strtol(argv[1], NULL, 10) : 10;
int x;
while( n-- && scanf("%d%*[ \t]", &x) == 1 ){
printf("Read: %d\n", x);
int c = getchar();
if( c == EOF || c == '\n' ){
break;
}
ungetc(c, stdin);
}
return 0;
}
However, this will probably not handle a stream like 10 5 x in a reasonable way. You'll need more logic on the first non-whitespace after an integer to handle that (maybe just do if( c == EOF || ! isdigit(c) ){ break; }). Parsing data with scanf if fickle (it really never has a purpose outside of university exercises). Just use fgets and strtol.
scanf() doesn't ignore \n
#include <stdio.h>
#include <stddef.h>
int main(int argc , char *argv[])
{
int b;
char c;
scanf("%d%c",&b,&c);
if(c == '\n') printf("and then " );
}
Someone posted an answer and then deleted but it was the perfect solution for my problem, so all credit to the original author.
The solution was reading normally with scanf and afterwards,with getchar, checking if it was \n or EOF. If it was break out of the cycle, if it wasn't, "unread" with ungetc so you can scanf the number in the next iteration.
So my final code looks like this:
while(scanf("%d",&num) == 1 && count<9){
//boring operations
c = getchar();
if (c == EOF || c == '\n') break;
if (ungetc(c,stdin) == EOF) break;
}
NOTE: like Andrew Henle pointed out in the replies, this doesn't work unless it is guaranteed that there isn't any space between the digits and the newline

How to enter new line until empty line is encountered in C

This is my code here, I'm trying to create a programme that counts characters using functions and then determine the average value of characters when an empty line is encountered. The programme is suppose to allow the user to enter multiple lines until an empty line is encountered but I can't seem to.
#include <stdio.h>
#include <Windows.h>
int main()
{
char str[1000];
int Digits, Char, SpecialChar, linecount = 0;
int counter;
int total;
int average;
Digits = Char = SpecialChar = 0;
printf("Please type in your words here: ");
gets(str);
for (counter = 0; str[counter] != NULL; counter++)
{
if (str[counter] >= '0' && str[counter] <= '9')
Digits++;
else if ((str[counter] >= 'A' && str[counter] <= 'Z') || (str[counter] >= 'a' && str[counter] <= 'z'))
Char++;
else
SpecialChar++;
}
while (str[counter] != '\n')
{
if (str[counter] = '\n')
{
linecount ++;
}
}
total = Digits + Char + SpecialChar;
average = total / linecount;
printf("\nDigits: %d \nCharacters: %d \nSpecial Characters: %d \nLine: %d", Digits, Char, SpecialChar, linecount);
printf("\nTotal no. of characters = %d", total);
printf("\nAverage no. of characters = %d", average);
Sleep(5000000);
return 0;
}
As far as I know, the function gets is interrupted after "\n". Also, using fgets you will have to put an attention on the '\0' addition on the string. That means that
The programme is suppose to allow the user to enter multiple lines until an empty line is encountered but I can't seem to.
will never be able to be accomplished using gets this way. Because gets is not a recommended function, I edited you code a little bit in a way that you might be searching.
Something to metion, that I found out it might be a logic error, before you read this code
for (counter = 0; str[counter] != NULL; counter++)
This seems strange, because the fgets will always record the "\n" character. So, the next condition
if (str[counter] = '\n')
will never be true
I see some others errors on you code, but, not majors ones. So, I see the suggestion as a sufficient as appointing them
while (fgets(str, 1000, stdin) && str[0] != '\n'){ //I dont know if checking the first element of the string is redundancy,
//because, the I think the fgets function will return NULL if you just press enter, as the first character
for (counter = 0; str[counter] != '\n'; counter++{
if (str[counter] >= '0' && str[counter] <= '9')
Digits++;
else if ((str[counter] >= 'A' && str[counter] <= 'Z') || (str[counter] >= 'a' && str[counter] <= 'z'))
Char++;
else
SpecialChar++;
}
linecount ++; //new line is someting that you will always reach, so,
//there is no reason for any condition
}
Solution is below:
#include <stdio.h>
#include <Windows.h>
int main()
{
char str[1000];
int Digits, Char, SpecialChar, linecount = 0;
int counter;
int total;
int average;
int flag = 1;
Digits = Char = SpecialChar = 0;
printf("Please type in your words here: ");
while(flag == 1)
{
gets(str);
if (str[0] == NULL || str[0] == '\n')
{
flag = 0;
break;
}
for (counter = 0; str[counter] != NULL; counter++)
{
if (str[counter] >= '0' && str[counter] <= '9')
Digits++;
else if ((str[counter] >= 'A' && str[counter] <= 'Z') || (str[counter] >= 'a' && str[counter] <= 'z'))
Char++;
else
SpecialChar++;
}
linecount ++;
}
total = Digits + Char + SpecialChar;
average = total / linecount;
printf("\nDigits: %d \nCharacters: %d \nSpecial Characters: %d \nLine: %d", Digits, Char, SpecialChar, linecount);
printf("\nTotal no. of characters = %d", total);
printf("\nAverage no. of characters = %d", average);
Sleep(5000000);
return 0;
}
The gets() function is unsafe and extremely susceptible to buffer overflows. Never use this function. A better alternative is fgets(), though the non-standard (but widely available) POSIX getline() function is also a good option.
Both fgets() and gets() fetch a line of input, but gets() discards the newline character, while fgets() keeps it and stores it in the input buffer (if there is room). This means that you may need to remove the newline character after fetching a line of input with fgets(). It also means that when the user simply presses ENTER, the input buffer contains only a \n character, followed by a \0 null terminator; fgets() always null-terminates the input buffer.
To read multiple lines of user input, stopping only when the user presses ENTER, fgets() should be called in a loop. One way of doing this is to use a for(;;) {} loop construction, which never terminates (equivalent to while(1) {}). If the first character in a line of input is a \n character, then a break; statement exits the loop.
Notes on the Posted Code
The posted code is comparing input characters with character constants to determine whether the input is numeric, alphabetic, or otherwise. This is better, and more portably, accomplished by using Standard Library functions from ctype.h. Using library functions here means that the code does not need to explicitly consider the current locale or character encoding (which may not be ASCII or UTF-8).
The posted code contains the line:
for (counter = 0; str[counter] != NULL; counter++) {}
Note that NULL is the null pointer constant, equivalent to (void *) 0, but not equivalent to 0. The goal of this loop appears to be to iterate over a string, terminating when the null terminator (\0) is reached. So, the controlling expression should be changed:
for (counter = 0; str[counter] != '\0'; counter++) {}
Also, the purpose of this loop in the posted code is unclear:
while (str[counter] != '\n')
{
if (str[counter] = '\n')
{
linecount ++;
}
}
If str[counter] is a newline character, then the loop is not entered; otherwise the if statement in the loop body assigns '\n' to str[counter], evaluating to true and incrementing linecount. On the next iteration str[counter] == '\n', so the loop is terminated. After the previous loop (with NULL changed to '\0' in the controlling expression), counter is the index of the \0 character in str, so this loop replaces the null terminator with a newline character, making str a string no longer. This will lead to undefined behavior if the code later attempts to treat str as a string.
If the line if (str[counter] = '\n') is a typo, meant to be if (str[counter] == '\n'), then this is an infinite loop once entered.
An Example Program
Here is a heavily modified of the posted code that uses fgets() to get user input, and Standard Library functions to classify input characters.
The fgets() function returns a null pointer in the event of an error, so this is checked for and handled in about the simplest way possible. After input has been stored in the str[] buffer array, the first character is checked; if it is \n, then the user entered an empty line (probably: see the next paragraph), and the loop is terminated. Otherwise, the next step is to see if the input line contains a newline character at all. The strchr() (from string.h) function is used here for this. If the \n is not found, then a null pointer is returned, otherwise a pointer to the \n character is returned. This is used to write over the \n with \0, effectively removing the newline character. Then linecount is incremented. Thus, the line counter is incremented only when a newline character is encountered in the input.
Note that when input is too large for the input buffer, at least the newline character will remain in the input stream waiting for the next I/O function call. It is possible that only the newline character remains in the input stream, so on the next loop iteration the first character in the buffer is \n, interpreted by this program as an empty line. If there is a possibility that input will be larger than the buffer allocation, more subtlety will be required to handle this situation. One solution is to use a flag to indicate whether the start of a line is being read. Here, line_start is initialized to 1, set to 1 whenever linecount is incremented, and set to 0 whenever a newline character is not found in the input buffer. In order for a newline to indicate an empty line of input, line_start must be set to 1, and the first character in the input buffer must be a \n character. With this modification, the program will reliably read lines of input even longer than the allocated 1000 characters. You can test this out by making the allocation for str[] smaller; try char str[2];.
Here is the complete program:
#include <stdio.h>
#include <ctype.h> // for isdigit(), isalpha()
#include <string.h> // for strchr()
int main(void)
{
char str[1000];
int Digits = 0;
int Char = 0;
int SpecialChar = 0;
int linecount = 0;
int counter;
int total;
int average;
puts("Please type in your words here:");
int line_start = 1;
for (;;) {
if (fgets(str, sizeof str, stdin) == NULL) {
/* Handle error */
fprintf(stdin, "I/O error\n");
return 1;
}
/* Terminate loop on empty line */
if (line_start && str[0] == '\n') {
break;
}
/* If newline present, remove and increment linecount */
char *cptr = strchr(str, '\n');
if (cptr != NULL) {
*cptr = '\0';
++linecount;
line_start = 1;
} else {
line_start = 0; // complete line not read
}
/* update character counters */
for (counter = 0; str[counter] != '\0'; counter++) {
unsigned char uc = str[counter];
if (isdigit(uc)) {
Digits++;
} else if (isalpha(uc)) {
Char++;
} else {
SpecialChar++;
}
}
}
total = Digits + Char + SpecialChar;
average = total / linecount; // integer division
printf("Digits: %d \nCharacters: %d \nSpecial Characters: %d \nLine: %d\n",
Digits, Char, SpecialChar, linecount);
printf("Total no. of characters = %d\n", total);
printf("Average no. of characters = %d\n", average);
return 0;
}

' ', '\n' , scanf() and output screen

I wrote the following code to accept characters from user and enter into an array till he inputs a free space (' ') or a line \n. But code is not functioning. As in, when space bar or return key is pressed in the input, my computer is still accepting values without exiting the loop.
char X[99];
printf ("Type the string without spaces\n");
for (i=0;i<99;i++) {
scanf ("%c",&m);
if(m!=' '&&m!='\n')
X[i]=m;
else i=99;
}
Please explain the error.
First the issue: the default tty mode is canonical, meaning input is made available line by line (see man termios)
Once you fix that, you can use getchar() or read() to get one character at a time. The tty setting, example straight out of the man page of termios .
#include <stdio.h>
#include <termios.h>
int ttySetNoncanonical(int fd, struct termios *prev)
{
struct termios t;
if (tcgetattr(fd, &t) == -1)
return -1;
if (prev != NULL)
*prev = t;
t.c_lflag &= ~ (ICANON);
t.c_cc[VMIN] = 1; // 1-char at a go
t.c_cc[VTIME] = 0; // blocking mode
if (tcsetattr(fd, TCSAFLUSH, &t) == -1)
return -1;
return 0;
}
void main(void) {
printf ("\nType the string without spaces\n");
ttySetNoncanonical(0,NULL); // set to non-canonical mode
char X[1000];
int m, ec=0;
int i;
X[0] = 0;
for (i=0;i<1000;i++) {
//ec = read(0,X+i,1); // read one char at a time
m = getchar();
if(m == EOF || m==' ' || m=='\n') {
X[i] = 0;
break;
}
X[i] = m;
}
printf("You entered %s. Bye.\n\n", X);
}
HTH. You might want to check the boundary condition, in case your user typed in 1000 characters. It works for me on a GNU Linux.
use getch(), scanf() will not work that way.
it would be something like :
for(i=0;i<99;i++)
{
char ch=getch();
if(m!=' ' && m!='\n' && m!='\r')
X[i]=m;
else i=99;
printf("%c",ch);
}
The scanf function reads and ignores any whitespace characters encountered before the next non-whitespace character (whitespace characters include spaces, newline and tab characters). So, instead of
scanf("%c",&m);
use
m = getchar();
The function int getchar ( void ); gets character from stdin, returns the next character from the standard input (stdin). It is equivalent to calling getc with stdin as argument.
Also, the condition should use logical-AND as in:
if(m!=' '&& m!='\n')
Also, outside the loop, write,
X[i] = '\0';
Lets look into the if condition
if(m!=' '||m!='\n')
1. When m is space m=' ' (i.e. ASCII value 32)
m=' '
As per your if condition (Cond1 || Cond2), Cond1 will fail and o/p will be 0 but Cond2 will be TRUE, because it is not ' '.
if(FALSE || TRUE)
will be if(TRUE).
When your input is newline (i.e ASCII value 10).
m='\n'
Here Cond1 will be TRUE because it is not SPACE, due to this it will not check the second condition as per C. and will execute the if(TRUE) statement.
Please try to code it by yourself.... It will help you to clear few C doubts and u will get to know how || and && condition works.
Better to use getchar() instead of scanf in here as you read character by character. Usually we use scanf for get strings as input.
Using the logical operators: && (AND) operator checks for one condition to be false, while or || (OR) operator checks for one condition to be true. So if you use && operator, it checks whether it is a space, and if not, it returns false, without even checking the second condition(EOF). But if you use || operator, as used below, it checks for both cases. Check for more details in operators here
You also have to increment the counter (i) after adding an item to the array (Inside the if) as if you don't, it will continuously add items to the same place of the array, which means you will lost the precious inputs.
So, here's my code:
char X[99];
char m;
printf ("Type the string without spaces\n");
for (i=0;i<99;i++) {
m = getchar();
if(m == ' ' || m=='\n') {
X[i]=m;
i++;
}
else i=99;
}
With the function getch(); it ends if your pressing space or enter!
#include <stdio.h>
int main(void) {
char m, X[99];
int i;
printf("Type the string without spaces\n");
for (i = 0; i < 99; i++) {
m = getch();
if (m == ' ' || m == '\n' || m == '\r')
i=100;
else
X[i] = m;
printf("%c", m);
}
}
try this
#include <stdio.h>
int main(){
int i;
char m, X[99];
printf("Type the string without spaces\n");
for (i=0;i<98;i++){//98 --> sizeof(X) -1
scanf("%c",&m);
if(m!=' ' && m!='\n')
X[i] = m;
else
break;
}
X[i] = '\0';
puts(X);
return 0;
}
Here's a working code:
#include <stdio.h>
int main ()
{
char x[100] = {0};
char m;
int i, j;
printf ("Type:\n");
for (i=0; i<99; i++) {
m = getchar();
getchar();
if ((m != ' ') && (m != '\n')) {
x[i] = m;
} else {
printf ("Breaking.");
break;
}
}
printf ("\n");
for (j=0; j<i; j++)
printf ("%c\n", x[j]);
return 0;
}
Here I have used an extra getchar() to consume the newline used to enter the character m. And so (please note) you will also need to enter a newline after a space (' ') and a newline ('\n') to enter and store these in m and compare them in the next lines.
First error, as everyone has pointed out is the logical operator in the if() statement. It should be
if(m!=' ' && m!='\n')
As you want to check if the entered character is neither a (space) nor a \n, so you have to use the && operator.
Next, the error you are getting is because of something called the trailing character. When you enter a letter and press enter (at the point where your scanf("%c",&m) is asking for input). That letter gets stored in the variable m, but the newline character \n caused by the enter pressed is still in the stdin. That character is read by the scanf("%c",&m) of the next iteration of the for loop, thus the loop exits.
What you need to do is consume that trailing character before the next scanf() is executed. for that you need a getchar() which does this job. So now your code becomes something like this..
#include<stdio.h>
int main()
{
char X[99];
char m;
int i;
printf("Type the string without spaces\n");
for (i=0;i<99;i++)
{
scanf("%c",&m);
if(m!=' ' && m!='\n')
{
X[i]=m;
getchar(); // this statement is consuming the left out trailing character
}
else
i=99;
}
printf("%s",X);
}
Now, the program works exactly as you want.

Putting numbers separated by a space into an array

I want to have a user enter numbers separated by a space and then store each value as an element of an array. Currently I have:
while ((c = getchar()) != '\n')
{
if (c != ' ')
arr[i++] = c - '0';
}
but, of course, this stores one digit per element.
If the user was to type:
10 567 92 3
I was wanting the value 10 to be stored in arr[0], and then 567 in arr[1] etc.
Should I be using scanf instead somehow?
There are several approaches, depending on how robust you want the code to be.
The most straightforward is to use scanf with the %d conversion specifier:
while (scanf("%d", &a[i++]) == 1)
/* empty loop */ ;
The %d conversion specifier tells scanf to skip over any leading whitespace and read up to the next non-digit character. The return value is the number of successful conversions and assignments. Since we're reading a single integer value, the return value should be 1 on success.
As written, this has a number of pitfalls. First, suppose your user enters more numbers than your array is sized to hold; if you're lucky you'll get an access violation immediately. If you're not, you'll wind up clobbering something important that will cause problems later (buffer overflows are a common malware exploit).
So you at least want to add code to make sure you don't go past the end of your array:
while (i < ARRAY_SIZE && scanf("%d", &a[i++]) == 1)
/* empty loop */;
Good so far. But now suppose your user fatfingers a non-numeric character in their input, like 12 3r5 67. As written, the loop will assign 12 to a[0], 3 to a[1], then it will see the r in the input stream, return 0 and exit without saving anything to a[2]. Here's where a subtle bug creeps in -- even though nothing gets assigned to a[2], the expression i++ still gets evaluated, so you'll think you assigned something to a[2] even though it contains a garbage value. So you might want to hold off on incrementing i until you know you had a successful read:
while (i < ARRAY_SIZE && scanf("%d", &a[i]) == 1)
i++;
Ideally, you'd like to reject 3r5 altogether. We can read the character immediately following the number and make sure it's whitespace; if it's not, we reject the input:
#include <ctype.h>
...
int tmp;
char follow;
int count;
...
while (i < ARRAY_SIZE && (count = scanf("%d%c", &tmp, &follow)) > 0)
{
if (count == 2 && isspace(follow) || count == 1)
{
a[i++] = tmp;
}
else
{
printf ("Bad character detected: %c\n", follow);
break;
}
}
If we get two successful conversions, we make sure follow is a whitespace character - if it isn't, we print an error and exit the loop. If we get 1 successful conversion, that means there were no characters following the input number (meaning we hit EOF after the numeric input).
Alternately, we can read each input value as text and use strtol to do the conversion, which also allows you to catch the same kind of problem (my preferred method):
#include <ctype.h>
#include <stdlib.h>
...
char buf[INT_DIGITS + 3]; // account for sign character, newline, and 0 terminator
...
while(i < ARRAY_SIZE && fgets(buf, sizeof buf, stdin) != NULL)
{
char *follow; // note that follow is a pointer to char in this case
int val = (int) strtol(buf, &follow, 10);
if (isspace(*follow) || *follow == 0)
{
a[i++] = val;
}
else
{
printf("%s is not a valid integer string; exiting...\n", buf);
break;
}
}
BUT WAIT THERE'S MORE!
Suppose your user is one of those twisted QA types who likes to throw obnoxious input at your code "just to see what happens" and enters a number like 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 which is obviously too large to fit into any of the standard integer types. Believe it or not, scanf("%d", &val) will not yak on this, and will wind up storing something to val, but again it's an input you'd probably like to reject outright.
If you only allow one value per line, this becomes relatively easy to guard against; fgets will store a newline character in the target buffer if there's room, so if we don't see a newline character in the input buffer then the user typed something that's longer than we're prepared to handle:
#include <string.h>
...
while (i < ARRAY_SIZE && fgets(buf, sizeof buf, stdin) != NULL)
{
char *newline = strchr(buf, '\n');
if (!newline)
{
printf("Input value too long\n");
/**
* Read until we see a newline or EOF to clear out the input stream
*/
while (!newline && fgets(buf, sizeof buf, stdin) != NULL)
newline = strchr(buf, '\n');
break;
}
...
}
If you want to allow multiple values per line such as '10 20 30', then this gets a bit harder. We could go back to reading individual characters from the input, and doing a sanity check on each (warning, untested):
...
while (i < ARRAY_SIZE)
{
size_t j = 0;
int c;
while (j < sizeof buf - 1 && (c = getchar()) != EOF) && isdigit(c))
buf[j++] = c;
buf[j] = 0;
if (isdigit(c))
{
printf("Input too long to handle\n");
while ((c = getchar()) != EOF && c != '\n') // clear out input stream
/* empty loop */ ;
break;
}
else if (!isspace(c))
{
if (isgraph(c)
printf("Non-digit character %c seen in numeric input\n", c);
else
printf("Non-digit character %o seen in numeric input\n", c);
while ((c = getchar()) != EOF && c != '\n') // clear out input stream
/* empty loop */ ;
break;
}
else
a[i++] = (int) strtol(buffer, NULL, 10); // no need for follow pointer,
// since we've already checked
// for non-digit characters.
}
Welcome to the wonderfully whacked-up world of interactive input in C.
Small change to your code: only increment i when you read the space:
while ((c = getchar()) != '\n')
{
if (c != ' ')
arr[i] = arr[i] * 10 + c - '0';
else
i++;
}
Of course, it's better to use scanf:
while (scanf("%d", &a[i++]) == 1);
providing that you have enough space in the array. Also, be careful that the while above ends with ;, everything is done inside the loop condition.
As a matter of fact, every return value should be checked.
scanf returns the number of items successfully scanned.
Give this code a try:
#include <stdio.h>
int main()
{
int arr[500];
int i = 0;
int sc = 0; //scanned items
int n = 3; // no of integers to be scanned from the single line in stdin
while( sc<n )
{
sc += scanf("%d",&arr[i++]);
}
}

Resources