In my program, I input an int value into argv[1]. I need to put an if statement like this:
num = 3;
if (argv[1] == num)
{
[...]
}
I get a warning: comparison between pointer and integer [enabled by default]
How can compare those two values?
Remember that argv, as passed to main, is an array of strings.
You can convert a string to an integer with functions like atoi or strtol (the latter is the preferred alternative). Or you convert the integer to a string, and do a strcmp.
num is an integer, while argv[1] is a string that may (or may not) be representing an integer. You can compare only items of the same type, so either compare a string-to-string or an integer-to-integer:
if (strcmp(argv[1], "3") == 0) {
// ...
}
or
if (atoi(argv[i]) == 3) {
// ...
}
The second way will fall apart when you try comparing to zero (atoi returns zero to indicate an error).
num = 3;
if (atoi(argv[1]) == num)
{
[...]
}
The command line arguments are strings. You will need to convert these strings first using atoi (not suggested) or strtol/strtoul (better, has error handling) and then use the converted value to compare with whatever integer you want to compare with.
char *endptr;
errno = 0;
long int n = strtol(argv[ i ], &endptr, 10);
if (endptr == argv[2])
...; /* no conversion */
else if (*endptr != '\0')
...; /* conversion incomplete */
else if (errno == ERANGE)
...; /* out of `long int''s range */
...
You may need to read a number from argv[1] using various methods, then compare with num. (s*scanf)
One that's most specific for you: http://pubs.opengroup.org/onlinepubs/7908799/xsh/strtol.html
Or print num into a string and do a strcmp with argv[1] (s*printf)
Related
I am writing a program in c. The incoming string is like this *H1W000500, this is a legit string and I copy the contents of the string after *H1W i.e 000500 to an integer type.
But I want filter this string if the string is not legit. For example *H1W..... or *H1W~##$, If string is not legit, do not copy content and skip. Only Copy contents if the string is legit as written above.
Here what I am doing, but whenever irrelevant string is there, it copies zero value, which is undesirable.
char ReceivedData[50];
unsigned int Head1Weight;
p = strstr(ReceivedData, "*H1W");
if(p)
{
Head1Weight = strtoul(p+4,&ptr,10);
}
You are close, but your use of strstr can be better expressed with strncmp to compare the first 4 chars of receiveddata. (if your target string exists in the middle of receiveddata, then strstr is fine) You also need to provide error checking on your strtoul conversion. Putting those pieces together you could do something like the following (note: this is shown for a single value, in a loop, change return to continue as noted in the comments)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
/* declare constants, avoid magic number use in code */
enum { PRE = 4, BASE = 10, MAX = 50 };
int main (void) {
char receiveddata[MAX] = "*H1W000500", *p = NULL;
unsigned long head1weight;
if (strncmp (receiveddata, "*H1W", PRE) != 0) /* cmp 4 chars */
return 1; /* you would continue here */
if (strlen (receiveddata) <= PRE) /* more chars exist? */
return 1; /* you would continue here */
errno = 0; /* set errno to known value */
head1weight = (unsigned)strtoul (&receiveddata[PRE], &p, BASE);
/* check for error conversions on conversion */
if ((errno == ERANGE && (head1weight == ULONG_MAX)) ||
(errno != 0 && head1weight == 0)) {
perror ("strtoul");
return 1; /* you would continue here */
}
if (&receiveddata[PRE] == p) { /* check if chars converted */
fprintf (stderr, "No digits were found\n");
return 1; /* you would continue here */
}
printf ("head1weight : %lu\n", head1weight);
return 0;
}
Example Use/Output
$ ./bin/parsetounsigned
head1weight : 500
Look it over and let me know if you have further questions.
(note: C generally avoids the use of MixedCase and camelCase variable names in favor of all lower-case, reserving all upper-case for use with constants and macros. It is style, so it is completely up to you...)
From the Linux man page on strtoul.
If there were no digits at all, strtoul() stores the original value of nptr in *endptr (and returns 0).
So if, after the strtoul, ptr is the same as the starting pointer, you know there were no legitimate characters.
char* ptr;
unsigned long Head1Weight = strtoul(p + 4, &ptr, 10);
if (ptr == p + 4)
{
// There were no digits
}
else if (strlen(ptr) > 0)
{
// There were characters in the string after the end of the number
}
OP's p = strstr(ReceivedData, "*H1W"); if(p) { is insufficient as it passes strstr("abcd12*H1W", "*H1W"), although it is a start.
OP has validation goals that are not specific enough. "the contents of the string after *H1W i.e 000500 to an integer type."
"+123", "-123", " 123" evaluate to integers, are they valid?
"123 " can evaluate to an integers, is that valid?
The sample implies the numeric part should be exactly 6 decimal digits, yet that is uncertain.
Sample code uses unsigned, could that be 16-bit? Only "000000" to "065535" valid?
"-123" converts successful via strtoul(), valid for this goal?
should this pass "*H1W000500xyz"? Is extra text allowed or to be ignored?
This is common in writing code as the specifications initially have interpretation issues and then tend to evolve.
Code should allow for evolution.
Let us start with *H1W followed by exactly 6 decimal digits with sscanf(). Code below uses "%n" to record the scanning position after checking for digits. This approach needs additional work should PREFIX contain %.
// PREFIX should not contain %
#define PREFIX "*H1W"
#define DIGIT_FMT "%*[0-9]"
#define VALID_LENGTH 10
char ReceivedData[50];
unsigned long Head1Weight = 0;
int n = 0;
sscanf(ReceivedData, PREFIX DIGIT_FMT "%n", &n);
if (n == VALID_LENGTH && ReceivedData[VALID_LENGTH] == '\0') {
Head1Weight = strtoul(ReceivedData + sizeof PREFIX - 1, NULL, 10);
}
I want to parse an int from a string (char*) in C. I'm writing a linux kernel module for embedded linux. I'm trying to use simple_strtol found here.
My tokens I'm parsing in an ideal situation will be numbers. The problem I have is "0" is a valid input. strtol returns 0 if a value could not be parsed (error). So that means that the following code:
char* token = "should_fail";
char **endptr;
char value = simple_strtol(token, endptr, 10); // 10 is the base to convert to
if (!**endptr){
printf("failed!");
} else {
printf(value);
}
char* token = "0"; // should pass
char **endptr;
char value = simple_strtol(token, endptr, 10); // 10 is the base to convert to
if (!**endptr){
printf("failed!");
} else {
printf(value);
}
prints out 0 in both cases, when it should fail in the first case. I am trying to use that *endptr argument to check to see if the conversion is successful, but its not working. According to what I've researched, *endptr is "A pointer to the end of the parsed string will be placed here". And I believe if a conversion failed the pointer will point to nothing so I can identify that failure.
Does anybody know how I can parse "0" properly and get a return value of 0 while still identifying a failed parse, and not having it return 0?
You should not create a pointer-to-pointer variable for endptr. Instead declare a single pointer, and pass the address of that with the unary & operator:
char *endptr;
simple_strtol(token, &endptr, 10);
if (endptr == NULL) ...
I didn't read the manual page but basically copied what the OP had used. Froom the manual page:
If the subject sequence is empty or does not have the expected form, no conversion is performed; the value of str is stored in the object pointed to by endptr, provided that endptr is not a null pointer.
This means that if strtol (or your alternative if it's working as specified) returns 0 and endptr == token then the string was invalid.
So the check should be
if (value == 0 && endptr == token)
{
/* String is not a valid number */
}
I am using strtod( ) function to extract an environment variable as a string, and then changing it to double using strtod:
enter code here
char strEnv[32];
strncpy(strEnv, getenv("LT_LEAK_START"), 31);
// How to make sure before parsing that env LT_LEAK_START is indeed a number?
double d = strtod(strEnv, NULL);
Now i want to make sure that this number entered by user is a number and not a string or special character. How can i make sure of that?
A code snippet would be of great help.
Thanks in advance.
The 2nd argument to the strtod function is useful.
char *err;
d = strtod(userinput, &err);
if (*err == 0) { /* very probably ok */ }
if (!isspace((unsigned char)*err)) { /* error */ }
Edit: examples added
The strtod function tries to convert the initial portion of the 1st argument to a double and stops either when there are no more chars, or there is a char that can't be used to make a double.
input result
---------- ----------------------------
"42foo" will return 42
and leave err pointing to the "foo" (*err == 'f')
" 4.5" will return 4.5
and leave err pointing to the empty string (*err == 0)
"42 " will return 42
and leave `err` pointing to the spaces (*err == ' ')
man strtod: If no conversion is performed, zero is returned and the value of nptr is stored in the location referenced by endptr.
char * endptr;
double d = strtod(strEnv, &endptr);
if (strEnv == endptr)
/* invalid number */
else
...
Surely you could do worse than just reading the man page for strtod() and acting upon that. E.g. on my Linux system it says:
RETURN VALUE
These functions return the converted value, if any.
If endptr is not NULL, a pointer to the character after the last character used in the conversion is stored in the location referenced by
endptr.
If no conversion is performed, zero is returned and the value of nptr is stored in the location referenced by endptr.
If the correct value would cause overflow, plus or minus HUGE_VAL (HUGE_VALF, HUGE_VALL) is returned (according to the sign of the value), and
ERANGE is stored in errno. If the correct value would cause underflow, zero is returned and ERANGE is stored in errno.
That pretty much tells you what you need to do in order to handle errors. Also, like Johann Gerell said, you also need to check whether getenv() succeeded; a similar approach works there, i.e. check the man page and write error handling code according to that.
First, check the return value of getenv - if it's NULL, then that environment variable doesn't exist.
Second, if the return value of getenv isn't NULL, then you have the value, as a string.
Third, don't set the char ** endptr parameter of strtod to NULL, but use it to check the validity of the converted value, also check for 0.0.
That second argument to strtod, which you've set to NULL, can be a pointer-to-pointer-to-char; the pointer-to-char that it points to will get set to the character after the last thing strtod managed to parse. If that's the end of the string, or at least there's nothing after it but whitespace, then what you had was a number. Otherwise, it was something else.
I don't know much about this language but I do know that strtod() will return 0.0 if the input is wrong. Maybe you could use a regular expression to validate the input string is a number.
OP's code has issues:
getenv() may return NULL
Consider a null pointer test of the result.
Not certainly a string
char strEnv[32];
strncpy(strEnv, getenv("LT_LEAK_START"), 31);
// strEnv is not certainly a string as it may lack a null character.
strEnv[31] = 0; // Add
The end pointer of strtod() is useful, yet deserves more testing
// Conversion problems not detected. See following.
double d = strtod(strEnv, NULL);
char *endptr;
errno = 0;
double d = strtod(strEnv, &endptr);
if (d == endptr) {
return Error_No_conversion; // Like "", "+", "-.", "abc"
}
// Tolerate trailing white-space as leading space is OK
while (isspace(((unsigned char *)endptr)[0])) {
endptr++;
}
if (*endptr) {
return Error_Junk_after_number; // Like "876 - 5309"
}
// Optional pedantic testing.
if (errno == ERANGE) {
if (fabs(d) > 1.0) {
// Usually this is OK to just continue;
// `d` will have the signed value of HUGE_VAL (DBL_MAX or infinity)
; // return Error_Number_too_large;
} else {
// Usually this is OK to just continue;
// `d` will have the signed value of DBL_MIN or 0.0 or some small value
; // return Error_Number_too_small;
}
} else if (errno) {
// Usually this is OK to just continue;
; return Error_Implementation_specific_error;
}
// Success, now use `d`.
So far, this answer does not fail "" nor "123 456".
gcc 4.4.4 c89
What is better to convert a string to an integer value.
I have tried 2 different methods atoi and sscanf. Both work as expected.
char digits[3] = "34";
int device_num = 0;
if(sscanf(digits, "%d", &device_num) == EOF) {
fprintf(stderr, "WARNING: Incorrect value for device\n");
return FALSE;
}
or using atoi
device_num = atoi(digits);
I was thinking that the sscanf would be better as you can check for errors. However, atoi doesn't doing any checking.
You have 3 choices:
atoi
This is probably the fastest if you're using it in performance-critical code, but it does no error reporting. If the string does not begin with an integer, it will return 0. If the string contains junk after the integer, it will convert the initial part and ignore the rest. If the number is too big to fit in int, the behaviour is unspecified.
sscanf
Some error reporting, and you have a lot of flexibility for what type to store (signed/unsigned versions of char/short/int/long/long long/size_t/ptrdiff_t/intmax_t).
The return value is the number of conversions that succeed, so scanning for "%d" will return 0 if the string does not begin with an integer. You can use "%d%n" to store the index of the first character after the integer that's read in another variable, and thereby check to see if the entire string was converted or if there's junk afterwards. However, like atoi, behaviour on integer overflow is unspecified.
strtol and family
Robust error reporting, provided you set errno to 0 before making the call. Return values are specified on overflow and errno will be set. You can choose any number base from 2 to 36, or specify 0 as the base to auto-interpret leading 0x and 0 as hex and octal, respectively. Choices of type to convert to are signed/unsigned versions of long/long long/intmax_t.
If you need a smaller type you can always store the result in a temporary long or unsigned long variable and check for overflow yourself.
Since these functions take a pointer to pointer argument, you also get a pointer to the first character following the converted integer, for free, so you can tell if the entire string was an integer or parse subsequent data in the string if needed.
Personally, I would recommend the strtol family for most purposes. If you're doing something quick-and-dirty, atoi might meet your needs.
As an aside, sometimes I find I need to parse numbers where leading whitespace, sign, etc. are not supposed to be accepted. In this case it's pretty damn easy to roll your own for loop, eg.,
for (x=0; (unsigned)*s-'0'<10; s++)
x=10*x+(*s-'0');
Or you can use (for robustness):
if (isdigit(*s))
x=strtol(s, &s, 10);
else /* error */
*scanf() family of functions return the number of values converted. So you should check to make sure sscanf() returns 1 in your case. EOF is returned for "input failure", which means that ssacnf() will never return EOF.
For sscanf(), the function has to parse the format string, and then decode an integer. atoi() doesn't have that overhead. Both suffer from the problem that out-of-range values result in undefined behavior.
You should use strtol() or strtoul() functions, which provide much better error-detection and checking. They also let you know if the whole string was consumed.
If you want an int, you can always use strtol(), and then check the returned value to see if it lies between INT_MIN and INT_MAX.
To #R.. I think it's not enough to check errno for error detection in strtol call.
long strtol (const char *String, char **EndPointer, int Base)
You'll also need to check EndPointer for errors.
Combining R.. and PickBoy answers for brevity
long strtol (const char *String, char **EndPointer, int Base)
// examples
strtol(s, NULL, 10);
strtol(s, &s, 10);
When there is no concern about invalid string input or range issues, use the simplest: atoi()
Otherwise, the method with best error/range detection is neither atoi(), nor sscanf().
This good answer all ready details the lack of error checking with atoi() and some error checking with sscanf().
strtol() is the most stringent function in converting a string to int. Yet it is only a start. Below are detailed examples to show proper usage and so the reason for this answer after the accepted one.
// Over-simplified use
int strtoi(const char *nptr) {
int i = (int) strtol(nptr, (char **)NULL, 10);
return i;
}
This is the like atoi() and neglects to use the error detection features of strtol().
To fully use strtol(), there are various features to consider:
Detection of no conversion: Examples: "xyz", or "" or "--0"? In these cases, endptr will match nptr.
char *endptr;
int i = (int)strtol(nptr, &endptr, 10);
if (nptr == endptr) return FAIL_NO_CONVERT;
Should the whole string convert or just the leading portion: Is "123xyz" OK?
char *endptr;
int i = (int)strtol(nptr, &endptr, 10);
if (*endptr != '\0') return FAIL_EXTRA_JUNK;
Detect if value was so big, the the result is not representable as a long like "999999999999999999999999999999".
errno = 0;
long L = strtol(nptr, &endptr, 10);
if (errno == ERANGE) return FAIL_OVERFLOW;
Detect if the value was outside the range of than int, but not long. If int and long have the same range, this test is not needed.
long L = strtol(nptr, &endptr, 10);
if (L < INT_MIN || L > INT_MAX) return FAIL_INT_OVERFLOW;
Some implementations go beyond the C standard and set errno for additional reasons such as errno to EINVAL in case no conversion was performed or EINVAL The value of the Base parameter is not valid.. The best time to test for these errno values is implementation dependent.
Putting this all together: (Adjust to your needs)
#include <errno.h>
#include <stdlib.h>
int strtoi(const char *nptr, int *error_code) {
char *endptr;
errno = 0;
long i = strtol(nptr, &endptr, 10);
#if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
if (errno == ERANGE || i > INT_MAX || i < INT_MIN) {
errno = ERANGE;
i = i > 0 : INT_MAX : INT_MIN;
*error_code = FAIL_INT_OVERFLOW;
}
#else
if (errno == ERANGE) {
*error_code = FAIL_OVERFLOW;
}
#endif
else if (endptr == nptr) {
*error_code = FAIL_NO_CONVERT;
} else if (*endptr != '\0') {
*error_code = FAIL_EXTRA_JUNK;
} else if (errno) {
*error_code = FAIL_IMPLEMENTATION_REASON;
}
return (int) i;
}
Note: All functions mentioned allow leading spaces, an optional leading sign character and are affected by locale change. Additional code is required for a more restrictive conversion.
Note: Non-OP title change skewed emphasis. This answer applies better to original title "convert string to integer sscanf or atoi"
If user enters 34abc and you pass them to atoi it will return 34.
If you want to validate the value entered then you have to use isdigit on the entered string iteratively
Working on a simple C program I'm stuck with an if test:
int line_number = 0;
if ((line_number >= argv[2]) && (line_number <= argv[4]))
gcc says:
cp.c:25: warning: comparison between pointer and integer
cp.c:25: warning: comparison between pointer and integer
What can I do to properly check the range of lines I want to deal with?
Of course it doesn't work: argv is a pointer to pointer to char.. it's not clear what you want to do but think about that argv[2] is third parameter and argv[4] is fifth one. But they are of char* type (they are strings) so if you want to parse them as integers you should do it using the function atoi:
int value = atoi(argv[2]);
will parse int that was as third parameter and place it into variable, then you can check whatever you want.
You should not be using function atoi. If fact, you should forget it ever existed. It has no practical uses.
While Jack's answer is correct in stating that the argv strings have to be converted to numbers first, using atoi for that purpose (specifically in the situation when the input comes from the "outside world") is a crime against C programming. There are virtually no situations when atoi can be meaningfully used in a program.
The function that you should be using in this case is strtol
char *end;
long long_value = strtol(argv[2], &end, 10);
if (*end != '\0' || errno == ERANGE)
/* Conversion error happened */;
The exact error checking condition (like whether to require *end == '\0') will actually depend on your intent.
If you want to obtain an int in the end, you should also check the value for int range (or for your application-specific range)
if (long_value < INT_MIN || long_value > INT_MAX)
/* Out of bounds error */;
int value = long_value;
/* This is your final value */