So I want to write a program to check if user enter. Say requirement is 4 digits number, if user enters 5 then program keeps asking user to renter exactly 4 digit number.
I got the working code like this: basically use scanf to read in a string value, then use strlen to count number of digits. If user enters the correct digit, then I use atoi to convert that string into an int, which I will use for later.
Say requirement is 4 digit number:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int digit, mynumber;
int digit = 5;
char str[5];
/* Checking if enter the correct digit */
do {
printf("Enter a %d digit number\n", digit);
scanf("%s", &str);
if (strlen(str) != digit) {
printf("You entered %d digits. Try again \n", strlen(str));
} else {
printf("You entered %d digits. \n", strlen(str));
printf("Converting string to num.....\n");
mynumber = atoi(str);
printf("The number is %d\n", mynumber);
}
} while (strlen(str) != digit);
return 0;
}
I want to modify this a bit. Instead of doing char str[5] for 5 digit string. I want to try a dynamic array.
So in place of char str[5], I do this:
char *str;
str = malloc(sizeof(char) * digit);
Running this through the code gives seg fault. Can anyone help me with this?
This is the complete code with the issue
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int mynumber;
int digit = 5;
char *str;
str = malloc(sizeof(char) * digit);
/* Checking if enter the correct digit */
do {
printf("Enter a %d digit number\n", digit);
scanf("%s", &str);
if (strlen(str) != digit) {
printf("You entered %d digits. Try again \n", strlen(str));
} else {
printf("You entered %d digits. \n", strlen(str));
printf("Converting string to num.....\n");
mynumber = atoi(str);
printf("The number is %d\n", mynumber);
}
} while (strlen(str) != digit);
return 0;
}
While you can use the formatted input function scanf to take your input as a string, scanf is full of a number of pitfalls that can leave stray characters in your input stream (stdin) depending on whether a matching-failure occurs. It also has the limitation using the "%s" conversion specifier of only reading up to the first whitespace. If your user slips and enters "123 45", you read "123", your tests fail, and "45" are left in stdin unread, just waiting to bite you on your next attempted read unless you manually empty stdin.
Further, if you are using "%s" without the field-width modifier -- you might as well be using gets() as scanf will happily read an unlimited number of characters into your 5 or 6 character array, writing beyond your array bounds invoking Undefined Behavior.
A more sound approach is the provide a character buffer large enough to handle whatever the user may enter. (don't Skimp on buffer size). The read an entire line at a time with fgets(), which with a sufficient sized buffer ensure the entire line is consumed eliminating the chance for characters to remain unread in stdin. The only caveat with fgets (and every line-oriented input function like POSIX getline) is the '\n' is also read and included in the buffer filled. You simply trim the '\n' from the end using strcspn() as a convenient method obtaining the number of characters entered at the same time.
(note: you can forego trimming the '\n' if you adjust your tests to include the '\n' in the length you validate against since the conversion to int will ignore the trailing '\n')
Your logic is lacking one other needed check. What if the user enters "123a5"? All 5 characters were entered, but they were not all digits. atoi() has no error reporting capability and will happily convert the string to 123 silently without providing any indication that additional characters remain. You have two-options, either use strtol for the conversion and validate no characters remain, or simply loop over the characters in your string checking each with isdigit() to ensure all digits were entered.
Putting that altogether, you could do something like the following:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define NDIGITS 5 /* if you need a constant, #define one (or more) */
#define MAXC 1024
int main (void) {
int mynumber;
size_t digit = NDIGITS;
char buf[MAXC]; /* buffer to hold MAXC chars */
/* infinite loop until valid string entered, or manual EOF generated */
for (;;) {
size_t len;
printf("\nEnter a %zu digit number: ", digit); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) { /* read entire line */
fputs ("(user canceled input)\n", stdout);
break;
}
buf[(len = strcspn(buf, "\n"))] = 0; /* trim \n, get len */
if (len != digit) { /* validate length */
fprintf(stderr, " error: %zu characters.\n", len);
continue;
}
for (size_t i = 0; i < len; i++) { /* validate all digits */
if (!isdigit(buf[i])) {
fprintf (stderr, " error: buf[%zu] is non-digit '%c'.\n",
i, buf[i]);
goto getnext;
}
}
if (sscanf (buf, "%d", &mynumber) == 1) { /* validate converstion */
printf ("you entered %zu digits, mynumber = %d\n", len, mynumber);
break; /* all criteria met, break loop */
}
getnext:;
}
return 0;
}
Example Use/Output
Whenever you write an input routine, go try and break it. Validate it does what you need it to do and catches the cases you want to protect against (and there will still be more validations you can add). Here, it covers most anticipated abuses:
$ ./bin/only5digits
Enter a 5 digit number: no
error: 2 characters.
Enter a 5 digit number: 123a5
error: buf[3] is non-digit 'a'.
Enter a 5 digit number: 123 45
error: 6 characters.
Enter a 5 digit number: ;alsdhif aij;ioj34 ;alfj a!%#$%$ ("cat steps on keyboard...")
error: 61 characters.
Enter a 5 digit number: 1234
error: 4 characters.
Enter a 5 digit number: 123456
error: 6 characters.
Enter a 5 digit number: 12345
you entered 5 digits, mynumber = 12345
User cancels input with ctrl+d on Linux (or ctrl+z on windows) generating a manual EOF:
$ ./bin/only5digits
Enter a 5 digit number: (user canceled input)
(note: you can add additional checks to see if 1024 or more characters were input -- that is left to you)
This is a slightly different approach to reading input, but from a general rule standpoint, when taking user input, if you ensure you consume an entire line of input, you avoid many of the pitfalls associated with using scanf for that purpose.
Look things over and let me know if you have further questions.
In your code you have an array of 5 bytes. If the user enters more than 4 digits, scanf will happily overflow the array and corrupt the memory. It will corrupt the memory in both cases, as an array and as a malloc. However, not every memory corruption causes a crash.
So, you need to limit the number of bytes the scanf can read. The way to do it is to use %4s in the format string.
However, in this case you will not be able to detect when the user enters more than 4 digits. You would need at least 1 more byte: str[6] and %5s;
I suggest, instead of using scanf, use getchar. It would allow you to read character by character and count them on the way.
Related
I want to write a C program to add the numbers given by the user as long as they want... can anyone fix this program?
I tried to use a do-while loop.
Any other suggestions to improve my code?
I am unable to end the loop.
#include <stdio.h>
int main()
{
int x=0, sum = 0, y=0, fu;
printf("first number you want to add:\n");
scanf("%d", &x);
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
do
{
printf("do you want to add numbers further? \nEnter 0:Yes or 1:No: \n");
scanf("%d", &fu);
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
}
while(fu>0);
sum=x;
printf("Sum of all integers = %d\n", sum);
return 0;
}
Ask for the 3rd and further numbers in an if and modify your while:
scanf("%d", &fu);
if(fu == 0) {
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
}
}
while(fu == 0);
Your prompt says:
Enter 0:Yes or 1:No:
so you need to continue that loop if 0 was entered:
while(fu == 0);
Also, you don't need to take another y after non-0 input.
The key to taking any input, either from the user, or from a file, is to validate every single input by checking the return. You cannot blindly use a variable holding input until you know whether the input succeeded or failed. Otherwise, if the input fails and you use a variable whose value is indeterminate, you invoke undefined behavior.
Also, if you are using a formatted input function such as scanf(), if a matching failure occurs, character extraction from stdin ceases at that point and the characters causing the failure are left in stdin -- unread, just waiting to bite you again on your next attempted input.
Instead, if you use a line-oriented input function such as fgets() or POSIX getline(), you read an entire line at a time. You can simply call sscanf() on the buffer filled by fgets() to convert a numeric input to an integer value. That way it does not matter if the conversion succeeds or fails, you do not leave anything unread in the input stream.
Just as you must validate every input, you so too must validate every conversion. Whether using sscanf() or strtol(), etc... a failure to validate every conversion will likely lead to undefined behavior when you fail to detect the conversion failure.
Another benefit of using fgets() or getline() is they read and store the '\n' from the user pressing Enter. So rather than having to prompt "do you want to add numbers further? \nEnter 0:Yes or 1:No: \n" and have to worry about yet another input and conversion -- you simply check if Enter was pressed on an empty line to know the user completed input (e.g. the first character in the buffer filed by fgets() is the '\n' character).
You also have to handle an invalid input correctly. What if the user enters "bananas" instead of a number?
Putting it altogether, you could do something similar to:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer (character array) to hold all user input */
int sum = 0, n = 0; /* sum and count of numbers */
puts ("press ENTER alone to exit:\n"); /* display instructions */
while (1) { /* loop continually */
int tmp; /* temporary int to add to sum */
/* prompt based on 1st or subsequent number */
fputs (n ? "next number : " : "first number : ", stdout);
/* read and validate input, break on EOF or empty line */
if (!fgets (buf, MAXC, stdin) || *buf == '\n') {
puts ("---------------------");
break;
}
/* validate conversion to int */
if (sscanf (buf, "%d", &tmp) == 1) { /* on success */
sum += tmp; /* add to sum */
n += 1; /* increment count */
}
else /* handle error */
fputs (" error: invalid integer input.\n", stderr);
}
printf (" sum : %d\n", sum); /* output final sum */
}
Example Use/Output
$ ./bin/sum
press ENTER alone to exit:
first number : 10
next number : -20
next number : 30
next number : -40
next number : bananas
error: invalid integer input.
next number : 50
next number :
---------------------
sum : 30
There are several ways to approach this, and if you wanted the user to be able to enter more than one-number per-line, you could parse buf with strtol() to extract all values. (you can do the same with sscanf() using an offset from the beginning of the string and the characters consumed on each conversion from the "%n" specifier) Many ways to go.
Let me know if you have further questions.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
int n=1,i,cont;
char string[50];
scanf("%d",&n);
while(n!=0){
gets(string);
cont=0;
for(i=0;i<strlen(string);i++){
if(string[i]=='.'){
cont++;
}
}
if(cont%2==0){
printf("S\n");
}else{
printf("N\n");
}
scanf("%d",&n);
}
return 0;
}
My problem is quite simple but troublesome, I want to read an integer value n, and then read a string, after that read n again, but whenever I run the program, it only reads the string value... but if I digit 0 the program ends... it's like my scanf is within the gets function.
Mixing scanf with gets or fgets is troublesome because they each handle newlines differently.
Get rid of the gets call (which is unsafe anyway) and replace it with the following scanf call:
scanf("%49s", string);
This will read at most 49 characters into string (i.e. one less that its size).
From OP's comments, it sounds like the goal is to be able to read strings containing spaces. While there are ways to accomplish this using scanf(), it would be better to use fgets(), which is at the least less error-prone.
The fgets() function can be used to read input for the number into a buffer, and this buffer can then be processed by sscanf() to extract the number. Since fgets() keeps the newline character, it is not left behind to interfere with the next I/O operation.
But, when fgets() is used to get the string, since the newline is retained, it may be desirable to remove it. This can be accomplished in a number of ways, but here strcspn() is used to provide the index of the first \r or \n character encountered; a \0 character is then written to this location, removing the terminating newline from the string.
The code below illustrates these suggestions. Note that both buffer[] and string[] are generously allocated to accommodate reasonably large inputs. If a user enters a large number of characters (more than 999 in this case), the extra characters are left behind in the input stream for the next I/O function call. Also note that the main loop has been streamlined a bit; now there is a for(;;) loop that never terminates, broken out of when the user enters 0 for the number. And, there is a nested loop within the main loop that prompts the user to enter a number until a valid number is entered. Since the #include <stdlib.h> was unnecessary, it was removed. Better code would check the values returned from the calls to fgets() for possible errors.
#include<stdio.h>
#include<string.h>
int main(void)
{
int n = 1, cont;
char buffer[1000];
char string[1000];
for (;;) {
/* Loop until user enters a number */
do {
printf("Please enter a number: ");
fgets(buffer, sizeof buffer, stdin);
} while (sscanf(buffer, "%d", &n) != 1);
/* Break on 0 */
if (n == 0) break;
/* Get a string, and remove trailing newline */
printf("Please enter a string\n");
fgets(string, sizeof string, stdin);
string[strcspn(string, "\r\n")] = '\0';
cont = 0;
for (size_t i = 0; i < strlen(string); i++) {
if (string[i] == '.') {
cont++;
}
}
if (cont % 2 == 0){
printf("S\n");
} else {
printf("N\n");
}
}
return 0;
}
When you enter 5 for an example, you hit a new line character afterwards.
So you are entering 2 characters: 5 and a new line character.
That new line character is causing your headache.
The new line character is also considered an input.
In order to ignore this new line char, simply add a new line that acts as a garbage collection:
char garbage[50];
scanf( "%d", &n);
fgets(garbage, sizeof(garbage), stdin);
I am trying to figure out if there is a simpler way in C to scanf() a certain part of an inputted number by the user.
Following the code:
printf("Enter opcode:\n");
scanf("%1d", &opcode);
If an user inputs the number 240, scanf("%1d", &opcode); will save the first digit only in opcode
Is there a way to select only the last two digits?
The easiest way to handle the task is to read the input as a string. Then perform validations, e.g. number of characters entered, that last two characters are valid hex-digits, etc.. and then use your conversion of choice to convert the last two digits to an unsigned value.
When taking input, it is recommended that you use a line-oriented function to read the entire line and then parse what you need from the line. The benefits are three-fold (1) you get an independent validation of the read; (2) you get an independent validation of the conversion; and (3) what remains in the input buffer doesn't depend on the scanf conversion specifier used.
A short example would be:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 1024 /* if you need a constant #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer to hold line (don't skimp on size) */
printf ("Enter opcode: "); /* prompt */
if (fgets (buf, MAXC, stdin)) { /* read entire line */
char *p; /* pointer - to set to last 2 digits */
size_t len; /* length of the string entered */
buf[(len = strcspn (buf, "\r\n"))] = 0; /* get length/trim '\n' */
if (len < 2) { /* validate at least 2 characters entered */
fputs ("error: minimum 2-characters required.\n", stderr);
return 1;
}
p = buf + len - 2; /* set p to point to next to last char */
if (!isxdigit(*p) || !isxdigit(*(p+1))) { /* validate hex digits */
fputs ("error: last 2 chars are not hex-digits.\n", stderr);
return 1;
}
printf ("last 2 digits: %s\n", p); /* output last 2 digits */
/* perform conversion of choice here
* (suggest strtoul or sscanf)
*/
}
return 0;
}
(note: choosing the conversion is left to you. Also note how you handle the '\n' included in the buffer by fgets is also up to you. Above it is simply overwritten with the nul-terminating character)
Example Use/Output
$ ./bin/opcodelast2
Enter opcode: 240
last 2 digits: 40
Other results:
Enter opcode: 40
last 2 digits: 40
Enter opcode: 3240
last 2 digits: 40
Enter opcode: 324a
last 2 digits: 4a
Enter opcode: 4g
error: last 2 chars are not hex-digits.
Enter opcode: 4
error: minimum 2-characters required.
You can adjust the tests (e.g. isdigit or isxdigit) to meet your particular needs. You can (and should) include a test that len < MAXC - 1 to ensure the entire line was read and that additional characters do not remain unread (e.g. a cat went to sleep on the keyboard). Let me know if you have any further questions.
The code of #Craig Estey can be broken if you enter a big number.
int main()
{
char str[100];
do {
scanf("%99s", str);
} while (strlen(str) < 2);
int opcode = atoi(str + strlen(str) - 2);
}
Yes, it's not perfect because it's break when you enter a string with more than 100 char.
But, you can replace the scanf by another function who can take a infinite string len.
Im trying to save a 100 character max string into an array and then print an specified character of the array via an index, yet I get Segmentation error 11, here is the code:`
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str1[100];
int index;
printf("Enter text of max 100 characters: \n");
scanf("%s", str1);
printf("Enter the index to search\n");
scanf("%d", &index);
printf("your char is: %c\n", str1[index]);
return(0);
}
`
Any suggestions?
While user input is generally better handled by reading input with fgets and then parsing what you need from the resulting buffer with sscanf of simply with a pair of pointers and "inch-worming down" (a/k/a "walking") the string testing each char and handling as needed -- it is always worth a look at scanf to detail what you need to do to successfully use it for user input.
While fgets is not without needed validations, the number and types of validations needed with scanf and handling of characters that remain in the input buffer in the different cases of input failure or matching failures create a number of extra pitfalls for new (and not so new) C programmers.
The two primary problems with scanf are (1) there is no default limitation on the number of characters that it will read into any buffer (potentially overflowing your array); and (2) the fact that it does not remove the trailing '\n' (or any of the characters following an input or matching failure) from the input buffer (e.g. stdin). It is up to you to account for all characters in the input buffer and empty the buffer as needed.
Further complicating the picture are the ways the different scanf format specifiers handle leading-whitespace (numeric conversions typically skip leading whitespace, while a character conversion won't) Another issue is handling included whitespace. The "%s" format specifier will only read up to the first whitespace encountered, making it impossible to read "My dog has fleas" with a single %s specifier. (you can use a character class to read included whitespace -- as shown in the example below)
There are many other subtleties with scanf as well, so it is well worth the time it takes to read and understand man scanf.
From the comments, you now know if you ask for a string of 100 chars, you need, at minimum, 101 characters of storage -- we will assume that is learned.
When taking any input with scanf, you must always validate the return to insure that the number of conversion expected, in fact took place. For example, if you are reading "5 dogs" with the conversion specifier "%d %s", a return of 2 indicates a successful conversion to integer and string. However, you also know, at minimum a '\n' remains in the input buffer (and potentially many more characters, if say, "5 dogs and cats" were entered. It is up to you to remove the '\n' and any other characters that remain, before attempting to read more input with scanf.
The following example captures most of the pitfalls with your example and provides a couple of tools you can use when dealing with user input. The bottom line is learn to use fgets, but know how to use scanf as well. Your goal is to provide as robust and bullet-proof input routine as you can. Think about all the dumb things a user might do when prompted for input (or heaven forbid, a cat walks across the keyboard) There are always more validations you can add. Look at each of the included validations, and let me know if you have questions:
#include <stdio.h>
#include <string.h>
#define MAXC 100 /* if you need a constant, declare one */
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
char str1[MAXC+1] = ""; /* initialize to all zero */
int index, rtn, len; /* index, return, length */
for (;;) { /* loop until valid input obtained */
printf ("Enter text of max 100 characters: ");
rtn = scanf ("%100[^\n]", str1); /* read at most MAXC char */
if (rtn != 1) { /* validate scanf return */
if (rtn == EOF) { /* check if EOF, ctrl+d, ctrl+z (windoze) */
printf ("input canceled.\n");
return 0;
}
if (!str1[0]) /* was a character entered? */
fprintf (stderr, "error: string is empty.\n");
/* remove '\n' and any chars that remain in stdin */
empty_stdin();
}
else { /* all conditions met, good entry, empty stdin and break */
empty_stdin();
break;
}
}
len = (int)strlen (str1); /* get string length */
for (;;) { /* now do the same thing for integer */
printf ("Enter the index to search (0-%d): ", len - 1);
if ((rtn = scanf ("%d", &index)) != 1) {
if (rtn == EOF) {
printf ("input canceled.\n");
return 0;
}
fprintf (stderr, "error: invalid input - not integer.\n");
/* only need to strip if non-integer entered, because %d
* will skip leading whitespace, including '\n'.
*/
empty_stdin();
}
else if (index < 0 || len < index + 1) /* validate index value */
fprintf (stderr, "error: invalid index - out of range.\n");
else
break;
}
printf ("your char is: %c\n", str1[index]);
return 0;
}
Example Use/Output
$ ./bin/scanfstr1
Enter text of max 100 characters: 12345678901234567890
Enter the index to search (0-19): -1
error: invalid index - out of range.
Enter the index to search (0-19): 0
your char is: 1
$ ./bin/scanfstr1
Enter text of max 100 characters: 12345678901234567890
Enter the index to search (0-19): foo
error: invalid input - not integer.
Enter the index to search (0-19): 6
your char is: 7
$ ./bin/scanfstr1
Enter text of max 100 characters: My dog has fleas.
Enter the index to search (0-16): d
error: invalid input - not integer.
Enter the index to search (0-16): 3
your char is: d
$ ./bin/scanfstr1
Enter text of max 100 characters:
error: string is empty.
Enter text of max 100 characters: My cats are fine.
Enter the index to search (0-16): meow
error: invalid input - not integer.
Enter the index to search (0-16): input canceled.
I have the following code:
#include <stdio.h>
#define MIN 0
#define MAX 9
int main()
{
int n;
while (1) {
printf("Enter a number (%d-%d) :", MIN, MAX);
scanf("%d", &n);
if (n >= MIN && n <= MAX) {
printf("Good\n");
} else {
printf("Damn you!\n");
break;
}
}
return 0;
}
The above code works as expected as long as the user inputs an integer value. For example,
$ ./a.out
Enter a number (0-9) :15
Damn you!
$ ./a.out
Enter a number (0-9) :5
Good
Enter a number (0-9) :3
Good
Enter a number (0-9) :-1
Damn you!
$ ./a.out
But, when the user enters any unexpected input (like <up-arrow> - which is ^[[A, or any string like abc or abc def, etc), it fails and goes in to an infinite loop.
$ ./a.out
Enter a number (0-9) :2
Good
Enter a number (0-9) :^[[A
Good
Enter a number (0-9) :Good
Enter a number (0-9) :Good
Enter a number (0-9) :Good
Enter a number (0-9) :Good
Enter a number (0-9) :Good
Enter a number (0-9) :Good
^C
One thing to note: when the use enters <up-arrow> for the first time, it works as expected! For example,
$ ./a.out
Enter a number (0-9) :^[[A
Damn you!
$
Why is this odd behavior? How should we handle the case where user enters something that is unappropriate?
My advice would be to check the return value of scanf(). If it is zero, there has been a matching failure (ie the user didn't input an integer).
The reason it is succeeding is because n is not altered by scanf() when the match fails, so the check is performed on an uninitialised 'n'. My advice -there- would be to always initialise everything so that you don't end up getting weird logic results like you have there.
For example:
if (scanf("%d",&n) != 1))
{
fprintf(stderr,"Input not recognised as an integer, please try again.");
// anything else you want to do when it fails goes here
}
Personally, I advise ditching scanf altogether for interactive user input, especially for numeric input. It just isn't robust enough to handle certain bad cases.
The %d conversion specifier tells scanf to read up to the next non-numeric character (ignoring any leading whitespace). Assume the call
scanf("%d", &val);
If your input stream looks like {'\n', '\t', ' ', '1', '2', '3', '\n'}, scanf will skip over the leading whitespace characters, read and convert "123", and stop at the trailing newline character. The value 123 will be assigned to val, and scanf will return a value of 1, indicating the number of successful assignments.
If your input stream looks like {'a', 'b', 'c', '\n'}, scanf will stop reading at the a, not assign anything to val, and return 0 (indicating no successful assignments).
So far, so good, right? Well, here's an ugly case: suppose your user types in "12w4". You'd probably like to reject this entire input as invalid. Unfortunately, scanf will happily convert and assign the "12" and leave the "w4" in the input stream, fouling up the next read. It will return a 1, indicating a successful assignment.
Here's another ugly case: suppose your user types in an obnoxiously long number, like "1234567890123456789012345678901234567890". Again, you'd probably like to reject this input outright, but scanf will go ahead and convert and assign it, regardless of whether the target data type can represent that value or not.
To properly handle those cases, you need to use a different tool. A better option is to read the input as text using fgets (protecting against buffer overflows), and manually convert the string using strtol. Advantages: you can detect and reject bad strings like "12w4", you can reject inputs that are obviously too long and out of range, and you don't leave any garbage in the input stream. Disadvantages: it's a bit more work.
Here's an example:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
...
#define DIGITS ... // maximum number of digits for your target data type;
// for example, a signed 16-bit integer has up to 5 digits.
#define BUFSIZ (DIGITS)+3 // Account for sign character, newline, and 0 terminator
...
char input[BUFSIZ];
if (!fgets(input, sizeof input, stdin))
{
// read error on input - panic
exit(-1);
}
else
{
/**
* Make sure the user didn't enter a string longer than the buffer
* was sized to hold by looking for a newline character. If a newline
* isn't present, we reject the input and read from the stream until
* we see a newline or get an error.
*/
if (!strchr(input, '\n'))
{
// input too long
while (fgets(input, sizeof input, stdin) && !strchr(input, '\n'))
;
}
else
{
char *chk;
int tmp = (int) strtol(input, &chk, 10);
/**
* chk points to the first character not converted. If
* it's whitespace or 0, then the input string was a valid
* integer
*/
if (isspace(*chk) || *chk == 0)
val = tmp;
else
printf("%s is not a valid integer input\n", input);
}
}
I would use a char buffer to get the input and then convert it to an integer with e.g. atoi.
Only problem here is that atoi returns 0 on failure (you can't determine if it's 0 because of failure or because the value is 0).
Also you could just compare the strings with strncmp.
// edit:
As suggested in the comments you can do the check with isdigit()
Since I'm a bit in a hurry I couldn't implemented my example in your use case, but I also doubt that this causes any troubles.
Some example code would be:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(void)
{
int x;
char buf[4] = {0};
scanf("%s",buf);
if(isdigit(buf[0]))
{
x = atoi(buf);
if( x > 9)
{
// Not okay
}
else
{
// okay
}
}
else
{
// Not okay
}
return 0;
}
If the first element of the buffer is not a digit you know its wrong input anyway.
Otherwise you check the value now with atoi and look if its greater than 9. ( You don't need to check the lower value since -1 would already be detected in the isdigt call ( buf[0] would be "-" )
I have updated the code as follows (checked scanf() return value) and it works fine.
#include <stdio.h>
#include <errno.h>
#define MIN 0
#define MAX 9
int main()
{
int n, i;
while (1) {
errno = 0;
printf("Enter a number (%d-%d) :", MIN, MAX);
if (scanf("%d", &n) != 1) {
printf("Damn you!\n");
break;
}
if (n >= MIN && n <= MAX) {
printf("Good\n");
} else {
printf("Damn you!\n");
break;
}
}
return 0;
}
The following are few things to note from the scanf() man page!
man scanf
The format string consists of a sequence of directives which describe how to process the sequence of input characters. If processing of a directive fails, no further input is read, and scanf() returns. A "failure" can be either of the following: input failure, meaning that input characters were unavailable, or matching failure, meaning that the input was inappropriate.
RETURN VALUE:
scanf return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure. The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs. EOF is also returned if a read error occurs, in which case the error indicator for the stream is set, and errno is set indicate the error.
scanf return the number of fields it read, so you can do something like
if (scanf("%d",&n)<1) exit(1)
or even:
while(scanf("%d",&n)!=1);