How to implement strict checking getInt() function? - c

I have written following code to get an integer from keyboard.
It will prompt an Error message until you give a valid integer value (either negative or positive ).
One condition is It has to check every possible test cases
like:
-3.2
5.0
984237.4329
0.343
.434
12344.
adfs34
233adds
3892710492374329
helloIamNotainteger
For all of this tests it should fail.It will pass only for int >=INT_MIN && int <=INT_MAX
value.
My Running code is :
#include<stdio.h>
#include<limits.h>
int min=INT_MIN;
int max=INT_MAX;
int main()
{
char str[50],c; //I have taken size 50 please ignore this
int check;
do
{
int flag=1,i=0,j=0,num=0;
check=0;
printf("Enter an Integer : ");
while((c=getchar())!='\n')
str[i++]=c;
if(str[0] == '-')
{
flag = -1;
j++;
}
for(;j<i;j++)
{
if(str[j] >= '0' && str[j] <= '9')
num=(str[j]-'0') + num*10;
else
break;
}
if(j<i)
{
printf("Not an Integer, Please input an integer \n");
}
else if(num < min || num >max)
{
printf("Integer is out of range,Please input an integer \n");
}
else
{
num *=flag;
printf("The given number is : %d\n",num);
check=1;
}
}while(check == 0);
return 0;
}
One example : For values like this.
83429439803248832409 (It's integer but it should fail because of range )but it passes and give some other integer value.
How to solve this within my code or any better idea to implement getInt() ?

The easiest way to do this is to use standard library functions.
#include <limits.h>
#include <stdlib.h>
int getInt (const char *s)
{
long int n = strtol (s, NULL, 10);
if (n < INT_MIN || n > INT_MAX)
/* handle overflows */
else
return (int) n;
}
To handle other errors, you can several conditions.
#include <errno.h>
int getInt (const char *s, size_t size)
{
const char *pEnd1 = s + size;
char *pEnd2;
long int n;
errno = 0;
n = strtol (s, &pEnd2, 10);
if (n < INT_MIN || n > INT_MAX || errno != 0 || pEnd1 != pEnd2)
/* error */
else
return (int) n;
}

Hey you are trying to store a value which is more than 32768 into the integer(not unsigned). So when you do that it will display some garbage value which was present in the storage variable. As you know that integers in c are having memory limits. Like int datatype of unsigned type can store values from 0 - 65535. So trying to store a number more than that will cause issues. If need to store bigger numbers try using long int datatype. Maybe it might help. But that datatype is also having memory restrictions with values close to 4lakh or something.
Hope that helps.

Although this would better fit as comment to Kirilenko well checking solution, my note would be to long so I'll be posting it at as answer.
The main problem with the converison functions is that it's difficult (strtol()) to impossible (atoi()) to test whether the conversion did what was expected.
So when trying to make things more reliable (as atoi()) and easier to use (as strtol()) solutions like Kirilenko's would be used.
Anyhow the approach provided by Omkant still suffers the misdesign of not being capable if something went wrong in the conversion (from the callers perspective).
So the interface should better be like that of lots of other system function, that return their outcome as function value:
/*
* Tryies to convert the first 'size' characters of 's' to an 'int' and optionally
* writes it to '*result'.
*
* Returns the number of characters converted or any qualified negative error code
* on failure.
*/
int getInt(
const char * s, /* source string to try to be converted to an integer */
size_z size, /* number of digits to be converted (shall not be > strlen(s)) */
int * result /* optional reference to an int to place the conversion result in */
);
And, As a notable side effect, by pass NULL as last parameter one is capable of simply testing whether the conversion would work, and in addtion one could receive the number of digits the resulting integer would need.
To then still be able to use this conversion function as if it was returning its result as a function value, what might be handy in situations, one might like to use the following macro:
GETINT(str, size, result, rc) \
(rc = getInt(str, size, &result), result)
Usage:
char s[] = "42";
int rc = 0;
int i = GETINT(s, 2, i, rc);
if (rc)
/* some error */
else
/* use i */

Here is the working code : please see this , I did that wihtout strtol and it satisfies all conditions and takes only int
#include<stdio.h>
#include<limits.h>
#include<string.h>
int main()
{
int num;
char str[500],c;
int check;
char max[12];
char min[12];
sprintf(max,"%d",INT_MAX);
sprintf(min,"%d",INT_MIN);
do
{
int flag=1,i=0,j=0;
num=0;
check=0;
printf("Enter an Integer : ");
while((c=getchar())!='\n')
str[i++]=c;
str[i]='\0';
if(str[0] == '-')
{
flag = -1;
j++;
}
for(;j<i;j++)
{
if(str[j] >= '0' && str[j] <= '9')
check = 1;
else
{
check = 0;
break;
}
}
if(check == 0)
{
printf("Not an Integer, Please input an integer \n");
}
/************Start of checking integer range **************/
else
{
if( (flag == -1 && (strlen(str) > strlen(min))) ||
(flag == 1 && (strlen(str) > strlen(max))) )
{
check = 0;
printf("Integer is out of range, \n");
}
else if(flag == -1 && (strlen(str) == strlen(min)) )
{
i=0;
while(min[i]!='\0')
{
if (str[i] > min[i])
{
check = 0;
printf("Integer is out of range \n");
break;
}
i++; }
if(check == 1)
{
for(j=1;j<strlen(str);j++)
num=(str[j]-'0')+num*10;
num *=flag;
printf("The given number is : %d\n",num);
}
}
else if(flag == 1 && (strlen(str) == strlen(max)) )
{
i=0;
while(max[i]!='\0')
{
if (str[i] > max[i])
{
check = 0;
printf("Integer is out of range\n");
break;
}
i++;
}
if(check == 1)
{
for(j=0;j<strlen(str);j++)
num=(str[j]-'0')+num*10;
num *=flag;
printf("The given number is : %d\n",num);
}
}
else
{
for(j=0;j<strlen(str);j++)
num=(str[j]-'0')+num*10;
num *=flag;
printf("The given number is : %d\n",num);
}
}
/************End of checking integer range ****************/
}while(check == 0);
return 0;

One easy way would be to compare them as strings. First of all, note that a 32 bit integer cannot be more than 10 digits (and a sign).
int32_t get_int32()
{
char input[13];
char check[12];
int32_t result;
if (scanf("%12s", input) != 1)
/* handle error */
/* ignore rest of number if any */
ungetc('x', stdin);
scanf("%*s");
/* if length is bigger than 11, error */
if (strlen(input) > 11)
/* handle error */
if (sscanf(input, "%"SCNd32, &result) != 1)
/* handle error */
sprintf(check, "%"PRId32, result);
if (strcmp(input, check) != 0)
/* handle error */
return result;
}
Note that the strlen check can be ignored and the strcmp will take care of that. Also note that if you get input and check large enough (say 25), you can safely use int instead of int32_t because you'd know that it can't be more than 64 bits (at least for many years).

Related

How to join two numbers of type char and make them int type

Essentially I have a line which ends in two numbers. I can read the numbers eg) '4' and '1'. I want to concatenate them into '41' and then read that as an int type of value 41. Converting a single character to int is straight forward but how would this work for two (or more) characters?
I am grabbing the characters using:
int first_digit = ctoi(line[1]);
int second_digit = ctoi(line[2]);
where ctoi is defined as :
int ctoi( int c ) // https://stackoverflow.com/a/2279401/12229659
{
return c - '0';
}
Easiest way would be to use a function such as sscanf (provided that line is a proper string)
int num;
if (sscanf(line, "%d", &num) != 1) {
// handle conversion error
}
Although, scanf in general doesn't provide protection from arithmetic overflow, so for a big number it will fail (and you won't be able to track it).
strtol and friends, will fail (and will let you know) when you exceed the range.
You could however build your own function, again with no overflow protection:
#include <ctype.h>
#include <stdlib.h>
int stringToInt(char *str) {
int num = 0;
size_t start = (*str == '-') ? 1 : 0; // handle negative numbers
for (size_t i = start; str[i] != '\0'; i++) {
if (isdigit((unsigned char)str[i]) == 0) { // we have a non-digit
exit(1); // ideally you should set errno to EINVAL and return or terminate
}
num = (num * 10) + (str[i] - '0');
}
return (start) ? -num : num;
}

