C - scanf waits until all parts in format can be evaluated - c

I don't have a lot of time, and a lot of the stuff about stdin just raises a lot more questions than it answers at this stage. I was hoping to test for multiple types of commands passed with mutliple instances of scanf.
If I do this:
char inputChar;
char inputChars[15] = { NULL };
int inputInt;
double inputDec;
if(scanf("%c %d %lf",&inputChar,&inputInt,&inputDec) == 3) {
...
}
else if(scanf("%c %d %s",&inputChar,&inputInt,&inputChars) == 3) {
...
}
else if(scanf("%c %d",&inputChar,&inputInt) == 2) {
...
}
else if(scanf("%c",&inputChar) == 1) {
...
}
else {
...
}
And then type a single character and hit Enter, the console waits until I enter another value before assessing whether or not there's a match.
Update
This seems to work fine, except that it expects your input to the perfect whenever it's used. If a user types aa then changes it to a 10 before hitting Enter, it matches the 4th evaluation. It's wrong for two reasons:
Because it should match the 3rd if it's capturing input correctly; and
Because if it doesn't, aa should be filtered into the 5th evaluation.
Revised code:
char input[50] = { NULL };
char inputChar = NULL;
char inputChars[15] = { NULL };
int inputInt;
double inputDec;
printf("Input String:\n>");
fgets(input,sizeof(input),stdin);
if(sscanf(input,"%c%d %lf",&inputChar,&inputInt,&inputDec) == 3) { }
else if(sscanf(input,"%c%d%s",&inputChar,&inputInt,&inputChars) == 3) { }
else if(sscanf(input,"%c%d",&inputChar,&inputInt) == 2) { }
else if(sscanf(input,"%c",&inputChar) == 1) { }
else { }
Doing something like this shows that backspace is not being filtered out:
for(int i=0;i<50;i++) {
if(input[i] == (char) 10) { break; }
printf("\n%c %d",(char) input[i],(int) input[i]);
}
So it appears fgets is out of the picture.

The statements are executed in order. So it's the first scanf call that waits for more input. If the input doesn't match the first call, then the next scanf call will start all over, waiting for input. And so on.
Instead you should use fgets to read the whole line, and then use sscanf on the line.

Please explain what "clears the input buffer" means. Do you mean _flushall erases my keystrokes before they occur? How could it do this? Perhaps you mean that you've already read the data that you need from the line, and you don't care about the rest of the line. In that case, I assume you wish to "read and discard all characters up to and including the next newline". I have a portable mechanism to do that:
for (int c = getchar(); c >= 0 && c != '\n'; c = getchar());
You may also wish to read this related answer.
edit: It has just occured to me that the behaviour you desire might be expressed as follows:
char inputChar;
char inputChars[15] = { NULL };
int inputInt;
double inputDec;
int x = scanf("%c%d", &inputChar, &inputInt);
if (x == 2 && scanf("%lf",&inputDec) == 1) {
...
}
else if (x == 2 && scanf("%14s", inputChars) == 1) {
...
}
else {
...
}

Probably not the best way to go about it but it works.
Using the iostream library run getline(std::cin,input); to capture input to std::string input then use sscanf however many times on input.c_str().

Related

How to check if scanf("%s", &var) is a number, and thus turn it into an integer

