Actually, I can easily found a similar question in Google, but it still can not solve my question.
How to prevent non-numeric input in C?
The upper-link is like a similar case.
Here is my code
#include <stdio.h>
int main()
{
int n;
printf("Enter 1 or 2?\n");
scanf("%d", &n);
while(n != 1 && n != 2)
{
printf("Please do not enter other characters\n");
printf("Enter 1 or 2?\n");
scanf("%d", &n);
}
}
I hope if users enter other numbers(e.g. 1, 45, 656), characters (e.g. a, f, u, e), or string(e.g. apple), the upper program can print out an error message and ask for user input again.
Yes! if users enter other numbers, the program can do what I want.
But! if users enter other characters, string, the program will keep looping.
What should I need to add to this program?
How to prevent non-numeric input in C & ask user input again
Do not use scanf()**. Use fgets().
scanf("%d", ...) does not consume non-numeric input. Instead that offending input remains in stdio for the next scanf().
Code cannot prevent non-numeric input unless it locks the keys from being pressed. Instead, read all input, identify the non-numeric text, toss it and present the user with feedback for new input.
Make a helper function to control impact on rest of code.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
// return -1 on EOF
int read_int_in_range(const char *prompt, const char *reprompt, int lo, int hi) {
if (prompt) {
fputs(prompt, stdout);
fflush(stdout);
}
char buf[100];
while (fgets(buf, sizeof buf, stdin)) {
char *endptr;
errno = 0;
long val = strtol(buf, &endptr, 10);
// no overflow, conversion occurred, in range
if (errno == 0 && endptr > buf && val >= lo && val <= hi) {
// Tolerate trailing white-space.
while (isspace((unsigned char ) *endptr)) {
endptr++;
}
// No junk after the numeric text
if (*endptr == '\0') {
return (int) val;
}
}
if (reprompt) {
fputs(reprompt, stdout);
fflush(stdout);
}
}
return EOF; // or `INT_MIN` or TBD code to distinguish `int` from an error.
}
Usage
const char *prompt = "Enter 1 or 2?\n";
const char *reprompt = "Please do not enter other characters\n" "Enter 1 or 2?\n";
int n = read_int_in_range(prompt, reprompt, 1, 2);
**I recommend to not use scanf() anywhere to read user input until ones understands its weaknesses and limitations.
scanf is meant for formatted input, ie: you know what input will be received, in this case the user may enter something other than an int and your program breaks down. So to deal with that unknown treat the input as a string then analyze the string. In this case you could capture the input in buf and use the function atoi to convert it to an int, like this:
#include <stdio.h>
#include <stdlib.h> /* for atoi */
int main()
{
int n;
char buf[10]; /* this is new */
printf("Enter 1 or 2\n");
/*scanf("%d", &n);*/
fgets(buf, 10, stdin);
n = atoi(buf);
while(n != 1 && n != 2)
{
printf("Please do not enter other characters\n");
printf("Enter 1 or 2?\n");
/*scanf("%d", &n);*/
fgets(buf, 10, stdin);
n = atoi(buf);
}
}
fgets is the way to do this, but wanted to post something like this to actually avoid fgets as asked. It may be of some interest. The appending part should've been delegated to some function and buffer size may've not been 10. Anyways:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define endl "\n"
int main (void)
{
int my_char = 0;
short bad = 0;
char text[10];
memset (text, 0, 10);
printf ("enter number: ");
while ( (my_char = getchar()) )
{
if ( my_char < '0' || my_char > '9' )
{
if ( my_char != '\n' ) bad = 1;
}
else
{
size_t len = strlen (text);
char *strtemp = malloc (len + 2);
strcpy (strtemp, text);
strtemp [len] = my_char;
strtemp [len + 1] = '\0';
strncpy (text, strtemp, 9);
free (strtemp);
}
if ( my_char == '\n' )
{
if ( bad )
{
printf ("enter again (just numbers): ");
fflush (stdout);
bad = 0;
memset (text, 0, 9);
}
else break;
}
}
printf ("entered: %s"endl, text);
}
Related
This might be a rookie question, but I need to make sure that the input given by the user is of data type char [%c] or a string [%s].
If it were an integer, I would just do something like this:
int data, x;
do {
printf("Please enter a number: ");
x = scanf(" %d", &data);
getchar();
} while(x!=1);
So I was wondering if there's a similar way to do this, if the input is supposed to be a string or a character. Thanks, Any help would be appreciated!
Avoid to use %c in scanf() because some unexpected character like \r\n will be input.
You can use a char[2] to receive a single character. An \0 will be filled after your string to represent the end of string, so the length of array must be bigger than 1.
An example:
#include <stdio.h>
int main()
{
char data[2];
scanf("%1s", data);
if (data[0] >= 'a' && data[0] <= 'z') // custom your constraint here
{
// legal
printf("legal: %s", data);
}
else
{
// illegal
printf("illegal: %s", data);
}
return 0;
}
While I input b, the data will be "b\0".
part of the answer is if you just want to read only alphabet you can use below.
#include <stdio.h>
#include <ctype.h>
int main()
{
char ch;
do {
printf("enter a char:");
scanf(" %c",&ch);
}while(!isalpha(ch));
printf("%c",ch);
return 0;
}
Update 1:
Just for the completeness and for the FUN part of the programing, have added code here.
This works well (not tested robustly, you can do if you need to) for the single char input or for a string of length 9.
Remember to type the EOF after input is entered in case length of input is < 9.
and read EOF behavior on same line and new line.
#include <stdio.h>
#include <ctype.h>
#define LEN 10
int main()
{
char ch;
char str[LEN] = {0};
int i = 0;
int ret;
printf("enter a char or string(len = 9) and press EOF if len < 9\n");
do {
if(1== (ret = scanf(" %c",&ch)))
{
if(isalpha(ch))
str[i++] = ch;
}
else
printf("scanf:Error (%d)\n", ret);
}while(ret != EOF && ( !isalpha(ch) || i < LEN-1));
str[i] = '\0';
printf("str is %s\n",str);
return 0;
}
I'm trying to write a function to get a decimal input from the user and return the actual value converted from ASCII. However, the function causes the next input from the user to be skipped. As in:
Enter input: 123
Enter input: /* doesn; allow for input */
Enter input: 456
long sum = 0;
int character = fgetc(stdin);
while(character != '\n'){
if(character >= '0' && character <= '9'){
/* convert from ASCII */
character -= '0';
sum = sum * 10 + character;
}
else{
/* reenter number */
}
character = fgetc(stdin);
}
return sum;
To figure out why your code doesn't work, I suggest you post your full code, because problems may lie in the way you call this function.
So before full code is posted, I can just tell you that this code works well on my machine:
#include <stdio.h>
#include <ctype.h>
int getlong();
int main() {
printf("\t%d\n", getlong());
printf("\t%d\n", getlong());
return 0;
}
int getlong() {
long sum = 0;
int character = fgetc(stdin);
while (character != '\n') {
if (isdigit(character)) {
/* convert from ASCII */
character -= '0';
sum = sum * 10 + character;
character = fgetc(stdin);
}
else {
character = fgetc(stdin);
continue;
}
}
return sum;
}
ctype.h is included in order to use isdigit(), while tells you whether a character is decimal digit.
But in fact, you don't have to do everything on your own. Using standard library is more effective and efficient, both for you and for the computer.
For example, you can scan a long integer directly from stdin:
#include <stdio.h>
int main() {
long value;
puts("Please input numbers:");
while (scanf(" %ld", &value) != 1) {
puts("Only numbers are welcome:");
scanf("%*[^\n]");
}
printf("%ld", value);
return 0;
}
Notice the white-space at the beginning of format, this makes scanf() discard all white-space characters(including spaces, newline and tab characters) extracted until a non-white-space character is met.
Or, use strtol(), while is relatively rarely seen:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char buf[80];
char *pEnd;
long value;
do
{
puts("Numbers please:");
if (fgets(buf, 80, stdin) == NULL)
{
perror("fgets()");
return 1;
}
value = strtol(buf, &pEnd, 10);
}
while (*pEnd != '\n');
printf("%ld", value);
return 0;
}
Of course, sscanf() also works, you can just write the code on your own.
From comments:
an extra newline in the stdin buffer...
Try replacing your current method with scanf() using following format string:
char* fmt = "%[^\n]%*c";
It reads everything up to the newline, then consumes the newline. * is an assignment suppressor.
Example: (includes functions to convert input string to float/integer number)
float get_float(void);
long get_int(void);
int main(void)
{
float num_f = get_float();
long num_i = get_int();
return 0;
}
float get_float(void)
{
char input[80];
char **dummy={0};
char* fmt = "%[^\n]%*c";
printf("Enter floating point number and hit return:\n");
scanf(fmt, input);
return strtod(input, dummy);
}
long get_int(void)
{
char input[80];
char **dummy={0};
char* fmt = "%[^\n]%*c";
printf("Enter integer number and hit return:\n");
scanf(fmt, input);
return strtol(input, dummy, 10);
}
Note: These functions are bare bones illustrations of how converting input into number variables might be done, and were written without any error or range checking. As the commenter has stated, it would be worth your while before implementing production versions to read up on strtol() and strtof in detail. (Links are to the Linux man pages, but because both functions are part of the C standard libraries, documentation can also be found on MSDN here and here)
Why not just use fgets and sscanf?
char buf[80];
float n;
if (fgets(buf, 80, stdin) != NULL) {
if (sscanf(buf, "%f", &n) == 1)
printf("%f\n", n);
else
fprintf(stderr, "invalid float\n");
}
I am reading the string from the stdin using fgets function and then trying to print the length of the string, But I am always getting the length of the string as 1 always for the first time
Here is my code
#incldue<stdio.h>
#include<string.h>
int main(void)
{
printf("\n Enter the no of test cases");
scanf("%d",&t);
int i,j;
for(i=0;i<t;++i)
{
char song[500],val[28];
int k=0,x=0;
fgets(song,500,stdin);
int len=strlen(song);
printf("\nlen=%d",len);
}
return 0;
}
I am always getting 1 as the length for the first test case :/
Please suggest where i am going wrong
You are not clearing the input buffer. After giving the input value to first scanf newline will be there. So fgets will not get the input from the user.
Newline will be placed in that buffer in a first(song[0]) position. So this is the reason strlen returns as value 1.
Make this line before the fgets.
int c;
if ( i == 0 )
while((c=getchar()) != '\n' && c != EOF );
fgets(song,500,stdin);
Or else place this line after getting the input from the scanf.
scanf("%d",&t);
while((c=getchar()) != '\n' && c != EOF );
Include \n in scanf input string (and in C declare variables at the beginning of the block { }).
Also notice the len will include the \n char.
#include<stdio.h>
#include<string.h>
int main(void)
{
int t, i;
printf("Enter the no of test cases: ");
scanf("%d\n",&t);
for(i=0;i<t;++i) {
char song[500];
int len;
fgets(song,500,stdin);
len=strlen(song);
printf("len=%d\n",len);
}
return 0;
}
update
If you need to handle weird input just use fgets (\n removed from len).
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(void)
{
char song[500];
int t, i, len;
printf("Enter the no of test cases: ");
fgets(song,500,stdin);
t = atoi(song);
for(i=0;i<t;++i) {
fgets(song,500,stdin);
if ((len=strlen(song)) > 0) {
song[--len] = '\0';
printf("len=%d\n",len);
}
}
return 0;
}
When using scanf (or its relatives), it is important to check the return of the function. scanf returns the number of input values correctly matched and assigned. If there are inappropriate characters or insufficient characters, scanf will experience a matching or input failure. A quick if statement will suffice:
if (!scanf ("%d", &t)) {
fprintf (stderr, "error: invalid type or number for test cases.\n");
return 1;
}
As also noted, fgets will read and include in song the trailing newline character. Generally, you will want to remove the trailing newline to prevent having stray newlines scattered through various strings within your code. (not to mention looking at a length=5 for data is a bit strange) A simple method for removing the newline after your call to fgets is:
len = strlen (song);
while (len && song[len-1] == '\n') /* strip newline */
song[--len] = 0;
Putting together the test of scanf return, emptying the input buffer, and stripping the newline after fgets, your code would look similar to:
#include <stdio.h>
#include <string.h>
int main (void)
{
int c = 0;
int i = 0;
int t = 0;
printf ("\n Enter the no of test cases: ");
if (!scanf ("%d", &t)) {
fprintf (stderr, "error: invalid type or number for test cases.\n");
return 1;
}
while ((c = getchar()) != '\n' && c != EOF);
for (i = 0; i < t; ++i) {
char song[500] = { 0 };
size_t len = 0;
if (printf ("\n case [%d] : ", i) && fgets (song, 500, stdin))
{
len = strlen (song);
while (len && song[len-1] == '\n') /* strip newline */
song[--len] = 0;
}
printf (" len : %zu\n", len);
}
printf ("\n");
return 0;
}
Output
$ ./bin/scanf_rd_int
Enter the no of test cases: 2
case [0] : this is case one - 28 chars.
len : 28
case [1] : this is case two -- 29 chars.
len : 29
I am looking to have a user enter an 8 digit account number. The code I have seems to work for everything unless you have multiple letters, i.e.: 'bbb' as an input. If that happens, it runs the while loop 3 times, displaying the printf, but not asking for another input.
Any input on how to fix this, or a better way to do it is welcome!
Right now, I'm using:
#include <stdio.h>
int main()
{
int return_val = 0;
int account_number = 0;
int within_range = 0;
printf("Please enter your 8 digit account number:\n");
return_val = scanf("%d", &account_number);
getchar();
getchar();
if((account_number > 9999999) && (account_number < 99999999))
{
within_range = 1;
}
while ((return_val != 1) || (within_range != 1))
{
printf("Invalid account number. Account number must be 8 digits.\n");
printf("Please enter your 8 digit account number: \n");
//scanf("%d", &clear);
return_val = scanf("%d", &account_number);
getchar();
getchar();
if((account_number > 9999999) && (account_number < 99999999))
{
within_range = 1;
}
}
printf("Account #: %d", account_number);
}
Will it help if you read the input as a string of characters (using fgets) and parse it from there using sscanf? You will not have to worry about the extra getchars.
#include <stdio.h>
int get_acct_num()
{
char line[80];
int acct_num;
int return_val = 0;
printf("Please enter your 8 digit account number:\n");
fgets ( line, sizeof ( line ), stdin );
return_val = sscanf(line, "%d", &acct_num);
if ( return_val != 1 )
return ( 0 );
if ( ( acct_num < 10000000 ) || ( acct_num > 99999999 ) )
return ( 0 );
return ( acct_num );
}
int main()
{
int account_number = 0;
while ( ! ( account_number = get_acct_num() ) )
printf("Invalid account number. Account number must be 8 digits.\n");
printf("Account #: %d", account_number);
}
In this case it is better to parse a string
#include <ctype.h>
...
char input[200];
scanf("%s", input);
int len = strlen(input);
int dig = 0;
if (len == 8) {
for ( ; dig<len ; dig++) if ( ! isdigit(input[dig])) break;
}
if (dig == 8) printf("OK\n");
else printf("Not ok\n");
the code ensures we have 8 digits and nothing else in input (prints "OK").
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main()
{
int account_number = 0;
int inval = 0;
char acc_buf[256];
printf("Please enter your 8 digit account number:\n");
scanf("%s", acc_buf);
if (strlen(acc_buf) == 8) {
for (int i = 0; i < 8; i++)
if (!isdigit(acc_buf[i])) {
inval++;
break;
}
} else inval++;
if (!inval) {
account_number = atoi(acc_buf);
printf("Account #: %d\n", account_number);
}
return 0;
}
May I suggest a little rewrite?
#include <stdio.h>
#include <string.h> /* this is for strlen */
#include <stdlib.h> /* this is for atoi */
int main()
{
char input [55]; /* this is to store the user input */
int account_number = 0;
printf("Please enter your 8 digit account number:\n");
while (fgets(input, 55, stdin)[0] == '\n')
; /* this is a safer way to get input, loop until there is input, ignore new lines */
account_number = atoi(input); /* convert to an int */
if (account_number < 10000000 || account_number > 99999999)
return -1;
/* quit if invalid input */
printf("Account #: %d\n", account_number);
return 0;
}
EDIT: I've used fgets here and atoi as I think it would be good to be familiar with those functions. Having said that atoi is not necessarily the best way to convert to an number. Strtol is far more reliable but a little more complicated to use.
This is one way to use strtol in this context:
char* temp = 0;
account_number = strtol(input, &temp, 10); /* convert to an int */
More on the subject of converting strings to numbers here.
EDIT-2:
Taking into account chux's comment, the loop can be constructed like this too:
char* out;
do
{
out = fgets(input, 55, stdin);
}
while (out == NULL || out[0] == '\n')
;
I really don't like using scanf(), prefer fgets(), then sscanf().
Details below.
2 key lines:
if (fgets(buf, sizeof(buf), stdin) == NULL)
...
while (1 != sscanf(buf, " %8lu %c", &AccontNummner, &ch));
Solution
#include <stdio.h>
#include <stdlib.h>
// Get 8 digit account number. Returns -1 on I/O error or EOF
// Parsing error just tries again.
long Get8DigitAccountNumber(void) {
const char *prompt = "Enter 8 digit account number: ";
unsigned long AccontNummner;
char ch; // Extra text
char buf[1024];
do { // or while (1)
ch = '\0';
printf(prompt);
fflush(stdout); // Appears to be needed on some systems.
prompt = "Error, try again: "; // Used on re-try
if (fgets(buf, sizeof(buf), stdin) == NULL) {
return -1; // handle I/O error
}
// If not _exactly_ one 1-8 digit field parsed, then try again.
// Leading and trailing whitespaces are OK
} while (1 != sscanf(buf, " %8lu %c", &AccontNummner, &ch));
return (long) AccontNummner;
}
int main() {
long AccontNummner;
while ((AccontNummner = Get8DigitAccountNumber()) >= 0) {
printf("# %lu\n", AccontNummner);
}
return 0;
}
If you want to read exactly 8 digits ...
int n1 = 0;
int n2 = 0;
} while ((1 != sscanf(buf, " %n%8lu%n %c", &n1, &AccontNummner, &n2, &ch) || ((n2 - n1) != 8));
Acceptable format: [optional whitespaces][1-8 digits][optional whitespaces][nothing more]
sscanf() format: " %8lu %c".
Use %u instead of %d to not allow '-'.
Explicitly allow optional leading and trailing spaces.
The %c catches any non-white char after the 8-digit number.
Scanning anything via %c causes sscanf() to return 2.
It's been a while since I fooled with formatted input in C but try scanf("%8d", &account_number);
I've searched in and out of these forums but am still having trouble. My understanding of C is still very basic. I'm creating a small program that takes 3 numerical values entered by the user and then calculates the highest. I nailed that.
I now want to ensure that the user enters only integer values. I managed to get the prog to keep prompting the user to re-enter the value until it is within the specified numerical range (for example, any number between 1 and 1000 inclusive, piece of cake) but that's not good enough. I used 3 while loops to test each value, but this only works as long as the input is of type integer.
The trick is I cant use built in functions. It needs to be manual (sorry, poor choice of words) I tried to use char variables and x = getchar(); to get the ASCII value and test it in a condition but I can't get it working in a loop. (while/ do-while)
I also tried using a "for loop" and array variables but once again am struggling to get it to keep prompting the user.
I've also tried to test the value returned by scanf to see if its integer but my knowledge level of correct C syntax is level: noob. Either my loops don't loop or they loop infinitely.
Here is some sample code:
int x, y, z =0;
printf("Enter the first number:\n");
scanf("d", &x);
while (condition) /* Here is where I need to determine that the entered val is false */
{
printf("Wrong input. Re-enter a valid value.\n");
x =0;
scanf("%d", &x); /*user re-prompted */
}
I'm getting the idea that I'll have to use ASCII and a loop, but I just can't get to it. Also, the values entered get sent to a function for comparing and are then returned.
Could someone give me some advice and a few tips please?
Much thanks
You would have to use something like fgets, and strtol:
long someValue;
char *bufEnd = NULL;
char buf[128]; // max line size
do {
printf("enter a value: ");
fgets(buf, 128, stdin);
someValue = strtol(buf, &bufEnd, 10); // base 10
} while (bufEnd == buf || *bufEnd != '\n');
printf("got value: %li", someValue);
What we are doing here is we are tapping into strtol's capability to tell us where it stopped parsing, by passing in bufEnd.
Then, we are making sure that bufEnd doesn't point to the beginning of buf (in which case, it didn't start with a number), and also checking to make sure that bufEnd points to \n, or the end of the line (making sure that the user didn't enter something like 123abc, which strtol would interpret as 123). You may wish to trim buf of whitespace characters first, however.
You're absolutely on the right track with "scanf()". Just check the return value. If you don't get the expected #/values, then you got invalid input:
char found = FALSE;
int ival;
double x;
while (!found)
{
printf("Please enter a valid integer: ");
if (scanf("%d", &ival) !=1) {
printf ("Invalid! Please re-enter!\n");
continue;
}
printf("Please enter a valid floating point number: ");
if (scanf("%lf", &x) !=1) {
printf ("Invalid! Please re-enter!\n");
continue;
}
found = TRUE;
}
Here's my solution. It safe against buffer overflow and straightforward .
#include <stdio.h>
#define LEN 10
int main() {
int a;
char str[LEN];
fgets( str, LEN, stdin );
while ( !sscanf( str, "%d", &a ) )
fgets( str, 10, stdin );
printf("Num is : %d\n", a);
return 0;
}
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
int getInteger(int* err){
int ch;
int n;//int32
int takeNum, sign;
long long int wk;//long long int as int64
wk=0LL;
*err = 0;
takeNum = 0;//flag
sign = 1;//minus:-1, other:1
/* //skip space character
while(EOF!=(ch=getchar()) && (ch == ' ' || ch == '\t' || ch == '\n'));
ungetc(ch, stdin);
*/
while(EOF!=(ch=getchar())){
if(ch == '-'){
if(takeNum != 0){//in input number
*err = 1;
break;
}
if(sign == -1){//already sign
*err = 2;
break;
}
sign = -1;
continue;
}
if(ch >= '0' && ch <= '9'){//isdigit(ch) in ctype.h
if(takeNum == 0)
takeNum = 1;
wk = wk * 10 + (ch - '0')*sign;
if(INT_MAX < wk || INT_MIN > wk){//overflow
*err = 3;
break;
}
continue;
}
if(ch != '\n'){//input other [-0-9]
*err = 4;
}
break;
}
if(takeNum == 0){//not input number
*err = 5;
} else {
n=wk;
}
while(ch != '\n' && EOF!=(ch=getchar()));//skip to newline
return n;
}
int getValue(const char* redoprompt, int low, int high){
int num, err=0;
while(1){
num = getInteger(&err);
if(err || low > num || high < num)
printf("%s", redoprompt);
else
break;
}
return num;
}
#define max(x,y) ((x)>(y))? (x) : (y)
int main(){
const char *error_message = "Wrong input. Re-enter a valid value.\n";
int x, y, z, max;
x = getValue(error_message, 1, 1000);
y = getValue(error_message, 1, 1000);
z = getValue(error_message, 1, 1000);
max = max(max(x,y), z);
printf("max:%d\n", max);
return 0;
}