Difficulty using scanf for input - c

Can someone help me to solve my problem? I have a problem with %[^\n]. When I try to enter a false input the program loop the warning that I wrote, but if I use %s and I enter my string the next statement is not working properly.
#pragma warning (disable:4996)
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(){
char name[30];
char number[12];
int flag, flag1, flag2, flag3;
int i;
printf("Add New Contact\n");
do {
printf("input name [1..30 char]: ");
scanf("%[^\n]", name); fflush(stdin);
if ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z')) {
flag = 1;
}
else {
flag = 0;
printf("First letter of name should be an alphabet (A-Z or a-z)\n");
}
if (strlen(name) > 30) {
flag1 = 0;
printf("Length of name should be between 1 and 30 characters\n");
}
else {
flag1 = 1;
}
} while (flag == 0 || flag1 == 0);
do {
printf("Input phone number[6..12 digits]: ");
scanf("%s", number); fflush(stdin);
for (i = 0; i < strlen(number); i++) {
if (number[i] >= '0' && number[i] <= '9') {
flag2 = 1;
}
else {
flag2 = 0;
}
}
if (flag2 == 0) {
printf("Phone numbers should only contain digits (0-9)\n");
}
if (strlen(number) >= 6 && strlen(number) <= 12) {
flag3 = 1;
}
else {
flag3 = 0;
printf("Length of phone numbers should be between 6 and 12 digits\n");
}
} while (flag2 == 0 || flag3 == 0);
printf("\n");
printf("New contact successfully added!\n");
printf("Press Enter to continue...");
getchar();
getchar();
return 0;
}

Oh by the way, the problem might simply be that the scanf call leaves the newline in the buffer, and if you loop and try again the first character seen will be the newline and scanf should not read any thing.
There are two things you should do: First check what scanf returns, it should return 1 if it read a string. Secondly you should tell scanf to discard any possible leading white-space by adding a space first in the format string: " %[^\n]".
Most scanf formats automatically skips leading white-space, but not when using the "%[" or "%c" formats.
Also, to not worry about writing out of bounds of the array, you should add a length modifier to make sure that scanf doesn't read more input than it can write: " %29[^\n]". If the length of the string is 29 after this, then you should probably read until you reach the end of the line, character by character.

Here is your program fixed:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
// In case you need this -- not needed for this case
void discard_input()
{
char c;
while( ( c = getchar() ) != '\n' && c != EOF );
}
void remove_trailing_newline(char * s)
{
char * ch = s + strlen( s ) - 1;
while( ch != s ) {
if ( *ch == '\n' ) {
*ch = 0;
break;
}
--ch;
}
return;
}
int main(){
char name[30];
char number[12];
int flag, flag1, flag2, flag3;
int i;
printf("Add New Contact\n");
do {
printf("\nInput name [1..30 char]: ");
fgets( name, 30, stdin );
remove_trailing_newline( name );
flag1 = flag = 1;
if ( !isalpha( name[ 0 ] ) ) {
flag = 0;
printf("First letter of name should be an alphabet (A-Z or a-z), found: %s\n", name );
}
// impossible
if (strlen(name) > 30) {
flag1 = 0;
printf("Length of name should be between 1 and 30 characters\n");
}
} while (flag == 0 || flag1 == 0);
do {
printf("\nInput phone number[6..12 digits]: ");
fgets( number, 12, stdin );
remove_trailing_newline( number );
flag2 = flag3 = 1;
int len_phone = strlen( number );
for (i = 0; i < strlen(number); i++) {
if ( !isdigit( number[ i ] ) ) {
flag2 = 0;
}
}
if (flag2 == 0) {
printf("Phone numbers should only contain digits (0-9), found:'%s'\n", number);
}
if ( len_phone < 6 || len_phone > 12) {
flag3 = 0;
printf("Length of phone numbers should be between 6 and 12 digits, found: %d\n", len_phone );
}
} while (flag2 == 0 || flag3 == 0);
printf("\n");
printf( "Name: '%s'\n", name );
printf( "Phone: '%s'\n", number );
printf("New contact successfully added!\n");
printf("Press Enter to continue...");
getchar();
return 0;
}
You can find the program here.
The fixings are more or less interesting, I enumerate they here:
At first, I thought that the problem was that the trailing new line was being left in the input buffer. fflush(stdin) is actually undefined behaviour in C, since the fflush() function is there for output streams. Anyway, I included the code in question 12.26b of the comp.lang.c FAQ, since I think it is interesing to have it as reference. Then, I decided to change scanf() with fgets(). This is due to the scanf() taking spaces as delimiters, so you wouldn't be able to write a complete name, i.e., name and surname. Remember that gets() is not an option, since it writes the input past the limit of the buffer. Actually, fgets() solves this by letting us define a limit of chars to read. The problem is that fgets() also includes the '\n' in the buffer, so, that's why I included the remove_trailing_newline() function. Tricky, isn't it?
You added a condition to check whether the name input had more than thirty chars. Actually, this is impossible to check in your program. First of all, fgets() will read 29 chars + the final char mark (0). Secondly, if you were actually allowing to input more than 30 chars, then the input would be written past the size of the buffer, which is undefined behaviour (crashes in most cases). You would have to use something more complex, like std::string in C++, and then check its length. Or maybe use a third party expandable string for C. Or roll out your own expandable string...
You can decide whether there is an alphabetic char or a digit by using isalpha(c) and isdigit(c) functions.
When you are going to use a value many times, such as strlen(name), then you should precompute it and store it in a local variable. Though a good compiler (its optimizer) will detect this situation and solve it for you, you never know which compiler is going to compile your code, and how advanced it is. Also, there is nothing wrong making things easier for the optimizer.
When you have a situation in which you set a flag for signaling an error condition, it is easier to set it to the "no error" value before checking anything, and solely in case of an error, set it to the "error" value. This will be easier to read, and therefore, to understand.
Hope this helps.