I have this code and I need help converting the comments to c code
// if the input of scanf() is "q"
{
break;
}
else
{
// convert to int
}
Firstly, how do I check if an input is a certain character. Secondly, how do I turn a string into an integer. Example: "123" -> 123
Things I've tried, that didn't work: (it is possible that I implemented these solutions incorrectly)
how does scanf() check if the input is an integer or character?
Convert char to int in C and C++
I am not using any standard libraries except for stdio.h to print some logging information on the window
you have to know also that any string is terminated by null character which is '\0' to indicate the termination of the string , also you have to check is the user entered characters not numbers and so on (that's not implemented in this code).
I also handled if negative numbers are entered.
but you have to handle if the user entered decimals numbers , to sum up . there are so many cases to handle.
and here the edited code :
#include <stdio.h>
int main(){
char inputString[100];
printf("enter the input:\n");
scanf("%s", &inputString);
if(inputString[0] == 'q' && inputString[1] == '\0' )
{
printf("quiting\n");
//break;
}
else {
int i = 0;
int isNegative = 0;
int number = 0;
// check if the number is negative
if (inputString[0] == '-') {
isNegative = 1;
i = 1;
}
// convert to int
for ( ;inputString[i] != '\0' ; i++) {
number *= 10;
number += (inputString[i] - '0');
}
if(isNegative == 1)
number *= -1;
printf("you entered %d\n", number);
}
return 0;
}
The fundamental question here is, Do you want to use scanf?
scanf is everyone's favorite library function for easily reading in values. scanf has an input specifier, %d, for reading in integers.
And it has a different input specifier, %s, for reading in arbitrary strings.
But scanf does not have any single input specifier that means, "Read in an integer as an integer if the user types a valid integer, but if the user types something like "q", have a way so I can get my hands on that string instead."
Unless you want to move mountains and implement your own general-purpose input library from scratch, I think you have basically three options:
Use scanf with %d to read integers as integers, but check scanf's return value, and if scanf fails to read an integer, use that failure to terminate input.
Use scanf with %s to read the user's input as a string, so you can then explicitly test if it's a "q" or not. If not, convert it to an integer by hand. (More on this below.)
Don't use scanf at all. Use fgets to read the user's input as a whole line of text. Then see if it's a "q" or not. If not, convert it to an integer by hand.
Number 1 looks something like this:
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(scanf("%d", &i) != 1) {
/* end of input detected */
break;
}
do something with i value just read;
}
The only problem with this solution is that it won't just stop if the user types "q", as your original problem statement stipulated. It will also stop if the user types "x", or "hello", or control-D, or anything else that's not a valid integer. But that's also a good thing, in that your loop won't get confused if the user types something unexpected, that's neither "q" nor a valid integer.
My point is that explicitly checking scanf's return value like this is an excellent idea, in any program that uses scanf. You should always check to see that scanf succeeded, and do something different if it fails.
Number 2 would look something like this:
char tmpstr[20];
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(scanf("%19s", tmpstr) != 1) {
printf("input error\n");
exit(1);
}
if(strcmp(tmpstr, "q") == 0) {
/* end of input detected */
break;
}
i = atoi(tmpstr); /* convert string to integer */
do something with i value just read;
}
This will work well enough, although since it uses atoi it will have certain problems if the user types something other than "q" or a valid integer. (More on this below.)
Number 3 might look like this:
char tmpstr[20];
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(fgets(tmpstr, 20, stdin) == NULL) {
printf("input error\n");
exit(1);
}
if(strcmp(tmpstr, "q\n") == 0) {
/* end of input detected */
break;
}
i = atoi(tmpstr); /* convert string to integer */
do something with i value just read;
}
One thing to note here is that fgets includes the newline that the user typed in the string it returns, so if the user types "q" followed by the Enter key, you'll get a string back of "q\n", not just "q". You can take care of that either by explicitly looking for the string "q\n", which is kind of lame (although it's what I've done here), or by stripping the newline back off.
Finally, for both #2 and #3, there's the question of, what's the right way to convert the user's string to an integer, and what if it wasn't a valid integer? The easiest way to make the conversion is to call atoi, as my examples so far have shown, but it has the problem that its behavior on invalid input is undefined. In practice, it will usually (a) ignore trailing nonnumeric input and (b) if there's no numeric input at all, return 0. (That is, it will read "123x" as 123, and "xyz" as 0.) But this behavior is not guaranteed, so these days, most experts recommend not using atoi.
The recommended alternative is strtol, which looks like this:
char *endp;
i = strtol(tmpstr, &endp, 10); /* convert string to integer */
Unlike atoi, strtol has guaranteed behavior on invalid input. Among other things, after it returns, it leaves your auxiliary pointer endp pointing at the first character in the string it didn't use, which is one way you can determine whether the input was fully valid or not. Unfortunately, properly dealing with all of the ways the input might be invalid (including trailing garbage, leading garbage, and numbers too big to convert) is a surprisingly complicated challenge, which I am not going to belabor this answer with.
Here are some guidelines:
scanf("%s", &var) is incorrect: you should pass the maximum number of characters to store into the array var and pass the array without the & as it will automatically convert to a pointer to its first element when passed as an argument:
char var[100];
if (scanf("%99s", var) != 1) {
printf("premature end of file\n");
return 1;
}
to compare the string read to "q", you can use strcmp() declared in <string.h>:
if (strcmp(var, "q") == 0) {
printf("quitting\n");
return 0;
}
to convert the string to the number it represents, use strtol() declared in <stdlib.h>:
char *p;
long value = strtol(var, &p, 0);
testing for a proper conversion is tricky: strtol() updated p to point to the character after the number and set errno in case of range error:
errno = 0;
char *p;
long value = strtol(var, &p, 0);
if (p == var) {
printf("not a number: %s\n", p);
return 1;
}
if (*p != '\0') {
printf("extra characters: %s\n", p);
return 1;
}
if (errno) {
printf("conversion error: %s\n", strerror(errno));
return 1;
}
printf("the number entered is: %ld\n", value);
return 0;
Here is a complete program:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char var[100];
char *p;
long value;
printf("Enter a number: ");
if (scanf("%99s", var) != 1) {
printf("premature end of file\n");
return 1;
}
if (strcmp(var, "q") == 0) {
printf("quitting\n");
return 0;
}
errno = 0;
value = strtol(var, &p, 0);
if (p == var) {
printf("not a number: %s\n", p);
return 1;
}
if (*p != '\0') {
printf("extra characters: %s\n", p);
return 1;
}
if (errno) {
printf("conversion error: %s\n", strerror(errno));
return 1;
}
printf("the number entered is: %ld\n", value);
return 0;
}
You can try this: (Assuming only positive integers needs to convert)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// Write C code here
char var[100];
int numb_flag=1, i=0,number=0;
scanf("%s",var);
while(var[i]!='\0') { // Checking if the input is number
if(var[i]>=48 && var[i]<=57)
i++;
else {
numb_flag = 0;
break;
}
}
if(numb_flag==1) {
number = atoi(var);
printf("\nNumber: %d",number);
} else {
printf("\nNot A Number");
}
return 0;
}
//Mind that in order to be more precise you could also use atof().
//The function works the same way as atoi() but is able to convert float
// (returned value is 'double') the string s
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXILEN 100 /*change the value based on your needs*/
int main(){
int n, lim = MAXILEN;
char s[MAXILEN], *p = s, c;
/*taking the string from the input -- I do not use scanf*/
while(--lim > 0 && (c = getchar()) != EOF && c != '\n')
*p++ = c;
//here you can also do the check (if you want the '\n' char):
//if(c == '\n')
// *s++ = c;
*p = '\0';
if(s[0] == 'q' && s[1] == '\0')
exit(EXIT_SUCCESS); /*change the argument based on your needs*/
else
n = atoi(s);
printf("[DEBUG]: %d\n", n);
}

