Vincent Jakl
Vincent Jakl

Reputation: 23

Ignoring a number of characters stored in a variable while using sscanf

In the following code i need to extract doubles and string from a line formated like this "[double,double] string" with the string starting as a first non white character and any number of white characters after [

I do care about loading properly the numbers, but don't care about the string (could even be empty).

char *read;
size_t len=0;
double x,y;
int size;

getline(&read,&len,stdin);
char *name=(char*)malloc(strlen(read));
if(sscanf(read,"[ %lf, %lf ] %n",&x,&y,&size)!=2)
{
return 0;
} 
sscanf(read,"%*.c%[^\n]s",size,name);

I already have implemented everything. I know that the second sscanf cannot work like this, only there to represent my thought. Is there any way how to get the size variable to be a parameter of how many characters not to load? I searched everything and the only thing i could find was to have number defined, which does not help here.

Also I am quite new to programming, so please be considerate about me not knowing maybe basic stuff.

Upvotes: 2

Views: 65

Answers (2)

David C. Rankin
David C. Rankin

Reputation: 84561

If I understand your question and you want to separate, e.g. "[123.456, 789.34] Daffy Duck" into the two doubles and the name using getline and sscanf allocating storage for name, then a single call to POSIX getline() and a single call to sscanf reading name into a temporary array and then allocating and copying to name will allow you to size name exactly as required.

For example:

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

#define MAXN 1024

int main (void) {

    double x, y;
    char *read = NULL, *name = NULL, tmp[MAXN];
    size_t n = 0;

    fputs ("input: ", stdout);
    if (getline (&read, &n, stdin) == -1) {
        fputs ("error: getline.\n", stderr);
        return 1;
    }

    if (sscanf (read, " [%lf ,%lf ] %1023[^\n]", &x, &y, tmp) == 3) {
        size_t len = strlen (tmp);
        if (!(name = malloc (len + 1))) {
            perror ("malloc-name");
            return 1;
        }
        memcpy (name, tmp, len + 1);

        printf ("x   : %f\ny   : %f\nname: %s\n", x, y, name);
        free (name);
    }
    free (read);
}

There is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?

Also note that when the parameter n = 0 as above, getline() will allocate storage as needed to handle your input line. So following the call to getline(), read has allocated storage duration. You will need to free read to avoid leaking memory and you cannot simply return a pointer to the beginning of name within read as you must preserve a pointer to the beginning of read in order to be able to free it.

Example Use/Output

Then by including appropriate whitespace in your sscanf format-string you can flexibly read your input regardless of leading or intervening whitespace, e.g.

$ ./bin/readxyname
input: [123.456, 789.34] Daffy Duck
x   : 123.456000
y   : 789.340000
name: Daffy Duck

With no whitespace in input:

$ ./bin/readxyname
input: [123.456,789.34]Daffy Duck
x   : 123.456000
y   : 789.340000
name: Daffy Duck

With arbitrary whitespace in input:

$ input:  [        123.456    ,        789.34  ]           Daffy Duck
x   : 123.456000
y   : 789.340000
name: Daffy Duck

Look things over and let me know if you have further questions.

Upvotes: 3

edmz
edmz

Reputation: 8494

scanf may ignore matches with the * formatter. More on that on conversion specifications here. That way, you can match and store double and match and ignore the following string i.e. %*s.

A bit easier way could be searching for ] with i.e. strchr and then passing sscanf only the string portion you're actually interested in (now that you know where it begins and ends) through simple pointer arithmetics.

Upvotes: 0

Related Questions