Related

Wanted to check if the value entered is a number or else. "isdigit() is not working here as expected

I am stuck with a problem here in C. I am posting the question and the code I have written below. Here I have to enter 10 numbers in an array and then I need to check how many times a number appeared. But to verify that I have entered a number and not anything else, I have used "isdigit()" function. But this is of no use. Can anyone help me to solve it.
/*
(a) Ten numbers are entered from the keyboard into an array. The number to be searched is entered through the
keyboard by the user. Write a program to find if the number to be searched is present in the array and if it is present, display
the number of times it appears in the array.
*/
#include<stdio.h>
#include<ctype.h>
main()
{
int num[10];
int i, j, cnt=0;
char rept;
printf("Enter 10 numbers: \n\n");
for(i=0; i<=9; i++)
{
printf("Number %d = ", i+1);
scanf("%d", &num[i]);
fflush(stdin);
if ( !isdigit(num[i]) )
{
printf("OK\n");
}
else
{
printf("Invalid number. Enter again.\n");
i=i-1;
}
}
do
{
printf("\nEnter the number to be searched in array: ");
scanf(" %d", &j);
for (i=0 ; i<=24; i++)
{
if(num[i]==j)
cnt++;
}
if(cnt>0)
printf("\nNumber %d is present at %d places", j, cnt);
else
printf("\nNumber not present.");
printf("\n\nDo you want to search another number. Press Y to repeat. Any other key to exit");
fflush(stdin);
scanf("%c", &rept);
}while (rept=='y'||rept=='Y');
getch();
}
No you can't do that. isdigit() is supposed to work with characters and you passed a multigit integer variable.
What you can do is simply like this
if( scanf("%d",&a[i])== 1){
// you can be sure number is entered
}
And fflush(stdin) is undefined behavior.
So the use of scanf will be more prominent if you would do this
int clearstdin(){
int c;
while ((c = getchar()) != '\n' && c != EOF);
return (c == EOF);
}
In main()
int earlyend = 0;
for(size_t i=0; i<SIZE; i++){
...
...
int ret = scanf("%d",&a[i]);
while( ret == 0){
if( clearstdin() ){ /* EOF found */earlyend = 1; break; }
fprintf(stderr,"%s\n","Entered something wrong");
ret = scanf("%d",&a[i]);
}
if( earlyend ){ /*EOF found*/ }
if( ret == EOF) { /* Error occured */}
...
}
The %d conversion specifier will cause scanf to skip over any leading whitespace, then read a sequence of decimal digits, stopping at the first non-digit character. If there are no digit characters in the input (for example, you enter something like ”abc”), then nothing is read from the input stream, a[i] is not updated, and scanf will return 0 to indicate a matching failure.
So, you can do a test like
if ( scanf( “%d”, &a[i] ) == 1 )
{
// user entered valid input
}
But...
This doesn’t fully protect you from bad input. Suppose you enter something like ”123abc” - scanf will read, convert, and assign 123 and return a 1 indicating success, leaving ”abc” in the input stream to potentially foul up the next read.
Ideally, you’d like to reject the whole thing outright. Personally, I do this as follows:
char inbuf[SOME_SIZE]; // buffer to store input
if ( fgets( inbuf, sizeof inbuf, stdin ) ) // read input as text
{
char *chk; // use strtol to convert text to integer
int temp = (int) strtol( inbuf, &chk, 10 ); // first non-digit character written to chk
if ( isspace( *chk ) || *chk == 0 ) // if chk is whitespace or 0, input is valid
{
a[i] = temp;
}
else
{
// bad input
}
}
This still isn’t a 100% solution - it doesn’t make sure the user didn’t enter more characters than the buffer can hold, but it’s a step in the right direction.
Input validation in C is, frankly, a pain in the ass.