Scanf() not able to detect wrong input

int i, f;
f = scanf("%d", &i);
When I enter input as 3333333333333333333333 (greater than the capacity of int). Shouldn't the value of f be 0?
Shouldnt the value of f be 0?
With standard C, no. With scanf("%d",&i), on int overflow, the result is undefined.
With scanf() in Unix (of which there are variations), I find no prevention of undefined behavior with overflow.
Best to ditch (not use) scanf() and use fgets() for all user input.
Code could try a textual width limit and a wider type:
intmax_t bigd;
// vv --- width limit
if (scanf("%18jd",&bigd) == 1 && bigd >= INT_MIN && bigd <= INT_MAX) {
d = (int) bigd;
} else {
puts("Oops");
}
Yet that has trouble on novel implementations where int is as wide as intmax_t.
scanf() returns 0 when no int textual input found.
A key design element missing from OP's questions is what should happen to user input that exceeds the int range? Stop reading after the first `"333333333"?
What is best, depends on how OP wants to handle, in detail, error conditions - something not yet stated.
No, it can't be detected that way.
The below is not a portable solution, but it works in gcc12.1, clang14.0 and msvc19.32. It may stop working in later releases.
You need to set errno = 0; first and then check it for range errors:
#include <errno.h>
// ...
errno = 0;
f = scanf("%d",&i);
if(f == 1 && errno != ERANGE) {
// success
}
For portability, read this from an early draft of the C2x standard:
Unless assignment suppression was indicated by a *, the result of the conversion is placed in the object pointed to by the first argument following the format argument that has not already received a
conversion result. If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.
A better (as in portable) option to detect this would be to read into a char[] buffer first and then use strtol() to convert it to a number. From the same standard draft:
The strtol, strtoll, strtoul, and strtoull functions return the converted value, if any. If no conversion could be performed, zero is returned. If the correct value is outside the range of representable values, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX, or ULLONG_MAX is returned (according to the return type and sign of the value, if any), and the value of the macro ERANGE is stored in errno.
Here's a demonstrative program using strtol() (which converts to long):
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
// A wrapper around `strtol` to convert to `int`
int strtoi(const char *str, char **str_end, int base) {
int errno_save = errno;
errno = 0; // clear it from any previous error (must be done)
long result = strtol(str, str_end, base);
if(errno == ERANGE) return result == LONG_MAX ? INT_MAX : INT_MIN;
if(result > INT_MAX || result < INT_MIN) {
errno = ERANGE;
return result > INT_MAX ? INT_MAX : INT_MIN;
}
// success or no conversion could be performed
errno = errno_save; // restore errno
return (int)result;
}
#define Size(x) (sizeof (x) / sizeof *(x))
int main(void) {
const char* strings[] = {
"3333333333333333333333 foo",
"2147483647 will probably succeed",
"2147483648 will probably fail",
"32767 guaranteed success",
"32767xyz",
"xyz",
"123",
""
};
char *end; // this will point at where the conversion ended in the string
for(unsigned si = 0; si < Size(strings); ++si) {
printf("testing \"%s\"\n", strings[si]);
errno = 0; // clear it from any previous error (must be done)
int result = strtoi(strings[si], &end, 10);
if(errno == ERANGE) {
perror(" to big for an int");
} else if(strings[si] == end) {
fprintf(stderr, " no conversion could be done\n");
} else if(*end != '\0' && !isspace((unsigned char)*end)) {
fprintf(stderr, " conversion ok,"
" but followed by a rouge character\n");
} else {
printf(" success: %d rest=[%s]\n", result, end);
}
}
}
Possible output:
testing "3333333333333333333333 foo"
to big for an int: Numerical result out of range
testing "2147483647 will probably succeed"
success: 2147483647 rest=[ will probably succeed]
testing "2147483648 will probably fail"
to big for an int: Numerical result out of range
testing "32767 guaranteed success"
success: 32767 rest=[ guaranteed success]
testing "32767xyz"
conversion ok, but followed by a rouge character
testing "xyz"
no conversion could be done
testing "123"
success: 123 rest=[]
testing ""
no conversion could be done
scanf("%d", &i) does not detect overflow, worse even, scanf() has undefined behavior if the number exceeds the range of the destination type: depending on the implementation, the value of i could be -434809515, -1, 0, INT_MAX or any value including a trap value with or without some undesirable side effects.
The proper way to check the input is to read it as a line in an array of char and to parse it with strtol():
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
char input[120];
char ch;
char *p;
long x;
int i;
printf("Enter an integer: ");
if (!fgets(input, sizeof input, stdin)) {
fprintf(stderr, "missing input\n");
return 1;
}
errno = 0;
x = strtol(input, &p, 0);
if (p == input) {
fprintf(stderr, "invalid input: %s", input);
return 1;
}
if (x < INT_MIN || x > INT_MAX) {
errno = ERANGE;
}
if (errno == ERANGE) {
fprintf(stderr, "number too large: %s", input);
return 1;
}
if (sscanf(p, " %c", &ch) == 1) {
fprintf(stderr, "trailing characters present: %s", input);
return 1;
}
i = (int)x; // we know `x` is in the proper range for this conversion
printf("The number is %d\n", i);
return 0;
}
You can encapsulate these tests in a getint() function:
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
/* read an int from a standard stream:
always update *res with the value read
return 0 on success
return -1 on out of range, value is clamped to INT_MIN or INT_MAX
return -2 on non a number, value is 0
only read characters as needed, like scanf
*/
int getint(FILE *fp, int *res) {
int n = 0;
int ret = 0;
int c;
while (isspace(c = getc(fp)))
continue;
if (c == '-') {
c = getc(fp);
if (!isdigit(c)) {
ret = -2;
} else {
while (isdigit(c)) {
int digit = '0' - c;
if (n > INT_MIN / 10 || (n == INT_MIN / 10 && digit >= INT_MIN % 10)) {
n = n * 10 + digit;
} else {
n = INT_MIN;
ret = -1;
}
c = getc(fp);
}
}
} else {
if (c == '+')
c = getc(fp);
if (!isdigit(c)) {
ret = -2;
} else {
while (isdigit(c)) {
int digit = c - '0';
if (n < INT_MAX / 10 || (n == INT_MAX / 10 && digit <= INT_MAX % 10)) {
n = n * 10 + digit;
} else {
n = INT_MAX;
ret = -1;
}
c = getc(fp);
}
}
}
if (c != EOF)
ungetc(c, fp);
*res = n;
return ret;
}
int main() {
int i, res;
printf("Enter an integer: ");
res = getint(stdin, &i);
switch (res) {
case 0:
printf("The number is %d.", i);
break;
case -1:
printf("Number out of range: %d, res=%d.\n", i, res);
break;
default:
printf("Invalid or missing input, res=%d.\n", res);
break;
}
return 0;
}

