Related
I'm studying about how scanf works.
After scanned other type variable, char variable stores a white-space('\n') by getchar() or scanf("%c").
To prevent this, they should clear buffer. And I did it with rewind(stdin)
though stdin is rewinded previous input value is keeping in buffer.
and I can do something with the previous value normally.(nothing runtime errors)
but if I try scanf again, scanf will scan a new value even there is a normal value in buffer.
how does scanf determine if it should scan a new value?
I found this mechanism with below code.
#include <stdio.h>
#define p stdin
int main() {
int x;
char ch;
void* A, * B, * C, * D, * E;
A = p->_Placeholder;
printf("A : %p\n", A);//first time, it shows 0000
scanf_s("%d", &x);
B = p->_Placeholder;
printf("B : %p\n", B);//after scanned something, I think it's begin point of buffer which is assigned for this process
rewind(stdin);//rewind _Placeholder
C = p->_Placeholder;
printf("C : %p\n", C);//it outputs the same value as B - length of x
D = p->_Placeholder;
printf("D : %c\n", ((char*)D)[0]);//the previous input value is printed successfully without runtime error. it means buffer is not be cleared by scanf
scanf_s("%c", &ch, 1);//BUT scanf knows the _Placeholder is not pointing new input value, so it will scan a new value from console. How??
E = p->_Placeholder;
printf("E : %p\n", E);
printf("ch : %c\n", ch);
}
You have at least three misunderstandings:
"char variable stores a white-space"
rewind(stdin) clears the buffer
_Placeholder tells you something interesting about how scanf handles whitespace
But, I'm sorry, none of these are true.
Let's review how scanf actually handles whitespace. We start with two important pieces of background information:
The newline character, \n, is in most respects an ordinary whitespace character. It occupies space in the input buffer just like any other character. It arrives in the input buffer when you press the Enter key.
When it's done parsing a %-directive, scanf always leaves unparsed input on the input stream.
Suppose you write
int a, b;
scanf("%d%d", &a, &b);
Suppose you run that code and type, as input
12 34
and then hit the Enter key. What happens?
First, the input stream (stdin) now contains six characters:
"12 34\n"
scanf first processes the first of the two %d directives you gave it. It scans the characters 1 and 2, converting them to the integer 12 and storing it in the variable a. It stops reading at the first non-digit character it sees, which is the space character between 2 and 3. The input stream is now
" 34\n"
Notice that the space character is still on the input stream.
scanf next processes the second %d directive. It doesn't immediately find a digit character, because the space character is still there. But that's okay, because like most (but not quite all) scanf format directives, %d has a secret extra power: it automatically skips whitespace characters before reading and converting an integer. So the second %d reads and discards the space character, then reads the characters 3 and 4 and converts them to the integer 34, which it stores in the variable b.
Now scanf is done. The input stream is left containing just the newline:
"\n"
Next, let's look at a slightly different — although, as we'll see, actually very similar — example. Suppose you write
int x, y;
scanf("%d", &x);
scanf("%d", &y);
Suppose you run that code and type, as input
56
78
(where that's on two lines, meaning that you hit Enter twice).
What happens now?
In this case, the input stream will end up containing these six characters:
"56\n78\n"
The first scanf call has a %d directive to process. It scans the characters 5 and 6, converting them to the integer 56 and storing it in the variable x. It stops reading at the first non-digit character it sees, which is the newline after the 6. The input stream is now
"\n78\n"
Notice that the newline character (both newline characters) are still on the input stream.
Now the second scanf call runs. It, too, has a %d directive to process. The first character on the input stream is not a digit: it's a newline. But that's okay, because %d knows how to skip whitespace. So it reads and discards the newline character, then reads the characters 7 and 8 and converts them to the integer 78, which it stores in the variable y.
Now the second scanf is done. The input stream is left containing just the newline:
"\n"
This may all have made sense, may have seemed unsurprising, may have left you feeling, "Okay, so what's the big deal?" The big deal is this: In both examples, the input was left containing that one, last newline character.
Suppose, later in your program, you have some other input to read. We now come to a hugely significant decision point:
If the next input call is another call to scanf, and if it involves one of the (many) format specifiers that has the secret extra power of also skipping whitespace, that format specifier will skip the newline, then do its job of scanning and converting whatever input comes after the newline, and the program will work as you expect.
But if the next input call is not a call to scanf, or if it's a call to scanf that involves one of the few input specifiers that does not have the secret extra power, the newline will not be "skipped", instead it will be read as actual input. If the next input call is getchar, it will read and return the newline character. If the next input call is fgets, it will read and return a blank line. If the next input call is scanf with the %c directive, it will read and return the newline. If the next input call is scanf with the %[^\n] directive, it will read an empty line. (Actually %[^\n] will read nothing in this case, because it leaves the \n still on the input.)
It's in the second case that the "extra" whitespace causes a problem. It's in the second case that you may find yourself wanting to explicitly "flush" or discard the extra whitespace.
But it turns out that the problem of flushing or discarding the extra whitespace left behind by scanf is a remarkably stubborn one. You can't portably do it by calling fflush. You can't portably do it by calling rewind. If you care about correct, portable code, you basically have three choices:
Write your own code to explicitly read and discard "extra" characters (typically, up to and including the next newline).
Don't try to intermix scanf and other calls. Don't call scanf and then, later, try to call getchar or fgets. If you call scanf and then, later, call scanf with one of the directives (such as "%c") that lacks the "secret extra power", insert an extra space before the format specifier to cause whitespace to be skipped. (That is, use " %c" instead of "%c".)
Don't use scanf at all — do all your input in terms of fgets or getchar.
See also What can I use for input conversion instead of scanf?
Addendum: scanf's handling of whitespace can often seem puzzling. If the above explanation isn't sufficient, it may help to look at some actual C code detailing how scanf works inside. (The code I'm going to show obviously isn't the exact code that's behind your system's implementation, but it will be similar.)
When it comes time for scanf to process a %d directive, you might imagine it will do something like this. (Be forewarned: this first piece of code I'm going to show you is incomplete and wrong. It's going to take me three tries to get it right.)
c = getchar();
if(isdigit(c)) {
int intval;
intval = c - '0';
while(isdigit(c = getchar())) {
intval = 10 * intval + (c - '0');
}
*next_pointer_arg = intval;
n_vals_converted++;
} else {
/* saw no digit; processing has failed */
return n_vals_converted;
}
Let's make sure we understand everything that's going on here. We've been told to process a %d directive. We read one character from the input by calling getchar(). If that character is a digit, it's the first of possibly several digits making up an integer. We read characters and, as long as they're digits, we add them to the integer value, intval, we're collecting. The conversion involves subtracting the constant '0', to convert an ASCII character code to a digit value, and successive multiplication by 10. Once we see a character that's not a digit, we're done. We store the converted value into the pointer handed to us by our caller (here schematically but approximately represented by the pointer value next_pointer_arg), and we add one to a variable n_vals_converted keeping count of how many values we've successfully scanned and converted, which will eventually be scanf's return value.
If, on the other hand, we don't even see one digit character, we've failed: we return immediately, and our return value is the number of values we've successfully scanned and converted so far (which may well be 0).
But there is actually a subtle bug here. Suppose the input stream contains
"123x"
This code will successfully scan and convert the digits 1, 2, and 3 to the integer 123, and store this value into *next_pointer_arg. But, it will have read the character x, and after the call to isdigit in the loop while(isdigit(c = getchar())) fails, the x character will have effectively been discarded: it is no longer on the input stream.
The specification for scanf says that it is not supposed to do this. The specification for scanf says that unparsed characters are supposed to be left on the input stream. If the user had actually passed the format specifier "%dx", that would mean that, after reading and parsing an integer, a literal x is expected in the input stream, and scanf is going to have to explicitly read and match that character. So it can't accidentally read and discard the x in the process of parsing a %d directive.
So we need to modify our hypothetical %d code slightly. Whenever we read a character that turns out not to be an integer, we have to literally put it back on the input stream, for somebody else to maybe read later. There's actually a function in <stdio.h> to do this, sort of the opposite of getc, called ungetc. Here is a modified version of the code:
c = getchar();
if(isdigit(c)) {
int intval;
intval = c - '0';
while(isdigit(c = getchar())) {
intval = 10 * intval + (c - '0');
}
ungetc(c, stdin); /* push non-digit character back onto input stream */
*next_pointer_arg = intval;
n_vals_converted++;
} else {
/* saw no digit; processing has failed */
ungetc(c, stdin);
return n_vals_converted;
}
You will notice that I have added two calls to ungetc, in both places in the code where, after calling getchar and then isdigit, the code has just discovered that it has read a character that is not a digit.
It might seem strange to read a character and then change your mind, meaning that you have to "unread" it. It might make more sense to peek at at an upcoming character (to determine whether or not it's a digit) without reading it. Or, having read a character and discovered that it's not a digit, if the next piece of code that's going to process that character is right here in scanf, it might make sense to just keep it in the local variable c, rather than calling ungetc to push it back on the input stream, and then later calling getchar to fetch it from the input stream a second time. But, having called out these other two possibilities, I'm just going to say that, for now, I'm going to plough ahead with the example that uses ungetc.
So far I've shown the code that you might have imagined lay behind scanf's processing of %d. But the code I've shown so far is still significantly incomplete, because it does not show the "secret extra power". It starts looking for digit characters right away; it doesn't do any skipping of leading whitespace.
Here, then, is my third and final sample fragment of %d-processing code:
/* skip leading whitespace */
while(isspace(c = getchar())) {
/* discard */
}
if(isdigit(c)) {
int intval;
intval = c - '0';
while(isdigit(c = getchar())) {
intval = 10 * intval + (c - '0');
}
ungetc(c, stdin); /* push non-digit character back onto input stream */
*next_pointer_arg = intval;
n_vals_converted++;
} else {
/* saw no digit; processing has failed */
ungetc(c, stdin);
return n_vals_converted;
}
That initial loop reads and discards characters as long as they're whitespace. Its form is very similar to the later loop that reads and processes characters as long as they're digits. The initial loop will read one more character than it seems like it should: when the isspace call fails, that means that it has just read a non whitespace character. But that's okay, because we were just about to read a character to see if it was the first digit.
[Footnotes: This code is still far from perfect. One pretty significant problem is that it doesn't have any checks for an EOF coming along in the middle of its parsing. Another problem is that it doesn't look for - or + before the digits, so it won't handle negative numbers. Yet another, more obscure problem is that, ironically, obvious-looking calls like isdigit(c) are not always correct — strictly speaking they need to be somewhat cumbersomely rendered as isdigit((unsigned char)c).]
If you're still with me, my point in all this is to illustrate these two points in a concrete way:
The reason %d is able to automatically skip leading whitespace is because (a) the specification says it's supposed to and (b) it has explicit code to do so, as my third example illustrates.
The reason scanf always leaves unprocessed input (that is, input that comes after the input it does read and process) on the input stream is because (a) again, the specification says it's supposed to and (b) its code is typically sprinkled with explicit calls to ungetc, or the equivalent, to make sure that every unprocessed character remains on the input, as my second example illustrates.
There are some problems with you approach:
you use an undocumented, implementation specific member of the FILE object _Placeholder which may or may not be available on different platforms and whose contents are implementation specific anyway.
you use scanf_s(), which is a Microsoft specific so-called secure version of scanf(): this function is optional and may not be available on all platforms. Furthermore, Microsoft's implementation does not conform to the C Standard: for example the size argument passed after &ch is documented in VS with a type of UINT whereas the C Standard specifies it as a size_t, which on 64-bit versions of Windows has a different size.
scanf() is quite tricky to use: even experienced C programmers get bitten by its many quirks and pitfalls. In your code, you test %d and %c, which behave very differently:
for %d, scanf() will first read and discard any white space characters, such as space, TAB and newlines, then read an optional sign + or -, it then expects to read at least one digit and stop when it gets a byte that is not a digit and leave this byte in the input stream, pushing it back with ungetc() or equivalent. If no digits can be read, the conversion fails and the first non digit character is left pending in the input stream, but the previous bytes are not necessarily pushed back.
processing %c is much simpler: a single byte is read and stored into the char object or the conversion fails if the stream is at end of file.
Processing %c after %d is tricky if the input stream is bound to a terminal as the user will enter a newline after the number expected for %d and this newline will be read immediately for the %c. The program can ignore white space before the byte expected for %c by inserting a space before %c in the format string: res = scanf(" %c", &ch);
To better understand the behavior of scanf(), you should output the return value of each call and the stream current position, obtained via ftell(). It is also more reliable to first set the stream to binary mode for the return value of ftell() to be exactly the number of bytes from the beginning of the file.
Here is a modified version:
#include <stdio.h>
#ifdef _MSC_VER
#include <fcntl.h>
#include <io.h>
#endif
int main() {
int x, res;
char ch;
long A, B, C, D;
#ifdef _MSC_VER
_setmode(_fileno(stdin), _O_BINARY);
#endif
A = ftell(stdin);
printf("A : %ld\n", A);
x = 0;
res = scanf_s("%d", &x);
B = ftell(stdin);
printf("B : %ld, res=%d, x=%d\n", B, res, x);
rewind(stdin);
C = ftell(stdin);
printf("C : %ld\n", C);
ch = 0;
res = scanf_s("%c", &ch, 1);
D = ftell(stdin);
printf("D : %ld, res=%d, ch=%d (%c)\n", D, res, ch, ch);
return 0;
}
Here's some code that illustrates the behavior of the %d conversion specifier; it may help understand how that aspect of scanf works. This isn't how it's actually implemented anywhere, but it follows the same rules (Updated to handle leading +/- sign, checks for overflow, etc).
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
/**
* Mimics the behavior of the scanf %d conversion specifier.
* Skips over leading whitespace, then reads and converts
* decimal digits up to the next non-digit character.
*
* Returns EOF if no non-whitespace characters are
* seen before EOF.
*
* Returns 0 if the first non-whitespace character
* is not a digit.
*
* Returns 1 if at least one decimal digit was
* read and converted.
*
* Stops reading on the first non-digit
* character, pushes that character back
* on the input stream.
*
* In the event of a signed integer overflow,
* sets errno to ERANGE.
*/
int scan_to_int( FILE *stream, int *value )
{
int conv = 0;
int tmp = 0;
int c;
int sign = 0;
/**
* Skip over leading whitespace
*/
while( ( c = fgetc( stream ) ) != EOF && isspace( c ) )
; // empty loop
/**
* If we see end of file before any non-whitespace characters,
* return EOF.
*/
if ( c == EOF )
return c;
/**
* Account for a leading sign character.
*/
if ( c == '-' || c == '+' )
{
sign = c;
c = fgetc( stream );
}
/**
* As long as we see decimal digits, read and convert them
* to an integer value. We store the value to a temporary
* variable until we're done converting - we don't want
* to update value unless we know the operation was
* successful
*/
while( c != EOF && isdigit( c ) )
{
/**
* Check for overflow. While setting errno on overflow
* isn't required by the C language definition, I'm adding
* it anyway.
*/
if ( tmp > INT_MAX / 10 - (c - '0') )
errno = ERANGE;
tmp = tmp * 10 + (c - '0');
conv = 1;
c = fgetc( stream );
}
/**
* Push the last character read back onto the input
* stream.
*/
if ( c != EOF )
ungetc( c, stream );
/**
* If we read a sign character (+ or -) but did not have a
* successful conversion, then that character was not part
* of a numeric string and we need to put it back on the
* input stream in case it's part of a non-numeric input.
*/
if ( sign && !conv )
ungetc( sign, stream );
/**
* If there was a successful read and conversion,
* update the output parameter.
*/
if ( conv )
*value = tmp * (sign == '-' ? -1 : 1);
/**
* Return 1 if the read was successful, 0 if there
* were no digits in the input.
*/
return conv;
}
/**
* Simple test program - attempts to read 1 integer from
* standard input and display it. Display any trailing
* characters in the input stream up to and including
* the next newline character.
*/
int main( void )
{
int val;
int r;
errno = 0;
/**
* Read the next item from standard input and
* attempt to convert it to an integer value.
*/
if ( (r = scan_to_int( stdin, &val )) != 1 )
printf( "Failed to read input, r = %d\n", r );
else
printf( "Read %d%s\n", val, errno == ERANGE ? " (overflow)" : "" );
/**
* If we didn't hit EOF, display the remaining
* contents of the input stream.
*/
if ( r != EOF )
{
fputs( "Remainder of input stream: {", stdout );
int c;
do {
c = fgetc( stdin );
switch( c )
{
case '\a': fputs( "\\a", stdout ); break;
case '\b': fputs( "\\b", stdout ); break;
case '\f': fputs( "\\f", stdout ); break;
case '\n': fputs( "\\n", stdout ); break;
case '\r': fputs( "\\r", stdout ); break;
case '\t': fputs( "\\t", stdout ); break;
default: fputc( c, stdout ); break;
}
} while( c != '\n' );
fputs( "}\n", stdout );
}
return 0;
}
Some examples - first, we signal EOF (in my case, by typing Ctrl-D):
$ ./convert
Failed to read input, r = -1
Next, we pass in a non-numeric string:
$ ./convert
abcd
Failed to read input, r = 0
Remainder of input stream: {abcd\n}
Since nothing was converted, the remainder of the input stream contains everything we typed (including the newline from hitting Enter).
Next, a numeric string with non-numeric trailing characters:
$ ./convert
12cd45
Read 12
Remainder of input stream: {cd45\n}
We stopped reading at 'c' - only the leading 12 is read and converted.
Several numeric strings separated by whitespace - only the first string is converted:
$ ./convert
123 456 789
Read 123
Remainder of input stream: {\t456\t789\n}
And a numeric string with leading whitespace:
$ ./convert
12345
Read 12345
Remainder of input stream: {\n}
Handle leading signs:
$ ./convert
-123abd
Read -123
Remainder of input stream: {abd\n}
$ ./convert
+456
Read 456
Remainder of input stream: {\n}
$ ./convert
-abcd
Failed to read input, r = 0
Remainder of input stream: {-abcd\n}
And, finally, we add an overflow check - note that scanf is not required to check for overflow by the C language standard, but I figured it was a useful thing to do:
$ ./convert
123456789012345678990012345667890
Read -701837006 (overflow)
Remainder of input stream: {\n}
%d, %i, %f, %s, etc., all skip over leading whitespace, since whitespace is not meaningful in those cases except to act as a separator between inputs. %c and %[ do not skip over leading whitespace, because it may be meaningful for those particular conversions (there are times when you want to know whether the character you just read is a space, or a tab, or a newline).
As Steve points out, whitespace handling in C stdio routines is and always has been a thorny problem, and no one solution always works the best, especially since different library routines handle it differently.
I want to make a loop in C that, when the program asks for an integer and the user types a non-digit character, the program asks again for an integer.
I just found the below code. but I don't understand what this means scanf("%*[^\n]%*c"). What does ^\n mean? What does the * before ^\n and c mean?
/*
This program calculate the mean score of an user 4 individual scores,
and outputs the mean and a final grade
Input: score1, score2,score2, score3
Output: Mean, FinalGrade
*/
#include <stdio.h>
//#include <stdlib.h>
int main(void){
int userScore = 0; //Stores the scores that the user inputs
float meanValue = 0.0f; //Stores the user mean of all the notes
char testChar = 'f'; //Used to avoid that the code crashes
char grade = 'E'; //Stores the final
int i = 0; //Auxiliar used in the for statement
printf("\nWelcome to the program \n Tell me if Im clever enough! \n Designed for humans \n\n\n");
printf("Enter your 4 notes between 0 and 100 to calculate your course grade\n\n");
// Asks the 4 notes.
for ( ; i<=3 ; i++ ){
printf("Please, enter your score number %d: ", i+1);
//If the note is not valid, ask for it again
//This is tests if the user input is a valid integer.
if ( ( scanf("%d%c", &userScore, &testChar)!=2 || testChar!='\n')){
i-=1;
scanf("%*[^\n]%*c");
}else{ //Enter here if the user input is an integer
if ( userScore>=0 && userScore<=100 ){
//Add the value to the mean
meanValue += userScore;
}else{ //Enter here if the user input a non valid integer
i-=1;
//scanf("%*[^\n]%*c");
}
}
}
//Calculates the mean value of the 4 scores
meanValue = meanValue/4;
// Select your final grade according to the final mean
if (meanValue>= 90 && meanValue <=100){
grade = 'A';
} else if(meanValue>= 80 && meanValue <90){
grade = 'B';
} else if (meanValue>= 70 && meanValue <80){
grade = 'C';
} else if(meanValue>= 60 && meanValue <70){
grade = 'D';
}
printf("Your final score is: %2.2f --> %c \n\n" , meanValue, grade);
return 0;
}
Breakdown of scanf("%*[^\n]%*c"):
%*[^\n] scans everything until a \n, but doesn't scan in the \n. The asterisk(*) tells it to discard whatever was scanned.
%*c scans a single character, which will be the \n left over by %*[^\n] in this case. The asterisk instructs scanf to discard the scanned character.
Both %[ and %c are format specifiers. You can see what they do here. The asterisks in both the specifiers tell scanf, not to store the data read by these format specifiers.
As #chux commented below, it will clear a single line of the stdin (Standard Input Stream) up to and including the newline character. In your case, the line with invalid input gets cleared from the stdin.
It is better to use
scanf("%*[^\n]");
scanf("%*c");
to clear the stdin. This is because, in the former case (single scanf), %*[^\n] will fail when the first character to be scanned is the \n character and the rest of the format string of the scanf will be skipped which means that the %*c will not function and thus, the \n from the input will still be in the input stream. In this case, this will not happen as even when the first scanf fails, the second one will execute as they are separate scanf statements.
You can take a string as input in C using scanf(“%s”, s). But, it accepts string only until it finds the first space.
In order to take a line as input, you can use scanf("%[^\n]%*c", s); where s is defined as char s[MAX_LEN] where MAX_LEN is the maximum size of s. Here, [] is the scanset character. ^\n stands for taking input until a newline isn't encountered. Then, with this %*c, it reads the newline character and here, the used * indicates that this newline character is discarded.
Suppose char sen[max_length] where maximum length is maximum size of sen[].
this scanf(“%[^\n]%*c”,&sen[]); will help you to get a entire sentence until the next line isn’t encountered “\n” or enter is pressed which is done with the help of “%[^\n]” here [ ] is the scan set character . the ”%*c” will read the newline character , asterisk ” * ” is used to indicate that the next line character is discarded.
%[^\n]%*c
Which will read everything up to the newline into the string you pass in, then will consume a single character (the newline) without assigning it to anything (that '*' is 'assignment suppression').
Otherwise,the newline is left in the input stream waiting to immediately terminate the the subsequent %[^\n] format directives.
The problem with adding a space character to the format directive (%[^\n]) is that the space will match any white space. So, it will eat the newline from the end of the previous input, but it will also eat any other whitespace (including multiple newlines).
Essentially this is two reads using scanf. The first part "%[^\n]"
means 'read everything that is not a newline' and this is assigned to
the character buffer str.
The second part "%*c" means 'read the next character but don't save it
anywhere'; this gets rid of the newline that we didn't read in the
first part.
https://www.sololearn.com/Discuss/2249429/what-s-the-use-of-scanf-n-c-in-c-programming
In order to take a line as input, you can use scanf("%[^\n]%*c", s); where is defined as char s[MAX_LEN] where is the maximum size of . Here, [] is the scanset character. ^\n stands for taking input until a newline isn't encountered. Then, with this %*c, it reads the newline character and here, the used * indicates that this newline character is discarded.
So, I got this assignment as a student that ask me to create a simple program using C.
This program input only allow you to input only characters A-Z, a-z, and (space).
and the length of the string should be no less than 1 character and no more than 100 characters.
So, I come with the conclusion that I should use if function to validate if the user input the allowed character.
#include <stdio.h>
#include <ctype.h>
int main()
{
char name[100];
scanf("%s",&name);
fflush(stdin);
if (isdigit(name))
^^^^
{
printf("Wrong answers");
getchar();
}
else
....
It was supposed to print "wrong answers" if you input numbers in there, but this program won't run.. It keeps saying :
error C2664: 'isdigit' : cannot convert parameter 1 from 'char [100]' to 'int'
I don't know what this error means.. Is there something I miss? Or am I using the wrong function?
I have also tried
if (((name>='A')&&(name<='Z'))||((name>='a')&&(name<='z')||)((name==' ')))
{
//this print what i want
}
else
{
printf("wrong answers");//this print "wrong answer"
}
but it always print "wrong answers" no matter I input the correct input or the wrong input.
Your help is highly appreciated.
Thank you.
*ps : I am a beginner at programming.
isdigit() takes an int as argument, not a char*:
int isdigit(int c);
You have to use a loop over the string and check each character in it.
Having said that, to achieve:
this program input only allow you to input only characters 'A'-'Z', 'a'-'z', and ' '(space)
you are better off using isalpha().
Try this out:
#include <stdio.h>
#include <ctype.h>
int main()
{
int i = 0;
char name[101], temp;
// take input one character at a time
while(scanf("%c", &temp)){
// stop when newline or carriage return
if(temp == '\n' || temp == '\0' || !isalpha(temp) ){
break;
}
// save character in array
name[i] = temp;
// move to the next position of the array
i++;
}
printf( "%s", temp );
return 0;
}
The problem you're seeing is that you're passing isdigit the wrong type of value - it expects an int, but you're passing it an array of char. You would have to loop over each and every character in your string to check if it's a digit or not.
But that is ultimately not what you're after as you're looking to confirm that the string contains letters or spaces - there are lots of characters that could be entered that aren't classed as digits that would be accepted incorrectly.
What would be the easiest solution for you, is to use the function strspn. It takes a string and returns the length of how many characters match the second parameter. If that length is the same length as your string, you know that it only contains valid characters.
size_t valid;
valid=strspn(name, "abcdefg(fill in with other valid characters)");
if(valid==strlen(name))
{
// Valid name
}
else
{
// Not valid
}
If you need to expand the accepted characters, it's just a simple case of adding them to the 2nd parameter.
OP's code fails as isdigit() test is a single character is a digit (0-9). It does not test a string.
int isdigit(int c);
The isdigit function tests for any decimal-digit character.
In all cases the argument is an int, the value of which shall be
representable as an unsigned char or shall equal the value of the macro EOF.
OP's buffer is too small to save 100 characters read from the user. At least 1 more needed to detect if too many were read and 1 more for a null character to mark the end of a string.
fflush(stdin); has its problems too.
scanf("%s",&name); does not save white-space. The parameter should have been name too. (no &)
Read a line of user input with fgets() which saves the result as a string.
Test if the input meets the criteria.
Read
#define N 100
// No need to be stingy on buffer size reading user input. Suggest 2x
// We need +1 for extra character detection, \n and \0
char buf[2*N + 1 + 1];
fgets(buf, sizeof buf, stdin);
// lop off potential \n
size_t length = strlen(buf);
if (length > 0 && buf[length-1] == '\n') {
buf[--length] = '\0';
}
Test
only characters 'A'-'Z', 'a'-'z', and ' '(space).
for (size_t i = 0; i<length; i++) {
if (!isalpha((unsigned char)buf[i]) && buf[i] != ' ') {
puts("Invalid chracter");
break;
}
}
length of the string should be no less than 1 character and no more than 100 characters.
if (length < 1 || length > 100) {
puts("Input wrong length");
}
Others approaches can be used to disqualify very long inputs. IMO, very long inputs represent an attack and should be handled differently than a simple line that was a bit too long.
if (length < 1 || length > 100) {
if (length + 2 >= sizeof buf) {
puts("Input way too long");
exit (EXIT_FAILURE);
}
puts("Input wrong length");
}
name must have one extra space for the \0 (NUL) character.
So to store 100 characters, its size should be at least 101.
char name[101];
You could first use
fgets(name, sizeof(name), stdin);
to read into name character array.
Note that fgets() will read in the trailing newline (\n) as well which need be removed like
name[strlen(name)-1]='\0';
Then use sscanf(). Like
size_t l=strlen(name);
sscanf(name, "%100[A-Za-z ]", name);
if(strlen(name)!=l)
{
printf("\nInvalid input.");
}
Note the space after the A-Za-z.
The 100 in the %100[A-Za-z] denotes reading at most 100 characters into name. The [A-Za-z ] will make the sscanf() stop reading if a non-alphabetic character which is not a space is encountered.
First read into name. Then store its length in l. Now read everything till a non-alphabet other than a space occurs in name to name itself (thereby modifying name).
Now compare the length of this new string with l. If they are the same. The input is valid as per your need.
You could also use scanf() instead of fgets() like
scanf("%100[^\n]", name);
which instructs to read every character till a \n into name. If this is used, no \n will added at the end of name unlike the case with fgets().
Now I would like to point out some mistakes in your code.
scanf("%s",&name);
will lead to errors. Correct one is
scanf("%s",name);
as the second argument here must be an address and since an array name in C decays into its base address, just name would do instead of &name.
As others have pointed out, using fflush() on stdin is undefined and must be avoided.
If you are trying to clear the input buffer till the next newline (\n), you could do
int ch;
while((ch=getchar())!='\n');// && ch!=EOF)
The argument of isdigit() must be a char and not a character array (type char [100] if size is 100).
And if is a statement and not a function.
Can a user type null terminator in an input, for which scanf is used, so the length of the input would be 0?
char msg[1000];
scanf("%1000s",msg); // the user would type nothing: '' or '\0'
On many systems, the answer is "Yes".
Usually, the magic sequence is Control-#.
The character code for # is usually 64, one less the that of A (65). Control-A is character code 1; one less is character code 0, aka '\0'.
Note that to get a zero-length input, you'd have to type the null byte as the first non-space character, and you would still need to hit return to enter the input. Your program would find it hard to reliably identify what else was entered after the null byte.
Some systems allow the terminal user to enter a NUL byte by pressing Ctrl-#. scanf() would then store this byte into the destination array, but would most likely not consider this byte to constitute a word separator, so input parsing would continue until end of file or a whitespace character such as newline is entered.
There is no way to tell from the destination array if a NUL byte was stored after other characters, nor precisely how many further characters have been read after this NUL byte. But if scanf() returned 1 and msg has a zero length, the only possibility is that the user entered a NUL byte after a possibly empty sequence of whitespace characters.
This would also be the only possibility for fgets() to return an empty string when passed a destination array with a size greater than 1.
If input is read from a file instead of a terminal, it is always a possibility for the file to contain NUL bytes and care should be taken to avoid undefined behavior if it happens. For example, here is a classic bug:
char msg[1000];
if (fgets(msg, sizeof msg, stdin)) {
int len = strlen(msg);
if (msg[len - 1] == '\n') { // potential undefined behavior!
msg[--len] = '\0' // potential undefined behavior!
}
...
}
Also note that your code has a flaw: the maximum number of characters to store into the destination should be 999, not 1000:
char msg[1000];
if (scanf("%999s",msg) == 1) {
/* word was read from the user */
if (*msg == '\0') {
printf("user entered a NUL byte\n");
}
}
Can a user type null terminator in an input, for which scanf is used, so the length of the input would be 0?
In addition to a user typing in a null character, stdin could use re-directed input that contains null characters.
foo < data.bin
Detail: When the first character scanned is a null character or '\0', it is read like any other character and saved like any other non-white-space character. So the length of input is still more than 0, but the value reported by strlen(msg) is fooled by the embedded null character and returns zero.
fgets() has the same problem as scanf() in this regard: strlen(msg) does not alway report the number of characters read.
Non-standard getline() does return the number of characters read and is a way out of this.
#Stargateur mentions using "%n" to store the number of characters saved in msg. Perhaps like
char msg[1000 + 1];
int start, end;
if (1 == scanf(" %n%1000s%n", &start, msg, &end)) {
printf("%d characters read\n", end - start);
}
IAC, we are trying to cope with a corner weakness of scanf() and family. The best read input solution depends on additional factors, not mentioned by OP.
Yes, it possible because '\0' is a non-white-space. scanf() will consider that is the end of the string. So %s can match a empty string.
You can use the m specifier to allocate a corresponding buffer to hold the input. Note this is POSIX 200112L standard.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char *str;
int n;
if (scanf("%ms%n", &str, &n) == 1) {
printf("I read %d character(s).\n", n);
if (strlen(str) != 0) {
printf("str = %s\n", str);
}
else {
printf("str is empty");
}
free(str);
}
else {
printf("The user enter nothing");
}
}
I want to make a loop in C that, when the program asks for an integer and the user types a non-digit character, the program asks again for an integer.
I just found the below code. but I don't understand what this means scanf("%*[^\n]%*c"). What does ^\n mean? What does the * before ^\n and c mean?
/*
This program calculate the mean score of an user 4 individual scores,
and outputs the mean and a final grade
Input: score1, score2,score2, score3
Output: Mean, FinalGrade
*/
#include <stdio.h>
//#include <stdlib.h>
int main(void){
int userScore = 0; //Stores the scores that the user inputs
float meanValue = 0.0f; //Stores the user mean of all the notes
char testChar = 'f'; //Used to avoid that the code crashes
char grade = 'E'; //Stores the final
int i = 0; //Auxiliar used in the for statement
printf("\nWelcome to the program \n Tell me if Im clever enough! \n Designed for humans \n\n\n");
printf("Enter your 4 notes between 0 and 100 to calculate your course grade\n\n");
// Asks the 4 notes.
for ( ; i<=3 ; i++ ){
printf("Please, enter your score number %d: ", i+1);
//If the note is not valid, ask for it again
//This is tests if the user input is a valid integer.
if ( ( scanf("%d%c", &userScore, &testChar)!=2 || testChar!='\n')){
i-=1;
scanf("%*[^\n]%*c");
}else{ //Enter here if the user input is an integer
if ( userScore>=0 && userScore<=100 ){
//Add the value to the mean
meanValue += userScore;
}else{ //Enter here if the user input a non valid integer
i-=1;
//scanf("%*[^\n]%*c");
}
}
}
//Calculates the mean value of the 4 scores
meanValue = meanValue/4;
// Select your final grade according to the final mean
if (meanValue>= 90 && meanValue <=100){
grade = 'A';
} else if(meanValue>= 80 && meanValue <90){
grade = 'B';
} else if (meanValue>= 70 && meanValue <80){
grade = 'C';
} else if(meanValue>= 60 && meanValue <70){
grade = 'D';
}
printf("Your final score is: %2.2f --> %c \n\n" , meanValue, grade);
return 0;
}
Breakdown of scanf("%*[^\n]%*c"):
%*[^\n] scans everything until a \n, but doesn't scan in the \n. The asterisk(*) tells it to discard whatever was scanned.
%*c scans a single character, which will be the \n left over by %*[^\n] in this case. The asterisk instructs scanf to discard the scanned character.
Both %[ and %c are format specifiers. You can see what they do here. The asterisks in both the specifiers tell scanf, not to store the data read by these format specifiers.
As #chux commented below, it will clear a single line of the stdin (Standard Input Stream) up to and including the newline character. In your case, the line with invalid input gets cleared from the stdin.
It is better to use
scanf("%*[^\n]");
scanf("%*c");
to clear the stdin. This is because, in the former case (single scanf), %*[^\n] will fail when the first character to be scanned is the \n character and the rest of the format string of the scanf will be skipped which means that the %*c will not function and thus, the \n from the input will still be in the input stream. In this case, this will not happen as even when the first scanf fails, the second one will execute as they are separate scanf statements.
You can take a string as input in C using scanf(“%s”, s). But, it accepts string only until it finds the first space.
In order to take a line as input, you can use scanf("%[^\n]%*c", s); where s is defined as char s[MAX_LEN] where MAX_LEN is the maximum size of s. Here, [] is the scanset character. ^\n stands for taking input until a newline isn't encountered. Then, with this %*c, it reads the newline character and here, the used * indicates that this newline character is discarded.
Suppose char sen[max_length] where maximum length is maximum size of sen[].
this scanf(“%[^\n]%*c”,&sen[]); will help you to get a entire sentence until the next line isn’t encountered “\n” or enter is pressed which is done with the help of “%[^\n]” here [ ] is the scan set character . the ”%*c” will read the newline character , asterisk ” * ” is used to indicate that the next line character is discarded.
%[^\n]%*c
Which will read everything up to the newline into the string you pass in, then will consume a single character (the newline) without assigning it to anything (that '*' is 'assignment suppression').
Otherwise,the newline is left in the input stream waiting to immediately terminate the the subsequent %[^\n] format directives.
The problem with adding a space character to the format directive (%[^\n]) is that the space will match any white space. So, it will eat the newline from the end of the previous input, but it will also eat any other whitespace (including multiple newlines).
Essentially this is two reads using scanf. The first part "%[^\n]"
means 'read everything that is not a newline' and this is assigned to
the character buffer str.
The second part "%*c" means 'read the next character but don't save it
anywhere'; this gets rid of the newline that we didn't read in the
first part.
https://www.sololearn.com/Discuss/2249429/what-s-the-use-of-scanf-n-c-in-c-programming
In order to take a line as input, you can use scanf("%[^\n]%*c", s); where is defined as char s[MAX_LEN] where is the maximum size of . Here, [] is the scanset character. ^\n stands for taking input until a newline isn't encountered. Then, with this %*c, it reads the newline character and here, the used * indicates that this newline character is discarded.