How to reject letter when requesting int in C

When I enter a letter the loop runs infinitely. Does a letter store as a zero when it is input as an int? How can I reject a non digit answer, just I have rejected an answer outside the range?
int main(int argc, const char * argv[]) {
// insert code here...
int categoryToScore;
int categoryScores = 6;
printf("Enter category to save score: ");
scanf("%d", &categoryToScore);
while (categoryToScore >= categoryScores || categoryToScore <= 0) {
printf("Error: invalid command. Enter 1-5 to save to an unused category\n");
printf("Enter category to save score: ");
scanf("%d", &categoryToScore);
}
return 0;
}
Just for background
I want to:
print a request an input that is between 1 and an upper bound
scanf for the input
check if the input is of a correct type and within the correct range
if it isn't then print an error message and go back to 1.
if it is then proceed
You are asking scanf to read a number from standard input. Scanf finds a non-digit character in the standard input and does not remove it from the standard input. Scanf fails and returns 0 (the number of fields successfully processed).
The next time you call scanf, it finds the same character at the start of standard input. So the process repeats indefinitely.
One solution is to read stdin one character at a time.
Another solution is to read (and discard) the one character from stdin before calling scanf again.
int main(int argc, const char * argv[]) {
// insert code here...
int categoryToScore;
int categoryScores = 6;
int scantRetVal;
printf("Enter category to save score: ");
scantRetVal = scanf("%d", &categoryToScore);
if (scantRetVal != 1) {
getchar(); // read and discard one character from stdin
categoryToScore = 0;
}
while (categoryToScore >= categoryScores || categoryToScore <= 0) {
printf("Error: invalid command. Enter 1-5 to save to an unused category\n");
printf("Enter category to save score: ");
scantRetVal = scanf("%d", &categoryToScore);
if (scantRetVal != 1) {
getchar(); // read and discard one character from stdin
categoryToScore = 0;
}
}
return 0;
}
Rather than fix this particular program I will show how to solve ANY similar problem using a concept called an "exit condition".
The idea of an exit condition is that you have an infinite loop and it has various exit conditions. Often there are two exit conditions: one for success and one for an error.
while( true ){ /* infinite loop */
char c = ... /* get the character you want */
if( c < '0' || c > '9' ){
printf( "invalid character, not a digit\n" );
continue; // get another character
}
... /* do whatever you with valid data */
if( c == '3' ) break; /* your exit condition, whatever it is */
if( c == '7' ) exit(0); /* exit the whole program */
}
Note: If you are accepting free form input (numbers and strings), scanf is probably not a good idea. scanf accepts very specific, well-formatted input. So if you ask for a %d, then there better be a %d (decimal number) in the input or you will have problems.
For example, if you accept numbers and strings, you should take everything as strings using fgets or something like that.
Here is a complete program that does what you want:
#include <stdio.h>
#include <stdbool.h>
int main(int argc, const char * argv[]) {
int iMaxScore = 6;
int charInput = 0;
int iInputValue = 0;
while( true ){
printf("Enter category to save score: ");
GetInput:
charInput = getchar();
if( charInput == 10 || charInput == 13 ) goto GetInput; /* ignore enter key */
if( charInput == 'q' ) break;
if( charInput < '0' || charInput > '9' ){
printf( "invalid entry, not a digit %d\n", charInput );
break;
}
iInputValue = charInput - '0';
if( iInputValue > iMaxScore ){
printf( "Error, input value exceeds maximum category %d\n", iMaxScore );
continue; /* try again */
}
printf( "you entered category %d\n", iInputValue );
/* continue ... */
}
return 0;
}