(C) Get safe int input

So I tasked myself to write a function, that:
overwrites an int with a safe value (not return gibberish if the
user decides to input char-s or anything bigger by absolute value
than (2^31-1)
if input exceeds (2^31 - 1) (meaning if the user inputs 8 or more
digits) the int must be overwritten with the upper value
Here is the code:
void getSafeIntWithBoundaries(int *dest, int lo, int hi, const char *message);
bool anyChars(const char *input, int len);
int main() {
int x;
getSafeIntWithBoundaries(&x, 1, 10, "Enter an integer between 0 and 10.");
printf("x = %d\n", x);
return 0;
}
void getSafeIntWithBoundaries(int * dest, int lo, int hi, const char * message) {
char input[33];
while (1) {
puts(message);
fgets(input, 33, stdin);
int len = strlen(input);
if (input[len - 1] == '\n') { input[len - 1] = '\0'; }
--len;
if (bool reset = anyChars(input, len)) {
puts("Try again.");
continue;
}
else {
int ret;
if (strcmp("2147483648", input) < 0) {
*dest = hi;
return;
}
sscanf(input, "%d", &ret);
ret = ret > hi ? hi : ret;
ret = ret < lo ? lo : ret;
*dest = ret;
break;
}
}
}
bool anyChars(const char * input, int len) {
for(int i = 0; i < len; i++) {
if (!isdigit(input[i])) {
return true;
}
}
return false;
}
A few more notes:
in getSafeIntWithBoundaries(...) I'm getting rid of the '\n', I'm
changing it for a '\0', respectively decreasing int len; which holds
the length of the input.
anyChars() checks whether the input contains any non digit char. If
it does, then the user has to re-enter. One of the problems is
however that in case of failure, message needs to be printed out only
once. If I input something ridiculously long, message will be printed
multiple times. I don't know how to fix this.
the strcmp() bit checks if the user entered a number bigger than
(2^31 - 1). If the user has, then the int must be overwritten with
the high value and the function needs to end. Problem is however, if
the user enters a very long number, the target int will be
overwritten with the low boundary. I don't know how to fix that
either.
2 ?s making sure the target int won't exceed its boundaries. I marked
the parts that I can't figure out with bold, essentially that's the
whole question.
Suggestions on improving the code are welcomed as well.
Suggestions on improving the code are welcomed
Code fails many cases
Overflow UB
When the range exceed int, sscanf(input, "%d", &ret) is undefined behavior.
Long lines not consumed
When input is more than 32 characters (including the '\n), left over input remains.
Null character input
Input starting with a null character '\0' lead to undefined behavior with input[len - 1]
Non ASCII input
isdigit(input[i]) is undefined behavior when input[i] < 0.
Assumed ranged
Code uses int assuming it covers the range 2^31 - 1. C requires int to have a
minimum range of [-32,767 ... 32,767].
Unclear goals
"if input exceeds (2^31 - 1) (meaning if the user inputs 8 or more digits)" --> What if input is `"0000000000000000000000000000000000001\n"? 35 zeros? It is in range yet exceeds 8 digits and exceed 33 character buffer.
End-of-file
puts("Try again."); does not make sense if input is closed. I'd expect int getSafeIntWithBoundaries() to return 1 on success, 0 on failure, EOF on end-of-file/input error.
Below is some untested code - will test later. I'll work on the message details later. It is certainty more than what one might think is needed to simply read an `int, but if you want robust code, it is work.
To read an entire line of input obliges reading until '\n' or EOF.
I'd tolerate leading and trailing spaces.
strtol() is good , but then the entire line needs to be read first. Recall valid input can have many leading spaces or zeros.
Do not overflow intmath- it is UB. Summing the value with negativesint` has greater range than the positive side.
Pre-C99 /,% has implementation defined behavior when the remainder is non-zero - so I avoided that.
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#define INT_MIN_LS_DIGIT ((-(INT_MIN + 10)) % 10)
#define INT_MIN_DIV_10 ((INT_MIN + INT_MIN_LS_DIGIT)/10)
int getSafeIntWithBoundaries(int * dest, int lo, int hi, const char *message) {
fputs(message, stdout);
fflush(stdout); // Insure data to sent out completely
int ch;
while (isspace((ch = fgetc(stdin))) && (ch != '\n')) {
;
}
bool positive = true;
if (ch == '-' || ch == '+') {
positive = ch == '+';
ch = fgetc(stdin);
}
bool digit_found = false;
bool overflow = false;
int sum = 0;
while (isdigit(ch)) {
digit_found = true;
int digit = ch = '0';
// Detect possible overflow
if (sum <= INT_MIN_DIV_10
&& (sum < INT_MIN_DIV_10 || digit > INT_MIN_LS_DIGIT)) {
sum = INT_MIN;
overflow = true;
} else {
sum = sum * 10 - digit;
}
}
if (positive) {
if (sum < -INT_MAX) {
sum = INT_MAX;
overflow = true;
} else {
sum = -sum;
}
}
if (sum > hi) {
sum = hi;
overflow = true;
}
if (sum < lo) {
sum = lo;
overflow = true;
}
*dest = sum;
while (isspace(ch) && ch != '\n') {
ch = fgetc(stdin);
}
if (ch == EOF && iserror(stdin)) {
return EOF; // Rare input error detected
}
if (!digit_found) {
return 1; // or a "No digit found" error code
}
if (overflow) {
errno = ERANGE;
return 1; // or a "Overflow" error code
}
if (ch != '\n' && ch != EOF) {
return 1; // or a "Extra trailing junk" error code
}
return 0;
}
strtol could be used to parse an integer from a string. It provides for overflow and the pointer to the last character allows for testing for valid terminating characters. This set the range to 0 and INT_MAX but any range from INT_MIN to INT_MAX could be used. The terminating character is nul but could be comma, semicolon or any appropriate character.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
//inputs
// char *line : pointer to text to be parsed
// char **next : pointer to pointer to allow modification of caller's pointer
// char *term : pointer to characters to be considered terminators
// int *value : pointer to int to allow modification of caller's int
// int min : minimum value of range
// int max : maximum value of range
// returns : 0 failure or 1 success
int get_int_range ( char *line, char **next, char *delim, int *value, int min, int max)
{
long int input = 0;
char *end = NULL;//will point to end of parsed value
if ( line == NULL) {
return 0;
}
errno = 0;
input = strtol ( line, &end, 10);//get the integer from the line. end will point to the end of the parsed value
if ( end == line) {// nothing was parsed. no digits
printf ( "input [%s] MUST be a number\n", line);
return 0;// return failure
}
// *end is the character that end points to
if ( *end != '\0' && !( delim && strchr ( delim, *end))) {// is *end '\0' or is *end in the set of term characters
printf ( "problem with input: [%s] \n", line);
return 0;
}
if ( ( errno == ERANGE && ( input == LONG_MAX || input == LONG_MIN))
|| ( errno != 0 && input == 0)){// parsing error from strtol
perror ( "input");
return 0;
}
if ( input < min || input > max) {// parsed value is outside of range
printf ( "input out of range %d to %d\n", min, max);
return 0;
}
if ( next != NULL) {// if next is NULL, caller did not want pointer to end of parsed value
*next = end;// *next allows modification to caller's pointer
}
if ( value == NULL) {
return 0;
}
*value = input;// *value allows modification to callers int
return 1;// success
}
int main( int argc, char *argv[])
{
char line[900] = {'\0'};
int valid = 0;
int number = 0;
do {
printf ( "Enter number or enter quit\n");
fgets ( line, sizeof ( line), stdin);//read a line
if ( strcmp ( line, "quit\n") == 0) {
return 1;// if quit is entered, exit the program
}
line[strcspn ( line, "\n")] = '\0';//remove trailing newline
valid = get_int_range ( line, NULL, "", &number, 0, INT_MAX);// call to parse a value
} while ( !valid);// on failure, keep looping the above
printf ( "input is %d\n", number);
return 0;
}

Reading integers with commas between them

I assumed using strtok would be best because of the formatting of the input.
But I've run into a few problems when trying to detect errors:
an example of a line the program would read:
.data 123,456,89
.data 12, 34, 53 , 64
these are all ok.
My problem is when the input is incorrect, for example:
.data 200 4000 // no comma speration
.data 1, ,3 // ,3 should be an error
.data 4, // the extra , should be an error
.data 12.2 // the .2 should be an error
and so on
My code (SIZE is for buffer size = 30, valid_num goes through the token to see if all the chars are numbers), the idea was to first check the validity of the tokens and add them to a buffer, if all numbers are valid, add the numbers to my data base:
while((sptr = strtok(NULL, ", \t\n")) != NULL){ //this is after reading using strtok before.
if(i < SIZE && valid_num(sptr)){ //buffer is not full and the token contains only numbers
temp_num = atoi(sptr);
if(temp_num >= MIN_VAL && temp_num <= MAX_VAL){ //number is within the required size
buffer[i] = temp_num; /*fill buffer*/
i++;
}
else{
fprintf(stderr, "(%d) Error: %d is out of bounds. Valid numbers are between %d and %d\n", line_count, temp_num, MIN_VAL, MAX_VAL);
}
}
else{
fprintf(stderr, "(%d) Error: %s is not a valid number\n",line_count, sptr);
}
tok_count++;
}
if(i == tok_count){ //if all tokens were read correctly, add the data to database.
DC += add_data(buffer, tok_count, DC, data_Table);
}
else{
if(sptr != NULL){
fprintf(stderr, "(%d) Error: %s is not a digit, .data can only be used for integers\n", line_count, sptr);
}
}
Should I try to do the same but with sscanf, even though the length of the input is unknown?
How can I enforce a certain pattern? number - comma - number ...
Perhaps using a few different strtok inside the loop?
There are many ways to parse the line.
OP's temp_num = atoi(sptr); does not detect overflow as 1) overflow with atoi() is undefined and 2) there is no error return value.
I believe the below will cope with all hostile input. It does not use strtok(), but strtol() to find non-numeric input.
Making use of helper functions provides clarity of each step.
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
void consume_whitespace(char **input) {
while (isspace((unsigned char ) **input))
(*input)++;
}
int parse_int(char **input, int *dest) {
char *endptr;
errno = 0;
long y = strtol(*input, &endptr, 10);
if (*input == endptr) return -1; // no conversion
if (errno) return -1; // overflow
#if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
if (y < INT_MIN || y > INT_MAX) return -1; // overflow
#endif
*input = endptr;
*dest = (int) y;
return 0;
}
int parse_data_line(char *input, const char *prefix, int *dest, int n) {
size_t prefix_length = strlen(prefix);
if (memcmp(input, prefix, prefix_length)) return -1;
input += prefix_length;
int i;
for (i = 0; i < n; i++) {
consume_whitespace(&input);
if (*input == '\0') break;
if (i > 0 && *input++ != ',') return -1;
if (parse_int(&input, &dest[i])) return -1;
}
consume_whitespace(&input);
if (*input) return -1; // extra text
return i;
}
Example usage
#define SIZE 30
int main() {
int numbers[SIZE];
char *input = foo();
int count = parse_data_line(input, ".data", numbers, SIZE);
if (count < 0) puts("Fail");
else bar(numbers, count);
}

A basic/ manual way to check that a value entered by the user is numeric

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;
}

Resources