I'm attempting to finish a portion of a program but I can't seem to make sense of how to complete a simple problem. In order for the user to get to the next portion of the program they need to enter a value as a char, decimal, and hex. If they don't the control switches to another function.
I was hoping it would be as easy as
if(input != "%c %d %x") etc.
from what I'm reading it seems to be much more confusing than that and I can't seem to find an answer although I'm sure I overlooked an obvious one.
Edit:
int main() {
char input[512];
int n;
user_id = (char *)malloc(100);
printf("Type your user id\n");
my_fgets(user_id, 100, stdin);
printf("Input for phase 1\n");
my_fgets(input, 512, stdin);
phase_1(input);
n = hash2(user_id);
printf("Your number is %d\n", n);
}
void phase_2(char *input, int n)
{
n = n + 100;
if(input != "%c\n")
explode_bomb();
}
You need to write an input function, which reads input into a string, then use isdigit() ischar() isxchar() to scan the string to find out what type the input is.
I'm assuming you expect input to be something like this: C 67 43. The character C along with its value in decimal and hex. You need to write a function to parse this string and determine if it's correct.
First, the parsing. You're on the right track. What you need is sscanf.
char char_char;
unsigned int char_hex;
unsigned int char_int;
if( sscanf(input, "%c %u %x", &char_char, &char_int, &char_hex) < 3 ) {
fprintf(stderr, "%s doesn't look like what I want\n", input);
return false;
}
I chose unsigned int because %x maps to an unsigned integer and char also can safely be compared with an unsigned integer.
Then all you need to do is check if the three values are the same. sscanf has already converted the hex into an unsigned integer, and char can safely be compared to an unsigned integer.
if( char_int != char_hex ) {
fprintf(stderr, "%u and %u don't match\n", char_int, char_hex);
return false;
}
if( char_int != char_char ) {
fprintf(stderr, "%u and %c don't match\n", char_int, char_char);
return false;
}
return true;
Put it all together and wrap a function around it.
#include <stdbool.h>
#include <stdio.h>
bool check_input(const char *input) {
char char_char;
unsigned int char_int;
unsigned int char_hex;
if( sscanf(input, "%c %d %x", &char_char, &char_int, &char_hex) < 3 ) {
fprintf(stderr, "%s doesn't look like what I want\n", input);
return false;
}
if( char_int != char_hex ) {
fprintf(stderr, "%u and %u don't match\n", char_int, char_hex);
return false;
}
if( char_int != char_char ) {
fprintf(stderr, "%u and %c don't match\n", char_int, char_char);
return false;
}
return true;
}
A better function won't print to stderr, but would instead set the error message via a passed in char *.
In order to test if a string complies with a given format, use "*" and "%n".
The * still scans for text that matches the specifier, but suppresses saving and also does not add to the return value.
"%n" directs *scanf() to save the number of char scanned so far.
By testing if n > 0 and if input[n] is the end of the string, code can simple detect a matching format.
int n = 0;
sscanf(input, "%*c %*d %*x %n", &n);
if (n > 0 && input[n] == '\0') {
puts("Success");
}
Related
I have this code and I need help converting the comments to c code
// if the input of scanf() is "q"
{
break;
}
else
{
// convert to int
}
Firstly, how do I check if an input is a certain character. Secondly, how do I turn a string into an integer. Example: "123" -> 123
Things I've tried, that didn't work: (it is possible that I implemented these solutions incorrectly)
how does scanf() check if the input is an integer or character?
Convert char to int in C and C++
I am not using any standard libraries except for stdio.h to print some logging information on the window
you have to know also that any string is terminated by null character which is '\0' to indicate the termination of the string , also you have to check is the user entered characters not numbers and so on (that's not implemented in this code).
I also handled if negative numbers are entered.
but you have to handle if the user entered decimals numbers , to sum up . there are so many cases to handle.
and here the edited code :
#include <stdio.h>
int main(){
char inputString[100];
printf("enter the input:\n");
scanf("%s", &inputString);
if(inputString[0] == 'q' && inputString[1] == '\0' )
{
printf("quiting\n");
//break;
}
else {
int i = 0;
int isNegative = 0;
int number = 0;
// check if the number is negative
if (inputString[0] == '-') {
isNegative = 1;
i = 1;
}
// convert to int
for ( ;inputString[i] != '\0' ; i++) {
number *= 10;
number += (inputString[i] - '0');
}
if(isNegative == 1)
number *= -1;
printf("you entered %d\n", number);
}
return 0;
}
The fundamental question here is, Do you want to use scanf?
scanf is everyone's favorite library function for easily reading in values. scanf has an input specifier, %d, for reading in integers.
And it has a different input specifier, %s, for reading in arbitrary strings.
But scanf does not have any single input specifier that means, "Read in an integer as an integer if the user types a valid integer, but if the user types something like "q", have a way so I can get my hands on that string instead."
Unless you want to move mountains and implement your own general-purpose input library from scratch, I think you have basically three options:
Use scanf with %d to read integers as integers, but check scanf's return value, and if scanf fails to read an integer, use that failure to terminate input.
Use scanf with %s to read the user's input as a string, so you can then explicitly test if it's a "q" or not. If not, convert it to an integer by hand. (More on this below.)
Don't use scanf at all. Use fgets to read the user's input as a whole line of text. Then see if it's a "q" or not. If not, convert it to an integer by hand.
Number 1 looks something like this:
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(scanf("%d", &i) != 1) {
/* end of input detected */
break;
}
do something with i value just read;
}
The only problem with this solution is that it won't just stop if the user types "q", as your original problem statement stipulated. It will also stop if the user types "x", or "hello", or control-D, or anything else that's not a valid integer. But that's also a good thing, in that your loop won't get confused if the user types something unexpected, that's neither "q" nor a valid integer.
My point is that explicitly checking scanf's return value like this is an excellent idea, in any program that uses scanf. You should always check to see that scanf succeeded, and do something different if it fails.
Number 2 would look something like this:
char tmpstr[20];
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(scanf("%19s", tmpstr) != 1) {
printf("input error\n");
exit(1);
}
if(strcmp(tmpstr, "q") == 0) {
/* end of input detected */
break;
}
i = atoi(tmpstr); /* convert string to integer */
do something with i value just read;
}
This will work well enough, although since it uses atoi it will have certain problems if the user types something other than "q" or a valid integer. (More on this below.)
Number 3 might look like this:
char tmpstr[20];
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(fgets(tmpstr, 20, stdin) == NULL) {
printf("input error\n");
exit(1);
}
if(strcmp(tmpstr, "q\n") == 0) {
/* end of input detected */
break;
}
i = atoi(tmpstr); /* convert string to integer */
do something with i value just read;
}
One thing to note here is that fgets includes the newline that the user typed in the string it returns, so if the user types "q" followed by the Enter key, you'll get a string back of "q\n", not just "q". You can take care of that either by explicitly looking for the string "q\n", which is kind of lame (although it's what I've done here), or by stripping the newline back off.
Finally, for both #2 and #3, there's the question of, what's the right way to convert the user's string to an integer, and what if it wasn't a valid integer? The easiest way to make the conversion is to call atoi, as my examples so far have shown, but it has the problem that its behavior on invalid input is undefined. In practice, it will usually (a) ignore trailing nonnumeric input and (b) if there's no numeric input at all, return 0. (That is, it will read "123x" as 123, and "xyz" as 0.) But this behavior is not guaranteed, so these days, most experts recommend not using atoi.
The recommended alternative is strtol, which looks like this:
char *endp;
i = strtol(tmpstr, &endp, 10); /* convert string to integer */
Unlike atoi, strtol has guaranteed behavior on invalid input. Among other things, after it returns, it leaves your auxiliary pointer endp pointing at the first character in the string it didn't use, which is one way you can determine whether the input was fully valid or not. Unfortunately, properly dealing with all of the ways the input might be invalid (including trailing garbage, leading garbage, and numbers too big to convert) is a surprisingly complicated challenge, which I am not going to belabor this answer with.
Here are some guidelines:
scanf("%s", &var) is incorrect: you should pass the maximum number of characters to store into the array var and pass the array without the & as it will automatically convert to a pointer to its first element when passed as an argument:
char var[100];
if (scanf("%99s", var) != 1) {
printf("premature end of file\n");
return 1;
}
to compare the string read to "q", you can use strcmp() declared in <string.h>:
if (strcmp(var, "q") == 0) {
printf("quitting\n");
return 0;
}
to convert the string to the number it represents, use strtol() declared in <stdlib.h>:
char *p;
long value = strtol(var, &p, 0);
testing for a proper conversion is tricky: strtol() updated p to point to the character after the number and set errno in case of range error:
errno = 0;
char *p;
long value = strtol(var, &p, 0);
if (p == var) {
printf("not a number: %s\n", p);
return 1;
}
if (*p != '\0') {
printf("extra characters: %s\n", p);
return 1;
}
if (errno) {
printf("conversion error: %s\n", strerror(errno));
return 1;
}
printf("the number entered is: %ld\n", value);
return 0;
Here is a complete program:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char var[100];
char *p;
long value;
printf("Enter a number: ");
if (scanf("%99s", var) != 1) {
printf("premature end of file\n");
return 1;
}
if (strcmp(var, "q") == 0) {
printf("quitting\n");
return 0;
}
errno = 0;
value = strtol(var, &p, 0);
if (p == var) {
printf("not a number: %s\n", p);
return 1;
}
if (*p != '\0') {
printf("extra characters: %s\n", p);
return 1;
}
if (errno) {
printf("conversion error: %s\n", strerror(errno));
return 1;
}
printf("the number entered is: %ld\n", value);
return 0;
}
You can try this: (Assuming only positive integers needs to convert)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// Write C code here
char var[100];
int numb_flag=1, i=0,number=0;
scanf("%s",var);
while(var[i]!='\0') { // Checking if the input is number
if(var[i]>=48 && var[i]<=57)
i++;
else {
numb_flag = 0;
break;
}
}
if(numb_flag==1) {
number = atoi(var);
printf("\nNumber: %d",number);
} else {
printf("\nNot A Number");
}
return 0;
}
//Mind that in order to be more precise you could also use atof().
//The function works the same way as atoi() but is able to convert float
// (returned value is 'double') the string s
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXILEN 100 /*change the value based on your needs*/
int main(){
int n, lim = MAXILEN;
char s[MAXILEN], *p = s, c;
/*taking the string from the input -- I do not use scanf*/
while(--lim > 0 && (c = getchar()) != EOF && c != '\n')
*p++ = c;
//here you can also do the check (if you want the '\n' char):
//if(c == '\n')
// *s++ = c;
*p = '\0';
if(s[0] == 'q' && s[1] == '\0')
exit(EXIT_SUCCESS); /*change the argument based on your needs*/
else
n = atoi(s);
printf("[DEBUG]: %d\n", n);
}
How to read the input with scanset like[0-9] or [a-z] in sscanf function in C.We have not been able to successfully implement a scanset to scan through a string successfully.
This is my example:
void printResult(char * test, char * actual, char * expected)
{
printf("\n\n%d. %s\r",testcount++, test);
if(strcmp(actual,expected) == 0)
{
printf("SUCCESS\r");
printf("Output:%s", actual);
}
else
{
printf("FAILED\r");
printf("Expected Output:%s Actual Output:%s", expected, actual);
}
}
int main()
{
char buffer[] = "250MEL\r";
char * pBuffer = buffer;
char output[5]={0};
//1
sscanf(pBuffer,"%[ 0-9 ]s",output);
printResult("%[0-9]s", output, "250");
//2
sscanf(pBuffer,"%3[ 0-9 ]s",output);
printResult("%3[0-9]s", output, "250");
return 0;
}
can you anyone help me to use a scanset correctly.
TL;DR It isn't clear what makes you think your scan sets failed; they work OK in this context for me. The mildly modified code shown below demonstrates this.
As I noted in a comment, a scan set is of the form %[…] — it stops at the ] and anything following (s in the code in the question) is not part of the scan set. If you need to include ] in the scan set, it must be the first character (after the caret that negates a scan set, if you're using a negated scan set). With the s after the scan set, if the input includes an s after the end of the digit and blank sequence (the first blank in the scan set is significant; the second, being a repeat, is not), then that character will be 'consumed' and the next input operation would continue after the s; if the next character is not an s, then it is left in the input for the next input operation. Also, if the next character is not an s, the matching fails, but there is no way for sscanf() to report that when the scan set is the last or only conversion specification in the format string. Trailing context is always expendable; it's absence cannot be detected.
Your code is curious in that it uses \r in a number of places. You will seldom need to use \r in C code — you should use \n in your code (or blanks, or …).
Here's a program closely based on yours, with some changes. The code checks the return value from sscanf(); it replaces most of the carriage returns with some other character(s); it keeps the faulty format strings; it makes the argument to the print function match the argument to the sscanf() function.
#include <stdio.h>
#include <string.h>
static int testcount = 1;
static void printResult(char *test, char *actual, char *expected)
{
printf("\n\n%d. [%s]: ", testcount++, test);
if (strcmp(actual, expected) == 0)
{
printf("SUCCESS ");
printf("Output: [%s]\n", actual);
}
else
{
printf("FAILED ");
printf("Expected Output: [%s], Actual Output: [%s]\n", expected, actual);
}
}
int main(void)
{
char buffer[] = "250MEL\r";
char *pBuffer = buffer;
char output[5] = {0};
// 1
if (sscanf(pBuffer, "%[ 0-9 ]s", output) != 1)
printf("scanf() 1 failed\n");
printResult("%[ 0-9 ]s", output, "250");
// 2
if (sscanf(pBuffer, "%3[ 0-9 ]s", output) != 1)
printf("scanf() 1 failed\n");
printResult("%3[ 0-9 ]s", output, "250");
return 0;
}
It also produces the expected output:
1. [%[ 0-9 ]s]: SUCCESS Output: [250]
2. [%3[ 0-9 ]s]: SUCCESS Output: [250]
If you weren't seeing SUCCESS before, that's because the \r character moves the writing position to the start of the line, so what followed overwrote SUCCESS.
Auxilliary question
Also, please let me know how can I set a range for A-Z like below — but this is not working:
sscanf(pBuffer,"%*[A-Z]s",output);
printResult("%[A-Z]s",output, "MEL" );
Please pay attention!
Your comment is showing that you still think %[…]s is the notation for a scan set but the s is spurious; it is not part of the scan set notation. Stop thinking of %[…] as a modifier for %s; it isn't a modifier. It is a completely independent conversion specification, almost wholly unrelated to and syntactically quite distinct from %s. The square-bracket notation is also, categorically and unequivocally, not a part of any standard printf() conversion specification syntax.
Here is some revised code loosely based on the previous part of the answer (and hence on the code in the question). It isn't stellar, but it does show some useful information.
#include <stdio.h>
#include <string.h>
static int strings_match(const char *actual, const char *expected);
static void printResult(const char *format, const char *data, const char *act1,
char *exp1, const char *act2, char *exp2);
int main(void)
{
char buffer1[] = "250MEL#93";
char buffer2[] = " 250 \t\tMELabc";
char number[5] = "";
char letters[5] = "";
const char fmt1[] = "%4[0-9]%4[A-Z]";
const char fmt2[] = " %4[0-9] %4[A-Z]";
if (sscanf(buffer1, fmt1, number, letters) != 2)
printf("sscanf() 1 failed\n");
else
printResult(fmt1, buffer1, number, "250", letters, "MEL");
if (sscanf(buffer2, fmt2, number, letters) != 2)
printf("sscanf() 2 failed\n");
else
printResult(fmt2, buffer2, number, "250", letters, "MEL");
number[0] = '\0';
letters[0] = '\0';
if (sscanf(buffer2, fmt1, number, letters) != 2)
printf("sscanf() 3 failed\n");
else
printResult(fmt2, buffer1, number, "250", letters, "MEL");
const char fmt3[] = "%4[0-9]s%c";
const char fmt4[] = "%4[0-9]%c";
char buffer3[] = "9876sun";
char buffer4[] = "9876moon";
char letter;
if (sscanf(buffer3, fmt3, number, &letter) != 2)
printf("sscanf() 4 failed\n");
else
printf("Data [%s], Format [%s], Output [%s] %c\n", buffer3, fmt3, number, letter);
if (sscanf(buffer3, fmt4, number, &letter) != 2)
printf("sscanf() 5 failed\n");
else
printf("Data [%s], Format [%s], Output [%s] %c\n", buffer3, fmt4, number, letter);
if (sscanf(buffer4, fmt3, number, &letter) != 2)
printf("sscanf() 6 failed\n");
else
printf("Data [%s], Format [%s], Output [%s] %c\n", buffer4, fmt3, number, letter);
return 0;
}
static int strings_match(const char *actual, const char *expected)
{
int rc;
if (strcmp(actual, expected) == 0)
{
rc = 1;
printf(" Output: [%s]", actual);
}
else
{
rc = 0;
printf(" Expected Output: [%s], Actual Output: [%s]", expected, actual);
}
return rc;
}
static int testcount = 1;
static void printResult(const char *format, const char *data, const char *act1,
char *exp1, const char *act2, char *exp2)
{
printf("Format: %d. [%s] Data: [%s]", testcount++, format, data);
int t1 = strings_match(act1, exp1);
int t2 = strings_match(act2, exp2);
if (t1 == 1 && t2 == 1)
printf(" - SUCCESS\n");
else
printf(" - FAILED\n");
}
Output:
Format: 1. [%4[0-9]%4[A-Z]] Data: [250MEL#93] Output: [250] Output: [MEL] - SUCCESS
Format: 2. [ %4[0-9] %4[A-Z]] Data: [ 250 MELabc] Output: [250] Output: [MEL] - SUCCESS
sscanf() 3 failed
Data [9876sun], Format [%4[0-9]s%c], Output [9876] u
Data [9876sun], Format [%4[0-9]%c], Output [9876] s
sscanf() 6 failed
Note how the last two successful conversion lines differ — the s in the format string is matched as a literal to the data, leaving u to be read into the character, compared with when there is no s in the format string and the character matches the s. In contrast, when the format looks for an s and finds m, the overall sscanf() fails — it only manages 1 instead of 2 conversion specifications.
int main()
{
int f;
printf("Type your age");
scanf("%d", &f);
if(!isdigit(f))
{
printf("Digit");
}
else
{
printf("Is not a digit");
}
return 0;
}
No matter if a typed 6 or a always shows me the "Digit" message
isdigit() should be passed a char not an int. And your if-else logic is reversed:
int main() {
char f;
printf("Type your age");
scanf("%c", &f);
if (isdigit(f)) {
printf("Digit");
} else {
printf("Is not a digit");
}
return 0;
}
As mentioned in the comments, this will only work for a single digit age. Validating input is a major topic under the 'C' tag, a search will reveal many approaches to more robust validation.
%d is an integer specifier. Change int f to char f and parse as a character. You are always passing an int into isdigit, which is why it is always true.
There's actually no need to use isdigit at all here since scanf with the %d format specifier already guarantees that the characters will be digits with an optional leading sign. And there's a separate specifier to get rid of the leading sign, %u.
If what you input isn't of the correct format, scanf will tell you (since it returns the number of items successfully scanned).
So, for a simple solution, you can just use something like:
unsigned int age;
if (scanf("%u", &age) == 1) {
puts("Not a valid age");
return 1;
}
// Now it's a valid uint, though you may want to catch large values.
If you want robust code, you may have to put in a little more effort than a one-liner scanf("%d") - it's fine for one-time or throw-away programs but it has serious shortcomings for code intended to be used in real systems.
First, I would use the excellent string input routine in this answer(a) - it pretty much provides everything you need for prompted and checked user input.
Once you have the input as a string, strtoul allows you to do the same type of conversion as scanf but with the ability to also ensure there's no trailing rubbish on the line as well. This answer (from the same author) provides the means for doing that.
Tying that all together, you can use something like:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
// Code to robustly get input from user.
#define OK 0 // Return codes - okay.
#define NO_INPUT 1 // - no input given.
#define TOO_LONG 2 // - input was too long.
static int getLine (
char *prmpt, // The prompt to use (NULL means no prompt).
char *buff, // The buffer to populate.
size_t sz // The size of the buffer.
) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
// Code to check string is valid unsigned integer and within range.
// Returns true if it passed all checks, false otherwise.
static int validateStrAsUInt(
char *str, // String to evaluate.
unsigned int minVal, // Minimum allowed value.
unsigned int maxVal, // Maximum allowed value.
unsigned int *pResult // Address of item to take value.
) {
char *nextChar;
unsigned long retVal = strtoul (str, &nextChar, 10);
// Ensure we used the *whole* string and that it wasn't empty.
if ((nextChar == str) || (*nextChar != '\0'))
return 0;
// Ensure it's within range.
if ((retVal < minVal) || (retVal > maxVal))
return 0;
// It's okay, send it back to caller.
*pResult = retVal;
return 1;
}
// Code for testing above functions.
int main(void) {
int retCode;
unsigned int age;
char buff[20];
// Get it as string, detecting input errors.
retCode = getLine ("Enter your age> ", buff, sizeof(buff));
if (retCode == NO_INPUT) {
printf ("\nError, no input given.\n");
return 1;
}
if (retCode == TOO_LONG) {
printf ("Error, input too long [%s]\n", buff);
return 1;
}
// Check string is valid age.
if (! validateStrAsUInt(buff, 0, 150, &age)) {
printf("Not a valid age (0-150)\n");
return 1;
}
// It's okay, print and exit.
printf("Age is valid: %u\n", age);
return 0;
}
(a) I'm reliably informed the author is actually quite clever, and very good looking :-)
I made this code:
/*here is the main function*/
int x , y=0, returned_value;
int *p = &x;
while (y<5){
printf("Please Insert X value\n");
returned_value = scanf ("%d" , p);
validate_input(returned_value, p);
y++;
}
the function:
void validate_input(int returned_value, int *p){
getchar();
while (returned_value!=1){
printf("invalid input, Insert Integers Only\n");
getchar();
returned_value = scanf("%d", p);
}
}
Although it is generally working very well but when I insert for example "1f1" , it accepts the "1" and does not report any error and when insert "f1f1f" it reads it twice and ruins the second read/scan and so on (i.e. first read print out "invalid input, Insert Integers Only" and instead for waiting again to re-read first read from the user, it continues to the second read and prints out again "invalid input, Insert Integers Only" again...
It needs a final touch and I read many answers but could not find it.
If you don't want to accept 1f1 as valid input then scanf is the wrong function to use as scanf returns as soon as it finds a match.
Instead read the whole line and then check if it only contains digits. After that you can call scanf
Something like:
#include <stdio.h>
int validateLine(char* line)
{
int ret=0;
// Allow negative numbers
if (*line && *line == '-') line++;
// Check that remaining chars are digits
while (*line && *line != '\n')
{
if (!isdigit(*line)) return 0; // Illegal char found
ret = 1; // Remember that at least one legal digit was found
++line;
}
return ret;
}
int main(void) {
char line[256];
int i;
int x , y=0;
while (y<5)
{
printf("Please Insert X value\n");
if (fgets(line, sizeof(line), stdin)) // Read the whole line
{
if (validateLine(line)) // Check that the line is a valid number
{
// Now it should be safe to call scanf - it shouldn't fail
// but check the return value in any case
if (1 != sscanf(line, "%d", &x))
{
printf("should never happen");
exit(1);
}
// Legal number found - break out of the "while (y<5)" loop
break;
}
else
{
printf("Illegal input %s", line);
}
}
y++;
}
if (y<5)
printf("x=%d\n", x);
else
printf("no more retries\n");
return 0;
}
Input
1f1
f1f1
-3
Output
Please Insert X value
Illegal input 1f1
Please Insert X value
Illegal input f1f1
Please Insert X value
Illegal input
Please Insert X value
x=-3
Another approach - avoid scanf
You could let your function calculate the number and thereby bypass scanf completely. It could look like:
#include <stdio.h>
int line2Int(char* line, int* x)
{
int negative = 0;
int ret=0;
int temp = 0;
if (*line && *line == '-')
{
line++;
negative = 1;
}
else if (*line && *line == '+') // If a + is to be accepted
line++; // If a + is to be accepted
while (*line && *line != '\n')
{
if (!isdigit(*line)) return 0; // Illegal char found
ret = 1;
// Update the number
temp = 10 * temp;
temp = temp + (*line - '0');
++line;
}
if (ret)
{
if (negative) temp = -temp;
*x = temp;
}
return ret;
}
int main(void) {
char line[256];
int i;
int x , y=0;
while (y<5)
{
printf("Please Insert X value\n");
if (fgets(line, sizeof(line), stdin))
{
if (line2Int(line, &x)) break; // Legal number - break out
printf("Illegal input %s", line);
}
y++;
}
if (y<5)
printf("x=%d\n", x);
else
printf("no more retries\n");
return 0;
}
Generally speaking, it is my opinion that you are better to read everything from the input (within the range of your buffer size, of course), and then validate the input is indeed the correct format.
In your case, you are seeing errors using a string like f1f1f because you are not reading in the entire STDIN buffer. As such, when you go to call scanf(...) again, there is still data inside of STDIN, so that is read in first instead of prompting the user to enter some more input. To read all of STDIN, you should do something the following (part of code borrowed from Paxdiablo's answer here: https://stackoverflow.com/a/4023921/2694511):
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // Used for strtol
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
#define NaN 3 // Not a Number (NaN)
int strIsInt(const char *ptrStr){
// Check if the string starts with a positive or negative sign
if(*ptrStr == '+' || *ptrStr == '-'){
// First character is a sign. Advance pointer position
ptrStr++;
}
// Now make sure the string (or the character after a positive/negative sign) is not null
if(*ptrStr == NULL){
return NaN;
}
while(*ptrStr != NULL){
// Check if the current character is a digit
// isdigit() returns zero for non-digit characters
if(isdigit( *ptrStr ) == 0){
// Not a digit
return NaN;
} // else, we'll increment the pointer and check the next character
ptrStr++;
}
// If we have made it this far, then we know that every character inside of the string is indeed a digit
// As such, we can go ahead and return a success response here
// (A success response, in this case, is any value other than NaN)
return 0;
}
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
// (Per Chux suggestions in the comments, the "buff[0]" condition
// has been added here.)
if (buff[0] && buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
void validate_input(int responseCode, char *prompt, char *buffer, size_t bufferSize){
while( responseCode != OK ||
strIsInt( buffer ) == NaN )
{
printf("Invalid input.\nPlease enter integers only!\n");
fflush(stdout); /* It might be unnecessary to flush here because we'll flush STDOUT in the
getLine function anyway, but it is good practice to flush STDOUT when printing
important information. */
responseCode = getLine(prompt, buffer, bufferSize); // Read entire STDIN
}
// Finally, we know that the input is an integer
}
int main(int argc, char **argv){
char *prompt = "Please Insert X value\n";
int iResponseCode;
char cInputBuffer[100];
int x, y=0;
int *p = &x;
while(y < 5){
iResponseCode = getLine(prompt, cInputBuffer, sizeof(cInputBuffer)); // Read entire STDIN buffer
validate_input(iResponseCode, prompt, cInputBuffer, sizeof(cInputBuffer));
// Once validate_input finishes running, we should have a proper integer in our input buffer!
// Now we'll just convert it from a string to an integer, and store it in the P variable, as you
// were doing in your question.
sscanf(cInputBuffer, "%d", p);
y++;
}
}
Just as a disclaimer/note: I have not written in C for a very long time now, so I do apologize in advance if there are any error in this example. I also did not have an opportunity to compile and test this code before posting because I am in a rush right now.
If you're reading an input stream that you know is a text stream, but that you are not sure only consists of integers, then read strings.
Also, once you've read a string and want to see if it is an integer, use the standard library conversion routine strtol(). By doing this, you both get a confirmation that it was an integer and you get it converted for you into a long.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
bool convert_to_long(long *number, const char *string)
{
char *endptr;
*number = strtol(string, &endptr, 10);
/* endptr will point to the first position in the string that could
* not be converted. If this position holds the string terminator
* '\0' the conversion went well. An empty input string will also
* result in *endptr == '\0', so we have to check this too, and fail
* if this happens.
*/
if (string[0] != '\0' && *endptr == '\0')
return false; /* conversion succesful */
return true; /* problem in conversion */
}
int main(void)
{
char buffer[256];
const int max_tries = 5;
int tries = 0;
long number;
while (tries++ < max_tries) {
puts("Enter input:");
scanf("%s", buffer);
if (!convert_to_long(&number, buffer))
break; /* returns false on success */
printf("Invalid input. '%s' is not integer, %d tries left\n", buffer,
max_tries - tries);
}
if (tries > max_tries)
puts("No valid input found");
else
printf("Valid input: %ld\n", number);
return EXIT_SUCCESS;
}
ADDED NOTE: If you change the base (the last parameter to strtol()) from 10 to zero, you'll get the additional feature that your code converts hexadecimal numbers and octal numbers (strings starting with 0x and 00 respectively) into integers.
I took #4386427 idea and just added codes to cover what it missed (leading spaces and + sign), I tested it many times and it is working perfectly in all possible cases.
#include<stdio.h>
#include <ctype.h>
#include <stdlib.h>
int validate_line (char *line);
int main(){
char line[256];
int y=0;
long x;
while (y<5){
printf("Please Insert X Value\n");
if (fgets(line, sizeof(line), stdin)){//return 0 if not execute
if (validate_line(line)>0){ // check if the string contains only numbers
x =strtol(line, NULL, 10); // change the authentic string to long and assign it
printf("This is x %d" , x);
break;
}
else if (validate_line(line)==-1){printf("You Have Not Inserted Any Number!.... ");}
else {printf("Invalid Input, Insert Integers Only.... ");}
}
y++;
if (y==5){printf("NO MORE RETRIES\n\n");}
else{printf("%d Retries Left\n\n", (5-y));}
}
return 0;}
int validate_line (char *line){
int returned_value =-1;
/*first remove spaces from the entire string*/
char *p_new = line;
char *p_old = line;
while (*p_old != '\0'){// loop as long as has not reached the end of string
*p_new = *p_old; // assign the current value the *line is pointing at to p
if (*p_new != ' '){p_new++;} // check if it is not a space , if so , increment p
p_old++;// increment p_old in every loop
}
*p_new = '\0'; // add terminator
if (*line== '+' || *line== '-'){line++;} // check if the first char is (-) or (+) sign to point to next place
while (*line != '\n'){
if (!(isdigit(*line))) {return 0;} // Illegal char found , will return 0 and stop because isdigit() returns 0 if the it finds non-digit
else if (isdigit(*line)){line++; returned_value=2;}//check next place and increment returned_value for the final result and judgment next.
}
return returned_value; // it will return -1 if there is no input at all because while loop has not executed, will return >0 if successful, 0 if invalid input
}
I'm trying to make a while loop that will be used to take an int and a double as input. But sometimes there could be a char in the input which I want to skip, and then continue the loop.
This will skip the loop and I want to still use the scanf:
scanf("%d%lf", &num,&n_double);
while((num != 'a')&&(n_double != 'a'))
First read the whole line, then use sscanf to parse the input line into an integer and a double. If, however parsing fails to give an int and a double, conclude that as the error case, i.e. a char present in the line.
You can try this code:
int main(int argc, char const *argv[])
{
char line[100];
while(fgets(line, 100, stdin)) {
int i;
double d;
if(sscanf(line, "%d %lf", &i, &d) == 2) {
printf("%d %lf\n", i, d);
}
else {
printf("wrong input!\n");
}
}
return 0;
}
Edited as per comment:
from cppreference.com
char *fgets( char *str, int count, FILE *stream );
Parameters
str - pointer to an element of a char array
count - maximum number of characters to write (typically the length
of str)
stream - file stream to read the data from
Return value
str on success, null pointer on failure.
So, while(fgets(str...)) translates to "while fgets doesn't return null", which translates to "while fgets continues to successfully read the input stream (which in this case is stdin, the standard input)".
Please take a look at the documentation for further clarification.
Do you mean input validation?
char line[80];
int ival;
double dval;
fgets(line, 80, stdin);
if (sscanf(line, "%d", &ival) == 1)
/* got an int */
else if (sscanf(line, "%f", &dval) == 1)
/* got a double */
else
fprintf(stderr, "Not int nor double\n");