0

I have a string such as: "1.5 3 aaa 0 bbbbbbbbbb" which I need to sscanf (I know exactly how many space-separated elements I expect and what their data type is, but I don't know the length of the two strings aaa and bbbbbbbbbb).

I would like to have something like this:

sscanf(rx_buffer, "%f %d %s %d %s", &a, &b, string1, &c, string2);
    

What is a simple, nice and effective way of storing the two strings into string1 and string2 (two char *)? Is maybe realloc involved?

Thank you in advance, every tip is greatly appreciated.

1
  • 1
    A "simple, nice and effective" way is to use strtod, strtol, and strchr. Commented Jun 25, 2020 at 13:27

2 Answers 2

1

You cannot interrupt sscanf while reading in a string, so you have no chance to perform a realloc once you exceed some buffer.

One thing you can do is providing a buffer that is large enough to hold the longest string you assume; you can limit the number of characters you accept in order to avoid overflows; and you can check how many items sscanf successfully scanned:

char string1[100];
char string2[100];
int elementsScanned = sscanf(rx_buffer, "%f %d %99s %d %99s", &a, &b, string1, &c, string2);
if (elementsScanned != 5) ... // do some error handling here

Another thing you can do is to assume that neither string1 nor string2 can ever get larger than rx_buffer itself. So you could allocate memory accordingly. This is "wasting" some memory, but you are on the save side:

size_t len = strlen(rx_buffer);
char* string1 = malloc(len+1);
char* string2 = malloc(len+1);
int elementsScanned = sscanf(rx_buffer, "%f %d %s %d %s", &a, &b, string1, &c, string2);
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, but that is exactly what I would like to avoid, i.e.: having to define many strings (two was just an example for simplicity) considering the worst case scenario. I wonder if there is a nice sscanf alternative which could output the desired result.
0

If you can assume a maximum length for the strings to parse from rx_buffer, sscanf() is a simple an effective solution.

If rx_buffer can have an arbitrary length, you could parse it using library functions, such as strtod(), strtol(), strspn(), strcspn() and strndup():

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

struct context {
    float a;
    int b;
    char *string1;
    int c;
    char *string2;
};

// return the number of fields parsed
int parse_response(struct context *cp, const char *rx_buffer) {
    //sscanf(rx_buffer, "%f %d %s %d %s", &a, &b, string1, &c, string2);
    const char *src = rx_buffer;
    char *p;
    size_t n;
    cp->a = strtod(src, &p);
    if (p == src)
        return 0;
    cp->b = strtol(src = p, &p, 10);
    if (p == src)
        return 1;
    src = p + strspn(p, " \t\r\n\f\v");
    n = strcspn(src, " \t\r\n\f\v");
    if (n == 0)
        return 2;
    if ((cp->string1 = strndup(src, n)) == NULL)
        return -1;
    src += n;
    src += strspn(src, " \t\r\n\f\v");
    cp->c = strtol(src, &p, 10);
    if (p == src)
        return 3;
    src = p + strspn(p, " \t\r\n\f\v");
    n = strcspn(src, " \t\r\n\f\v");
    if (n == 0)
        return 4;
    if ((cp->string2 = strndup(src, n)) == NULL)
        return -1;
    return 5;
}

This is obviously neither simple nor nice.

If your target is a linux system with the GNU libc, you could use an extension of their sscanf() implementation: the m prefix between % and s provides a simple, nice and effective solution:

#include <stdio.h>

// return the number of fields parsed
int parse_response(const char *rx_buffer) {
    float a;
    int b, c;
    char *string1 = NULL, *string2 = NULL;
    int n = sscanf(rx_buffer, "%f %d %ms %d %ms", &a, &b, &string1, &c, &string2);
    if (n == 5) {
        // all fields parsed correctly, handle values
        ...
    }
    free(string1);
    free(string2);
    return n;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.