2

How can I check the number fromstring using sscanf . I tried the following example , it is work however I would like to know how i can include numbers with points formate such as 3.6 and negative sign

#include <stdio.h>

int main() {
    char * string = "xx=3300   rr=3.6   zz=-0.8";
    int val;
    if(sscanf(string, "%*[^0123456789]%d", &val)==1)
        printf("%d\n", val);
    else
        printf("not found\n");
    return 0;
}
user873101
  • 27
  • 5
  • 2
    How do you plan to represent a number like 3.6 in an `int` value like `val`? Seems like you should be using a floating point type (`float` or `double`) for your value and the corresponding format specifier. – lurker Feb 12 '18 at 18:36
  • 1
    Don't use scanf. Scan for equals signs using `strchr` and then convert what follows to a floating-point number using `strtod`. – zwol Feb 12 '18 at 18:42
  • 1
    If `string` begins with an integer, `sscanf(string, "%*[^0123456789]%d", &val)` will not return 1 as `"%*[^0123456789]"` obliges at least 1 matching character before continuing to `"%d"`. – chux - Reinstate Monica Feb 12 '18 at 19:14
  • Is numeric text only immediate after a `'='` or may it appear elsewhere in the string? – chux - Reinstate Monica Feb 12 '18 at 20:09

3 Answers3

2

If it is just about extending your sscanf-approach, then simply add the -+.-characters, and use datatype float or double, which can represent floating point values:

int main() {
    char * string = "xx=3300   rr=3.6   zz=-0.8";
    float val;
    if(sscanf(string, "%*[^-+.0123456789]%f", &val)==1)
        printf("%f\n", val);
    else
        printf("not found\n");
    return 0;
}

The better approach would be, however, to first split the string into tokens, e.g. based on the white spaces or the =-sign, such that you know exactly where you expect a number in the input; then you can convert a string to a number of your choice. Such an approach could look as follows:

int main() {
    char string[] = "xx=3300   rr=3.6   zz=-0.8";
    char *pair = strtok(string," ");
    while (pair) {
        char *beginOfVal = strchr(pair, '=');
        if (beginOfVal) {
            beginOfVal++;
            char *endOfVal;
            double val = strtod(beginOfVal, &endOfVal);
            if (endOfVal==beginOfVal) {
                printf("error scanning %s as a number.\n", beginOfVal);
            }
            else {
                printf("val: %lf\n", val);
            }
        }
        pair = strtok(NULL," ");
    }
}
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
  • Actually I need to avoid using strtok due to thread safe concern , is it possible to use the second approach without using strtok, any other suggested functions to spilt the string based on space ?! @Stephan Lechner – user873101 Feb 13 '18 at 13:12
2

You shouldn't use sscanf for this, because you should never use any of the scanf functions for anything.

You should instead use a combination of strchr to scan for equals signs, and strtod to convert text to machine floating-point. Here's a sketch of the necessary loop:

void read_numbers_from_string (const char *str, void (*callback)(double))
{
    while (*str)
    {
        char *p = strchr(str, '=');
        if (!p) break;

        errno = 0;
        char *endp;
        double n = strtod(p + 1, &endp);
        if (endp > p + 1 && !errno)
            callback(n);

        str = endp;
    }
}

Untested. Error handling around strtod may be insufficient for your application—read the NOTES section of the strtod manpage very carefully.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Note that OP is not using `scanf()`. – chux - Reinstate Monica Feb 12 '18 at 18:48
  • @chux: Note that the sentiment (and reasoning) extends to the whole `*scanf()` family. – DevSolar Feb 12 '18 at 18:49
  • @DevSolar `scanf()` has many short-comings not in `sscanf()`. For those reason, I eschew `scanf()`. `sscanf()` has few short-comings, of which they are readily handled. – chux - Reinstate Monica Feb 12 '18 at 18:52
  • @DevSolar `strtod()` has its weaknesses too as it needs a proper set-up of `errno` and various post call checks - something not shown here. The right tool often obliges the right surrounding code . – chux - Reinstate Monica Feb 12 '18 at 18:56
  • This code appears to be an infinite loop as it is doing `strtod("=3300 rr=3.6 zz=-0.8", &endp);`. Did you want `p++; double n = strtod(p, &endp);`? – chux - Reinstate Monica Feb 12 '18 at 19:01
  • @chux I did mean `strtod(p+1, &endp)`, thanks. I have added basic error handling. Re `scanf` vs `strtod`, I consider all three of the issues listed in the post I linked to to be fatal design flaws in the entire `scanf` family of functions. – zwol Feb 12 '18 at 19:14
  • I wouldn't say that you should never use `scanf` & co, but you should definitely use for not properly formatted input. The problem is that many people do not realize what `scanf` should be used for and use it as a quick & dirty way of getting values from the user. Because user input is random as best, sometimes it works and sometimes it's very hard to formulate a proper format that accounts for the randomness of the input. – Pablo Feb 12 '18 at 19:39
1

strpbrk could be used to find the first digit, sign or dot. Parse using strtol or strtod

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    char * string = "xx=3300   rr=3.6   zz=-0.8  .89  4e-5";
    char *each = string;
    char *start = NULL;
    char *stop = NULL;
    long int val = 0;
    double dbl = 0.0;

    while ( ( start = strpbrk ( each, "0123456789.+-"))) {//find digit sign or dot
        val = strtol ( start, &stop, 10);//parse a long
        if ( '.' == *stop || 'e' == *stop) {//found a dot
            dbl = strtod ( start, &stop);//parse a double
            printf("%f\n", dbl);
        }
        else {
            printf("%ld\n", val);
        }
        if ( start == stop) {//unable to parse a long or double
            break;
        }

        each = stop;
    }
    return 0;
}
user3121023
  • 8,181
  • 5
  • 18
  • 16