Check if input is a string (4 characters only) and if not return to input again

My aim is to accept 4-digit numbers, and 4-character strings (string should not contain digits or special characters)
If an invalid input is given the program should not terminate and it must allow the user to enter the details and continue until he wish to terminate.
I am able to find whether the input is a digit.
if(scanf("%d",&input)!=1)
{
printf("enter the number please");
... // I have option to re enter using while and flags
}
else
{
// I continue my work
...
}
To check it is four digits I have tried using the commands
i=0;
num = input;
while(num>0)
{
i = i+1;
num = num/10;
}
if(i==4){
...//I continue
}
else
printf("please enter four digit");
I have no idea of checking the same for characters. (I know how to check its length using strlen())
Please help me with the code in C. (Also help me to reduce/optimize the above logic to check whether the input is a 4-digit number)
I believe you want 2 inputs a number and a string. You can do that as
int number= 0;
char string[10] = { 0 };
do {
printf("please enter four digit");
scanf("%d", &number);
if(number >=1000 && number<= 9999)
break;
} while(1);
do {
printf("please enter four character string");
fgets(string, sizeof(string), stdin);
if(strlen(string) == 4)
break;
} while(1);
To check it is four digit number you can simply put a check whether the number lies between 1000 and 9999. (I am assuming you don't want the number to start with 0.)
strtol can help:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char s[32], *p;
int x;
fgets(s, sizeof(s), stdin);
if ((p = strchr(s, '\n')) != NULL)
*p = '\0';
x = (int)strtol(s, &p, 10);
if ((p - s) == 4) {
printf("%d\n", x);
} else {
printf("Please enter four digit\n");
}
return 0;
}
char input[16];
int ok = 1, k = 0;
if (scanf("%s", input) > 0 && strlen(input) == 4) {
// check if it's a word
for (; k < 4; k++)
if (!isalpha(input[k])) {
// check if it's a number
for (int k = 0; k < 4; k++)
if (!isdigit(input[k]))
ok = 0;
break;
}
}
else ok = 0;
if (!ok)
printf("invalid input, please enter a 4-digit number or 4-letter word");
else {
printf("valid input");
...
}
You can use gets()1 fgets() to get the whole line and check line length. If the first character is between '0' and '9' then check the remaining if they are 3 numbers too. If the first character is a valid character in string then check the 3 remaining chars if it's also valid in string.
1See Why is the gets function so dangerous that it should not be used?

Validation using scanf() to protect against string