Using the Scanf function for inputs for different lengths

I want to use the scanf function to get inputs that are either two numbers, or one number from the user and put them in an array. However, I'm not sure how I can use this same function to get inputs for both one element of the array, and also two elements of the array.
i.e. if the user enters, 9 0, it should be able to successfully store that in an array and move onto new code, or if the user enters something like 1, it should also be able to successfully store that in the array, and move onto new code.
I've tried putting the scanf into a while loop:
int scanned_array[2] = {};
int element = 0;
while(scanf("%d", &scanned_array[element]) {
//... more code here which will have different functions depending on the input...
element++;
}
How can I successfully do this while only using the scanf function, while loops, arrays and if statements?
**Edit: Just wondering, if I changed my code according to what was suggested below by #nmgari, how could I go to if(num == 1) { ... without having to press ctrl+d?
Thanks for reading!
If i understand correctly you want to call scanf() once and read either one number or two.
Something like this should work:
int scanned_array[2];
int num_input = 0;
num_input = scanf("%d %d", &scanned_array[0], &scanned_array[1]);
if(num_input == 1)
{
//Do something
}
else if (num_input == 2)
{
//Do somethine else
}
Anyway, you need to consider that the second element in the array may never get a value.
You should read the input line with fgets() and parse it with sscanf():
char buf[120];
if (fgets(buf, sizeof buf, stdin)) {
int i1, i2;
switch (sscanf(buf, "%d%d", &i1, &i2)) {
case 2:
/* the user entered 2 integers */
handle_2_integers(i1, i2);
break;
case 1:
/* the user entered a single integer */
handle_1_integer(i1);
break;
default:
/* the user did not enter a number */
handle_invalid_input();
break;
}
} else {
/* the input stream is at end of file or encountered an error */
handle_end_of_file();
}
If you run scanf("%d%d", &i1, &i2), scanf() will keep reading the input stream until either end of file, or 2 integers have been read or an character has been input that cannot be converted to an integer, possibly reading multiples lines of input for one of these cases to occur. If you want to handle a single line of input, you cannot use scanf() directly.
A separate scan for whitespace could be used.
The scanset %1[ \t\r\n] will capture whitespace character by character. The loop will exit on non-whitespace or a newline.
Then try to scan an integer.
#include <stdio.h>
#include <stdlib.h>
int main ( void) {
char space[2] = "";
int scanned = 0;
int scanned_array[2] = { 0};
while ( 1) {
while ( 1 == scanf ( "%1[ \t\r\n]", space)) {
if ( '\n' == space[0]) {
break;
}
}
if ( '\n' == space[0]) {
break;
}
if ( 1 != scanf ( "%d", &scanned_array[scanned])) {
fprintf ( stderr, "bad input\n");
return 1;
}
++scanned;
if ( 2 <= scanned) {
break;
}
}
for ( int each = 0; each < scanned; ++each) {
printf ( "%d\n", scanned_array[each]);
}
return 0;
}

C: Break Loop on Empty Input

So I'm writing a program that will loop forever, accepting string inputs until the user just presses enter with no string (along the way, I'm tracking the longest/shortest strings entered). I have this loop:
char stringIn[1000] = {'\0'};
while(1) {
scanf("%[^\n]s", stringIn);
if(stringIn[0] == '\0') {
break;
}
if(strlen(stringIn) > strlen(longString)) {
longString == stringIn;
} else if (strlen(stringIn) < strlen(shortString)) {
shortString == stringIn;
}
i++;
}
Currently this just loops forever. I'm still really new to C, but to me this looks like it should've worked.
Points to note:
You probably mistook the == operator for =, which is assignment. Even so, it wouldn't work because here it would only copy addresses of buffers (which get overwritten) (actually in my code it would throw a compile time errors). For copying strings you wanna use strcpy.
scanf is pretty vulnerable to buffer overflows and leaves the delimiter in the buffer. fgets is a much better choice for reading lines as it takes a buffer length as argument (check this out).
scanf fills a number of items in it's list until characters matching the format string are read. If no chars match, then it doesn't fill stringIn, and hence doesn't append a '\0' at the end, and that's why your code never goes to break;. Instead we can use the return value, which is the number of items of the list that it fills (see here).
Anyway, here is code that does what you want:
int main() {
char stringIn[1000] = "";
char longString[2000] = "", shortString[2000] = "";
int read, firstFlag = 0;
while(1) {
read = scanf("%[^\n]", stringIn);
if (read == 0) {
break;
}
// to consume the '\n' left by scanf in the buffer
getchar();
if (!firstFlag || strlen(stringIn) > strlen(longString)) {
strcpy(longString, stringIn);
}
if (!firstFlag || strlen(stringIn) < strlen(shortString)) {
strcpy(shortString, stringIn);
}
firstFlag = 1;
}
printf("%s, %s\n", longString, shortString);
return 0;
}
UPDATE: Edited according to Jonathan Leffler's comment above, correcting the use of the scanset.

