Why we need getchar() although using scanf()? - c

this code should read a positive number and if user enter a non-numeric value, it asking him again to enter a number and wait the input to check again till entering a number
do
{
printf("Enter a positive number: ");
}
while ( (scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0) ) ;
but actually when entering non-numeric it keeps doing the body of the while loop without reading the waiting to check the condition of the while loop while(scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0)
when edit the condition to
int clean_stdin()
{
while (getchar()!='\n');
return 1;
}
do
{
printf("Enter a positive number: ");
}
while ( (scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0) && clean_stdin() ) ;
It executes in the right way, but I don't understand why we need to add getchar() although we already use scanf() in the condition

When scanf("%d%c", ... encounters non-numeric input, the "%d" causes the scanning to stop and the offending character to remain in stdin for the next input function. The "%c" does not get a chance to read that non-numeric character.
If code re-reads stdin with the same scanf("%d%c", ..., the same result. Some other way is needed to remove the non-numeric input. getchar(), getch(), etc. will read any 1 character.
Example code GetPositiveNumber()

Consider using fgets to collect input. Any invalid input is already removed from the input stream making it simpler to try again.
Parse the input with sscanf or others as needed.
char input[40] = "";
int valid = 0;
int n = 0;
do {
printf ( "Enter a positive integer\n");
if ( fgets ( input, sizeof ( input), stdin)) {
int last = 0;
if ( 1 == ( valid = sscanf ( input, "%d%n", &n, &last))) {
if ( n < 0 || input[last] != '\n') {
valid = 0;//reset if negative or last is not newline
}
}
}
else {
fprintf ( stderr, "problem getting input from fgets\n");
return 0;
}
} while ( valid != 1);

Because scanf() with most specifiers ignores white spaces, and you need to collect them before calling scanf() again if a '\n' is left in the buffer. If you don't collect them (and discard them immediately), then scanf() will return immediately on the next call.

See this code
#include <stdio.h>
int
main(int argc, char* argv[]) {
char c = 0;
scanf("%c", &c);
printf("%c\n", c);
scanf("%c", &c);
printf("%c\n", c);
return 0;
}
If you type over two or three characters, second call of scanf doesn't work as interructive. And getch() is not standard. Also you shouldn't call getchar() since it may block. You can use fseek.
Hack using fseek works only on Windows. :-(
BAD SOLUTION
#include <stdio.h>
int
main(int argc, char* argv[]) {
char c = 0;
scanf("%c", &c);
printf("%c\n", c);
fseek(stdin, 0, SEEK_END);
scanf("%c", &c);
printf("%c\n", c);
return 0;
}
GOOD SOLUTION
#include <stdio.h>
int
main(int argc, char* argv[]) {
char buf[BUFSIZ];
char c = 0;
scanf("%c", &c);
printf("%c\n", c);
while (!feof(stdin) && getchar() != '\n');
scanf("%c", &c);
printf("%c\n", c);
return 0;
}
This works good on Windows & Linux

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

Scanf returns 0 without waiting for input

I have never programmed in C and today I have to write small code. Program is very easy - I want to add two integers.
But when I'm trying to check if given input is a number and first scanf returns 0, the second one returns 0 too without waiting for input.
Code:
int main()
{
int a = 0;
int b = 0;
printf("Number a:\n");
if (scanf("%d", &a) != 1)
{
printf("Not a number. a=0!\n");
a = 0;
}
printf("Number b:\n");
if (scanf("%d", &b) != 1)
{
printf("Not a number. b=0!\n");
b = 0;
}
printf("%d\n", a+b);
return 0;
}
The input that failed to convert to a number for the first fscanf() is still pending in standard input's buffer and causes the second fscanf() to fail as well. Try discarding offending input and re-prompting the user:
#include <stdio.h>
int main(void) {
int a = 0;
int b = 0;
int c;
printf("Number a:\n");
while (scanf("%d", &a) != 1) {
printf("Not a number, try again:\n");
while ((c = getchar()) != EOF && c != '\n')
continue;
if (c == EOF)
exit(1);
}
printf("Number b:\n");
while (scanf("%d", &b) != 1) {
printf("Not a number, try again:\n");
while ((c = getchar()) != EOF && c != '\n')
continue;
if (c == EOF)
exit(1);
}
printf("%d\n", a + b);
return 0;
}
Factorizing the code with a utility function makes it much clearer:
#include <stdio.h>
int get_number(const char *prompt, int *valp) {
printf("%s:\n", prompt);
while (scanf("%d", valp) != 1) {
printf("Not a number, try again:\n");
while ((c = getchar()) != EOF && c != '\n')
continue;
if (c == EOF)
return 0;
}
return 1;
}
int main(void) {
int a, b;
if (!get_number("Number a", &a) || !get_number("Number b", &b)) {
return 1;
}
printf("%d\n", a + b);
return 0;
}
That is because, once the first scanf() failed, it is probably because of matching failure, and the input which caused the matching failure, remains inside the input buffer, waiting to be consumed by next call.
Thus, the next call to scanf() also try to consume the same invalid input residing in the input buffer immediately, without waiting for the explicit external user input as the input buffer is not empty.
Solution: After the first input fails for scanf(), you have to clean up the input buffer, for a trivial example, something like while (getchar() != '\n'); should do the job.
This happens if there is any input from the previous entry, can take that, and skip input from the user. In the next scanf also It takes the new line which is left from the last scanf statement and automatically consumes it. That's what happening in your code.
You can clear previous input in stdin stream by using fflush(stdin); before starting your program.
This can also be solved by leaving a space before % i.e scanf(" %d",&n);,Here leaving whitespace ensures that the previous new line is ignored.
Or we can use getc(stdin) before calling any scanf statement, Sometimes it helps very much.
int main()
{
int a = 0;
int b = 0;
printf("Number a:");
//getc(stdin);
if (scanf(" %d", &a) != 1)
{
printf("Not a number. a=0!\n");
a = 0;
}
printf("Number b:\n");
//getc(stdin);
if (scanf(" %d", &b) != 1)
{
printf("Not a number. b=0!\n");
b = 0;
}
printf("%d\n", a+b);
return 0;
}
It's because of input and output aren't synchronized in C. The program can output some lines after user's input while the input hasn't been read. Try to run this code:
char token;
scanf("%c", &token);
printf("%c\n", token);
printf("line 1\n");
scanf("%c", &token);
printf("%c\n", token);
printf("line 2\n");
scanf("%c", &token);
printf("%c\n", token);
printf("line 3\n");
And input abc in one line.
You can imagine this like there are two separated consoles, one for input and another for output.
For example you want to input asd for a and 3 for b. In this case, the first scanf won't find any number and will return 0. But it also won't read anything from the input. Because of this, the second scanf will see asd too.
To clear the input if a isn't a number, you should input all remaining chars in the line until '\n' (look at the #Sourav's solution)
You could do the same thing using strings without problems with scanf. Just take the user input as string and convert it to integer. Then convert the string back to integer to check whether they are the same. If they are the same, treat a and b as integers, if not, do what you do (You will need to include string.h). Here is the working code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int a;
int b;
char str1[100];
char str2[100];
printf("Number a:\n");
scanf("%s", &str1); //take input as a string
a = atoi(str1); //convert string to integer
snprintf(str2, 100, "%d", a); //convert integer back to string
//if the integer converted to string is not the same as the string converted to integer
if (strcmp(str1, str2)!= 0)
{
printf("Not a number. a=0!\n");
a = 0;
}
printf("Number b:\n");
scanf("%s", &str1);
b = atoi(str1);
snprintf(str2, 100, "%d", b);
if (strcmp(str1, str2)!= 0)
{
printf("Not a number. b=0!\n");
b = 0;
}
printf("%d\n", a+b);
return(0);
}

Using character for sentinel but receiving pointer error

I'm trying to program a loop that counts characters until it receives a certain sentinel value. The sentinel value is supposed to be a #, but I've also tried a 0 and a Z and had the same response.
When it compiles, I receive "warning: comparison between pointer and integer" for lines 16 (the line that calls the sentinel.)
If I don't define the sentinel, but instead rely on logical operators in the while statement, then I receive no error, but have an endless loop.
Thanks!
#include <stdio.h>
int main()
{
#define SENTINEL '#'
char ch;
int chcount;
printf("Enter your text, terminate with a #:");
scanf("%s", &ch);
chcount = 0;
while (ch != SENTINEL)
{
if ((ch >= 'A') && (ch <= 'Z'))
{
chcount = chcount +1;
printf("You have entered %d characters", chcount);
}
}
return(0)
}
With the %s format specifier, scanf expects the address of a char buffer, where the string you type will be copied.
And you gave the address &ch of a single char, which is obviously not enough to contain a "word" from input with its terminating null character.
Moreover, your loop reads no input from the user. Thus the endless loop.
This is because the way you use scanf(), with %s format specifier you are writing to a char*, not the char ch (as you've declared). In order to write to a single char variable, you should use a %c format specifier.
To fix this you should either use f.e. getchar() instead of scanf() or use scanf() (and change ch to char* then) but iterate over scanned string to check whether there is #.
I would recommend the first solution.
The while loop never ends so I changed your while loop.
I tried to change your program to:
#include <stdio.h>
#define SENTINEL '#'
int main()
{
char ch;
int chcount;
printf("Enter your text, terminate with a #:");
chcount = 0;
while ((ch = getchar()) != SENTINEL)
{
if ((ch >= 'A') && (ch <= 'Z'))
{
chcount = chcount + 1;
printf("You have entered %d characters\n", chcount);
}
}
return(0);
}
Some issues I found with your code:
scanf("%s", &ch);
It should be
scanf("%c", &ch);
Next, semicolon missing here: return(0);
However, since your aim is:
I'm trying to program a loop that counts characters until it receives a certain sentinel value. The sentinel value is supposed to be a #
I suggest moving your scanf() inside while loop:
#include <stdio.h>
int main()
{
#define SENTINEL '#'
char ch='0';
int chcount;
printf("Enter your text, terminate with a #:");
chcount = 0;
int i=0;
while (ch != SENTINEL)
{ scanf("%c", &ch);
if ((ch >= 'A') && (ch <= 'Z'))
{
chcount = chcount +1;
printf("You have entered %d characters", chcount);
i++;
}
}
return(0);
}
here is a working version of the posted code.
It contains numerous corrections.
Corrections include consistent/usable indentation and logic corrections
Note: not all implementations have the getline() function
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
int sentinelFound = 0;
#define SENTINEL '#'
char* line = NULL;
size_t lineLen = 0;
printf("Enter your text, terminate with a #:");
int chcount;
getline(&line, &lineLen, stdin );
size_t i;
for( i=0; i<lineLen; i++)
{
if( SENTINEL == line[i] )
{
sentinelFound = 1;
break;
}
if ((line[i] >= 'A') && (line[i] <= 'Z')) // only count capital letters
{
chcount = chcount +1;
}
}
free( line );
if( !sentinelFound )
printf( "You did not enter the sentinel character!" );
else
printf("You have entered %d capital characters\n", chcount);
return(0);
} // end function: main

String length Error

I am reading the string from the stdin using fgets function and then trying to print the length of the string, But I am always getting the length of the string as 1 always for the first time
Here is my code
#incldue<stdio.h>
#include<string.h>
int main(void)
{
printf("\n Enter the no of test cases");
scanf("%d",&t);
int i,j;
for(i=0;i<t;++i)
{
char song[500],val[28];
int k=0,x=0;
fgets(song,500,stdin);
int len=strlen(song);
printf("\nlen=%d",len);
}
return 0;
}
I am always getting 1 as the length for the first test case :/
Please suggest where i am going wrong
You are not clearing the input buffer. After giving the input value to first scanf newline will be there. So fgets will not get the input from the user.
Newline will be placed in that buffer in a first(song[0]) position. So this is the reason strlen returns as value 1.
Make this line before the fgets.
int c;
if ( i == 0 )
while((c=getchar()) != '\n' && c != EOF );
fgets(song,500,stdin);
Or else place this line after getting the input from the scanf.
scanf("%d",&t);
while((c=getchar()) != '\n' && c != EOF );
Include \n in scanf input string (and in C declare variables at the beginning of the block { }).
Also notice the len will include the \n char.
#include<stdio.h>
#include<string.h>
int main(void)
{
int t, i;
printf("Enter the no of test cases: ");
scanf("%d\n",&t);
for(i=0;i<t;++i) {
char song[500];
int len;
fgets(song,500,stdin);
len=strlen(song);
printf("len=%d\n",len);
}
return 0;
}
update
If you need to handle weird input just use fgets (\n removed from len).
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(void)
{
char song[500];
int t, i, len;
printf("Enter the no of test cases: ");
fgets(song,500,stdin);
t = atoi(song);
for(i=0;i<t;++i) {
fgets(song,500,stdin);
if ((len=strlen(song)) > 0) {
song[--len] = '\0';
printf("len=%d\n",len);
}
}
return 0;
}
When using scanf (or its relatives), it is important to check the return of the function. scanf returns the number of input values correctly matched and assigned. If there are inappropriate characters or insufficient characters, scanf will experience a matching or input failure. A quick if statement will suffice:
if (!scanf ("%d", &t)) {
fprintf (stderr, "error: invalid type or number for test cases.\n");
return 1;
}
As also noted, fgets will read and include in song the trailing newline character. Generally, you will want to remove the trailing newline to prevent having stray newlines scattered through various strings within your code. (not to mention looking at a length=5 for data is a bit strange) A simple method for removing the newline after your call to fgets is:
len = strlen (song);
while (len && song[len-1] == '\n') /* strip newline */
song[--len] = 0;
Putting together the test of scanf return, emptying the input buffer, and stripping the newline after fgets, your code would look similar to:
#include <stdio.h>
#include <string.h>
int main (void)
{
int c = 0;
int i = 0;
int t = 0;
printf ("\n Enter the no of test cases: ");
if (!scanf ("%d", &t)) {
fprintf (stderr, "error: invalid type or number for test cases.\n");
return 1;
}
while ((c = getchar()) != '\n' && c != EOF);
for (i = 0; i < t; ++i) {
char song[500] = { 0 };
size_t len = 0;
if (printf ("\n case [%d] : ", i) && fgets (song, 500, stdin))
{
len = strlen (song);
while (len && song[len-1] == '\n') /* strip newline */
song[--len] = 0;
}
printf (" len : %zu\n", len);
}
printf ("\n");
return 0;
}
Output
$ ./bin/scanf_rd_int
Enter the no of test cases: 2
case [0] : this is case one - 28 chars.
len : 28
case [1] : this is case two -- 29 chars.
len : 29

Resources