I have a code here that has a job to see if the user input is either string or integer from a range of 1-49. If I enter "asdas" it says invalid, and if I enter a integer from "1-49" it says valid. The problems I am having with this code is that if I enter "2 asda"
it will it count it has valid, and invalid at the same time, and if I enter "2 2" It will consider that valid as well. Just found out it also accepts "2d" as a valid input to.
for (i = 0; i < 6; i++)
{
printf("\nPlease enter the %d winning ticket numbers!: ", i+1);
if (scanf("%d", (&winningNumbers[i])) == 0 || (winningNumbers[i] <= 0) || (winningNumbers[i] >= 50))
{
inputFlush();
printf("\nInvalid Input. Please re-enter.\n") ;
i = i - 1;
}
}
for (i = 0; i < 6; i++)
{
printf("%d, ", winningNumbers[i]);
}
Read the entire line into a string (fgets, line 2 in the snippet). Read data from the string using sscanf: read the integer and one more character, after a space. Check whether sscanf returns something different than 1. If it does then you either have strings in the beginning (it returns 0 since it couldn't read an integer) or you have extra whitespace characters at the end (that is it also matched the %c format specifier). The space is needed to jump over whitespace until the end of the line (including the stored \n).
printf("\nPlease enter the %d winning ticket numbers!: ", i+1);
fgets(buffer, size, stdin);
if (sscanf(buffer, "%d %c", &winningNumbers[i], &c) != 1 || (winningNumbers[i] <= 0) || winningNumbers[i] >= 50))
{
// inputFlush(); not needed now that you read the entire line
printf("\nInvalid Input. Please re-enter.\n") ;
i = i - 1;
}
Look at this example.
#define MAX_LINE_SIZE 500
int main(int argc, char** argv)
{
char line[MAX_LINE_SIZE];
unsigned int num;
char *ptr;
while (fgets(line, MAX_LINE_SIZE, stdin) != NULL){
num = strtol(line, &ptr, 10);
if (line[0] != '\n' && (*ptr == '\n' || *ptr == '\0')) {
printf("Your num: %u\n", num); // check num if you need
} else {
printf("Error\n");
}
}
return 0;
}
The output:
12
Your num: 12
45
Your num: 45
34 2
Error
ads
Error
Here fgets function read data from stdin. strtol parse read string and assign an address of a char after parsed number to ptr pointer. Assuming that user has to input only one number (without any character after) we have to check whether *ptr is new line or end of line.
line[0] != '\n' prevents empty string.
Check the next character using peek, as detailed here. You can tell if it's good or not that way.
Another SO question that's pretty much the same.

detect reading a non-digit using scanf()

Hey everyone thanks for the advise. unfortunately i'm not allowed to use such functions. so i wrote this code, which works great with 1 problem. if i enter lets say 'hhjk' it will freak out. i want to clear the buffer after the first 'h' is detected as non-digit.. heard about the function fflush but i can't get to understand it..
int get_int()
{
char inp; /*inp, for input*/
int number; /*the same input but as integer*/
int flag=0; /*indicates if i need to ask for new input*/
do {
flag=0; /*indicates if i need to ask for new input*/
scanf("%c",&inp);
if (inp<48 || inp>57 ) /*this means it is not a number*/
{
inp=getchar(); /*Here i clear the buffer, the stdin for new input*/
printf("Try again...\n");
flag=1;
}
else
if (inp>53 && inp<58 && flag!=1) /*this means it is a number but not in the 0-5 range*/
{
inp=getchar(); /*here i clear the buffer, the stdin so i can get a new input*/
flag=1;
}
} while (flag);
number=inp-48; /*takes the ascii value of char and make it an integer*/
return number;
}
A simple way is to input a string, then check to make sure everything in there is a character. We can use strtol() to check since it returns a 0 when it can't do the converstion, the only condition is since you want 0 to be valid input, we have to put a special condition on the check:
int main()
{
char input[50]; // We'll input as a character to get anything the user types
long int i = 0;
do{
scanf("%s", input); // Get input as a string
i = strtol(input, NULL, 10); // Convert to int
if (i == 0 && input[0] != '0') { // if it's 0 either it was a numberic 0 or
printf("Non-numeric\n"); // it was not a number
i = -1; // stop from breaking out of while()
}
else if(i<0 || i > 5)
printf("wrong\n");
}while (i < 0 || i >5);
return 0;
}
Another way is to use the seldom seen %[] format for the scanf family. In the code below, I have %[0-9]. This gives us only numbers. ( haven't shown the return code, etc.)
do {
if ((scanf("%[0-9]%c", input, &nl) == 2) && (nl == '\n')) {
value = strtol(input, NULL, 0);
} else {
value = -1;
}
} while ((0 <= value) && (value <= 5));

Resources