4

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 prefer sscanf, 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
gsamaras
  • 71,951
  • 46
  • 188
  • 305
Lively
  • 111
  • 1
  • 1
  • 10
  • 2
    Hello and welcome to stackoverflow.com. Please take some time to read [the help pages](http://stackoverflow.com/help), especially the sections named ["What topics can I ask about here?"](http://stackoverflow.com/help/on-topic) and ["What types of questions should I avoid asking?"](http://stackoverflow.com/help/dont-ask). And more importantly, please read [the Stack Overflow question checklist](http://meta.stackexchange.com/questions/156810/stack-overflow-question-checklist). You might also want to learn what a [SSCCE](http://sscce.org/) is. – Some programmer dude Feb 19 '14 at 14:55
  • "but on really old C" - are you a masochist? – Karoly Horvath Feb 19 '14 at 15:08
  • Game-Editor platform do not support newer version of C ^^. – Lively Feb 19 '14 at 15:17
  • my condolences. well, as long as it's not a core language feature, you can always take an existing implementation of the missing part and use it. – Karoly Horvath Feb 21 '14 at 12:03

4 Answers4

3

A combination of digit filtering and sscanf() should work.

int GetNumber(const char *str) {
  while (!(*str >= '0' && *str <= '9') && (*str != '-') && (*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; 
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Actually 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; }` – Lively Feb 19 '14 at 23:52
  • @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'; }` – chux - Reinstate Monica Feb 20 '14 at 00:18
  • Thanks. 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.._ – Lively Feb 21 '14 at 14:13
  • You may with to use `while (!isdigit(*str)) { ... }` as the condition instead. It's in (http://www.cplusplus.com/reference/cctype/isdigit/). – Jason C Dec 09 '20 at 15:24
  • 1
    @JasonC `isdigit(*str)` is UB when `*str < 0`. `(*str >= '0' && *str <= '9')` does not have that problem. An alternative is to use `isdigit((unsigned char) *str)` which handles most other cases. – chux - Reinstate Monica Dec 09 '20 at 16:10
  • Good point thanks. And `static_cast` 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'`, whereas `isdigit(static_cast(*str))` covers it. – Jason C Dec 09 '20 at 17:56
  • (Although the `'0' + value`'s scattered all over the universe would scream if that ever weren't the case.) – Jason C Dec 09 '20 at 17:59
  • 1
    @JasonC C specifies `'0'` through `'9'` as sequential, so `(*str >= '0' && *str <= '9')` is good, ASCII or not as is `'0' + value`. `static_cast(*str)` is a compiler error in C as this post is tagged. I suspect you are thinking of some other language. – chux - Reinstate Monica Dec 09 '20 at 18:01
  • 1
    @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 – chux - Reinstate Monica Dec 09 '20 at 18:08
  • Oh. 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. – Jason C Dec 09 '20 at 18:19
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");
}
AnthonyLambert
  • 8,768
  • 4
  • 37
  • 72
  • With `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. – Utkan Gezer Feb 19 '14 at 15:07
  • The 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 how `sscanf` could be used. – Lively Feb 19 '14 at 15:13
  • Usually 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. – AnthonyLambert Feb 19 '14 at 15:34
1

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.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • The 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? – Lively Feb 19 '14 at 15:21
  • @Lively Oh my, I feel really sorry for you then! But yeah, that's the gist of it. – Some programmer dude Feb 19 '14 at 15:22
  • Well hehe @Joachim i gotta get used doing this. Seems i will create a compare input-output function by myself. Thanks. – Lively Feb 19 '14 at 15:28
1
#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;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70