scanf isn't executing on every loop iteration

I am writing a program for fun (not for school), and am having a hard time figuring out why the scanf function isn't executing on every iteration of my loop - I've toyed with both 'for' loops and 'while' loops.
I know that depending on how I write the scanf function (i.e. scanf("%s", &variablename); VS scanf("%99[^\n]s", &variablename);) makes a difference, but I have tried everything and I'm desperate!
When I do a printf check on my input from the scanf, on every iteration it is only intaking one string per iteration, so if I enter two words in my first input, then it takes two iterations to process - one word per. Here is the segment of code I'm describing:
int main(void){
int tries = 0;
int score = 0;
char question[100];
char useranswer[100];
const char *phrase = {"our favorite saying\0"};
printf("\nQuestion #3 (10 points): What is our secret saying?\n");
sleep(1);
tries = 1;
while (tries<=3){
printf("YOUR ANSWER:");
scanf("%s[^\n]", useranswer);
if(strncmp(useranswer, phrase, 15) != 0){
printf ("Nope, try again!\n");
printf("You have used %d out of 3 tries!\n", tries);
if (tries == 2){
printf("Here's your final hint:xxx...\n");
}
if (tries == 3){
printf("You didn't get it. The answer is: our favorite saying!\n");
}
tries++;
}
if (strncmp(useranswer, phrase, 15) == 0){
printf("Damn, you're good. Well done.\n");
score += 10;
break;
}
}
The output of this code is:
Question #3 (10 points): What is our secret saying?
YOUR ANSWER:our favorite saying
Nope, try again!
You have used 1 out of 3 tries!
YOUR ANSWER:Nope, try again!
You have used 2 out of 3 tries!
Here's your final hint:xxx...
YOUR ANSWER:Nope, try again!
You have used 3 out of 3 tries!
You didn't get it. The answer is: our favorite saying!
(It only allowed me to input once, and I typed "our favorite saying".)
In comments you could find why your format specifier in scanf doesn't work.
An alternative is to use fgets instead, maybe in an helper function which takes care of some of the corner cases that can arise while reading user input:
#include <ctype.h>
char *read_line( char *buf, size_t n, FILE *pfin )
{
ssize_t length = 0;
int ch;
if ( !buf || n == 0 )
return NULL;
/* Consume trailing control characters, like '\0','\n', '\r', '\f'...
but also '\t'. Note that ' ' is not skipped. */
while ( (ch = fgetc(pfin)) != EOF && iscntrl(ch) ) { }
if ( ch == EOF )
return NULL;
/* At least one char is printable */
*buf = ch;
++length;
/* Read from file till a newline or up to n-2 chars. The remaining chars
are left in the stream buffer. Return NULL if no char is read. */
if ( fgets(buf + 1, n - 1, pfin) )
{
/* Trim the string to the first control character */
while ( !iscntrl(buf[length]) )
{
++length;
}
buf[length] = '\0';
}
return buf;
}
I'd change the following logic too. OP uses strncmp(useranswer, phrase, 15) multiple times, but that magic number 15 is lower then phrase's size so it ends up comparing only a substring.
while ( tries <= 3 ) {
printf("YOUR ANSWER:");
if ( !read_line(useranswer, sizeof useranswer, stdin) ) {
printf("Error: Unexpected end of input.\n");
exit(EXIT_FAILURE);
}
if( strcmp(useranswer, phrase) == 0 ) {
printf("Damn, you're good. Well done.\n");
score += 10;
break;
} else {
printf ("Nope, try again!\n");
printf("You have used %d out of 3 tries!\n", tries);
if (tries == 2) {
printf("Here's your final hint:xxx...\n");
}
if (tries == 3) {
printf("You didn't get it. The answer is: our favorite saying!\n");
}
tries++;
}
}
As a final note, I found OP declaration of phrase a bit weird (maybe a typo):
const char *phrase = {"our favorite saying\0"};
// string literals are already ^^ null terminated...
While we can use a simple array declaration, like:
const char phrase[] = "our favorite saying";
Consider also what values sizeof phrase returns in those two different cases.
Thanks to #chux for all the valuable hints and the interesting links provided:
https://stackoverflow.com/a/27729970/4944425
https://stackoverflow.com/a/28462221/4944425
And to #Dmitri for having pointed out in his comment that once we are sure that both the strings are null terminated, we can use strcmp instead of strncmp.

scanf() in a loop

I am trying to make a program where the computer has to guess a number between 0 and 100 picked by the user(me). The program should guess the number within 7 tries which it does, but I'm having a problem with scanf(). When the program tries to guess the number, the user must tell it if the number guessed is too high, too low, or correct. The program works fine when the user just types a one character response, but freaks out when there is more than one character in "response". So I would like to limit the response to just one character. How do I do this?
Thanks
#include <stdio.h>
#include <string.h>
int main() {
char response[1];
int numOfGuesses = 1;
int min = 0;
int max = 100;
int guess = (max+min)/2;
int end = 0;
do {
printf("%d) %d: (h)igh, (l)ow, or (c)orrect? ", numOfGuesses, guess);
if (scanf("%1s", response) == 1) {
printf("%s \n", response);
if (strlen(response) > 1) {
printf("D'oh! Wrong response!! \n");
} else {
if (response[0] == 'h') {
max = guess;
numOfGuesses++;
} else if (response[0] == 'l') {
min = guess;
numOfGuesses++;
} else if (response[0] == 'c') {
printf("Correct! It took %d turns. \n", numOfGuesses);
end = 1;
} else {
printf("D'oh! Wrong response! \n");
}
guess = (max+min)/2;
}
} else {
end = 1;
}
} while ( end == 0 );
return 0;
}
Your response buffer is not null terminated, which pretty much makes it explode at
if (strlen(response) > 1) {
Make it response[2].
Alternatively, you can use %c in scanf instead of %1s.
Edit:
Try calling this function after every scanf, it flushes the input buffer until the next newline, removing any invalid input after the first char.
void flush_input()
{
int c = 0;
do
{
c = getchar();
} while (c != EOF && c != '\n');
}
If you're just looking for a single character input, why not use getchar() instead of scanf()?
Here's a page that discusses getchar() in more detail and has some usage examples, if you're interested: http://rabbit.eng.miami.edu/class/een218/getchar.html
Are you getting extra points for saving memory?
#include <stdio.h> up top, then declare response to be a large-ish array: char response[BUFSIZ];
You should initialize it before the loop, just because it's a good habit: *response='\0';
As you're not doing much, you could call gets(response) instead of scanf(). This will read in the entire line entered. Modern compilers and/or run-times will whine about the gets() as it can run off the end of the provided array (which is what your scanf() was doing), suggesting fgets() instead. The more-correct snippet becomes:
fgets(response, sizeof response, stdin);
use
if (scanf("%2s", response) == 1) {
instead of
if (scanf("%1s", response) == 1) {
this makes the compiler read up to 2 characters instead of just 1 at a time

Resources