C-Language: How to get number from string via sscanf?
Solution 1
A combination of digit filtering and sscanf()
should work.
int GetNumber(const char *str) {
while (!(*str >= '0' && *str <= '9') && (*str != '-') && (*str != '+')) str++;
int number;
if (sscanf(str, "%d", &number) == 1) {
return number;
}
// No int found
return -1;
}
Additional work needed for numbers that overflow.
A slower, but pedantic method follows
int GetNumber2(const char *str) {
while (*str) {
int number;
if (sscanf(str, "%d", &number) == 1) {
return number;
}
str++;
}
// No int found
return -1;
}
Solution 2
scanf tries to match a pattern.... so if you knew the string was "He is 16 years old." where 16 was an integer number you wished to decode.
( I think your input string implies your format is somewhat free form. I'm assuming its predictable. )
{
char* inputstr = "He is 16 years old.";
int answer = 0;
int params = sscanf (inputstr, "He is %d years old.", &answer);
if (params==1)
printf ("it worked %d",answer);
else
printf ("It failed");
}
Solution 3
For starters, you should know that the strtok
function is pretty ancient as well. It's in the C89 standard, but probably existed in many implementations before that (for example in 4.3BSD which was released in 1986). In fact, the sscanf
function is probably newer than the strtok
function.
But if you have such an ancient compiler that, and actually don't have the strtok
function, and your input string doesn't follow the exact format you have in the question, but can be more free-form (and so can't really use the pattern-matching functionality of sscanf
) then you have to parse the string manually.
This manual parsing can actually be quite simple, just loop over the string until you find a digit, then collect all consecutive digits while constructing the number. Once you get a non-digit character, you have your number. Of course, this will only get the first number in the string.
Solution 4
#include <stdio.h>
int main() {
char * string = "He is 16 years old.";
int age;
if(sscanf(string, "%*[^0123456789]%d", &age)==1)
printf("%d\n", age);
else
printf("not found\n");
return 0;
}
Comments
-
Lively almost 2 years
Recently I have need to extract a number from a string, but on really old C with means functions like
strtok
are not supported. I would prefersscanf
, but i can't understand it. Note that the integer is in random place (user-defined).In general thats what i want to happan as an example:
Input: char * string = "He is 16 years old."; Output: 16
-
Utkan Gezer about 10 yearsWith
sscanf
this is as far as you could get Lively (OP). If you are to acquire some other non-standard input, anything else than "He is x years old.",sscanf
won't do it. -
Lively about 10 yearsThe string given is like example. Basically the user will just write random string with integer somewhere in it.. And the function has to output those integers. Is it even possible.. i just saw similar questions with
sscanf
as an answer, but not explanation howsscanf
could be used. -
Lively about 10 yearsThe compiler is even more then ancient..
strtok
is not supported and even God do not know why. You mean convert the string in array and set compare if the first char is 0-10 then return it.. if not skip to the next? -
Some programmer dude about 10 years@Lively Oh my, I feel really sorry for you then! But yeah, that's the gist of it.
-
Lively about 10 yearsWell hehe @Joachim i gotta get used doing this. Seems i will create a compare input-output function by myself. Thanks.
-
Johnny about 10 yearsUsually input strings conform to some agreed syntax. If it is totally free form then I would write a routine to split the input string between white space into an array of smaller strings. Then step through the array calling scanf on each. The Params value returns how many %d in out example decoded to a int (a maximum of one) in our example.
-
Lively about 10 yearsActually i've been trying proceed and finally made str to int function:
int strtoint(char source[]) { int multiplier = 1; int i, result; for (i=strlen(source)-1; i>=0; i--) { if(source[i] >= '0' && source[i] <= '9') { result = result + ((source[i]-'0') * multiplier); multiplier *= 10; } } return result; }
-
chux - Reinstate Monica about 10 years@Lively Could go in the forward direction.
{ size_t i; int result = 0; for (i=0; source[i]; i++) { if(source[i] >= '0' && source[i] <= '9') { result = result*10 + source[i] - '0'; }
-
Lively about 10 yearsThanks. I fixed this part already :) Also made strtok function by myself. I don't know if it would be useful if i post it. OFFTOPIC: if i had 2 more reputation i could answer my posts..
-
chux - Reinstate Monica almost 4 yearsFails corner case when
string
begins with amint
like `"123abc". -
Jason C over 3 yearsYou may with to use
while (!isdigit(*str)) { ... }
as the condition instead. It's in <cctype> (cplusplus.com/reference/cctype/isdigit). -
chux - Reinstate Monica over 3 years@JasonC
isdigit(*str)
is UB when*str < 0
.(*str >= '0' && *str <= '9')
does not have that problem. An alternative is to useisdigit((unsigned char) *str)
which handles most other cases. -
Jason C over 3 yearsGood point thanks. And
static_cast<unsigned char>
if you're feeling even more official. Further, on that note, character literals evaluate to their value in the execution charset, which isn't specifically tied to ASCII, and so'1'
technically isn't guaranteed to be>= '0'
and<= '9'
, whereasisdigit(static_cast<unsigned char>(*str))
covers it. -
Jason C over 3 years(Although the
'0' + value
's scattered all over the universe would scream if that ever weren't the case.) -
chux - Reinstate Monica over 3 years@JasonC C specifies
'0'
through'9'
as sequential, so(*str >= '0' && *str <= '9')
is good, ASCII or not as is'0' + value
.static_cast<unsigned char>(*str)
is a compiler error in C as this post is tagged. I suspect you are thinking of some other language. -
chux - Reinstate Monica over 3 years@JasonC Ref: "In both the source and execution basic character sets, the value of each character after 0 in the above list of decimal digits shall be one greater than the value of the previous." C17dr § 5.2.1 3
-
Jason C over 3 yearsOh. Hey, thanks on all points. I have been awake for a very, very long time. I hope I don't add confusion for any readers. I'm going to close the browser before I make any messes anywhere else.