0

I have a file that contains multiple lines with the following format:

 2019-12-05 07:14:00 (151.45,-23.917)

I want to split each line into 3 variables so that the first variable is "2019-12-05", the second variable is "07:14:00" and the final variable is (151.45,-23.917) in the example of the above line shown.

I have a snippet of code below in which I tried to split the lines into three variables but I get a segmentation fault.

int main(int argc, char *argv[])
{
    FILE    *fp=NULL;
    char inpFname[81],buf[8000];
    int dates,hour,lon,lat; 
    int i=0;
    int voyageid = atoi(argv[1]);

    sprintf(inpFname,"%d_latlon.txt",voyageid);

    if ((fp=fopen(inpFname,"rt"))==NULL)
    {
        printf("\nERROR: Cannot open/read input file [%s]\n\n",inpFname);
        exit(1);
    }

    while(!feof(fp))
    {
        fgets(buf,8000,fp);
        printf("%d) buf is %s",i,buf);
        sscanf(buf,"%s %s %s", dates,hour,lon);
        printf("date is %s and lat is %s and lon is %s\n",dates,lat,lon);
        i=i+1;  
    }

}

How can I tweak the code to ensure a successful split of each line into separate variables?

The file being input looks like this:

2019-12-03 06:00:00 (152.282,-13.505)
2019-12-03 12:00:00 (152.393,-14.8478)
2019-12-03 18:00:00 (152.505,-16.1906)
2019-12-04 00:00:00 (152.618,-17.5335)
2019-12-04 06:00:00 (152.732,-18.8763)
2019-12-04 12:00:00 (152.846,-20.2191)
2019-12-04 18:00:00 (152.962,-21.5619)
Steve Friedl
  • 3,929
  • 1
  • 23
  • 30
jms1980
  • 1,017
  • 1
  • 21
  • 37
  • Please provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). We can't possibly tell why a seg fault occurs if we don't know how all the buffers and variables are defined and set. – kaylum Nov 25 '19 at 23:38
  • 2
    For starters, always check the function return values. Particularly `sscanf` in this case. Should not blindly attempt to print the string values if `sscanf` fails to parse any of them. That may seg fault depending on how those variables were defined and initialised. – kaylum Nov 25 '19 at 23:41
  • How are `dates`, `hour`, and `lon` declared? – Steve Summit Nov 25 '19 at 23:42
  • `int dates`? You need char buffers because that's what you are telling `sscanf` is being provided with `%s`. – kaylum Nov 25 '19 at 23:44
  • 2
    If that code doesn't scream warnings at you, you seriously need to turn up your compiler warnings and treat them as errors. You're asking to parse three strings, the mandate for `%s` is to provide a target `char` buffer sufficiently sized to accept the incoming data plus terminator. You're passing three `int` values rather than three `char `buffer addresses. So.. undefined behavior. – WhozCraig Nov 25 '19 at 23:46

1 Answers1

1

Three things:

  1. You need to declare dates, hour, lon, and lat as strings, because that's what %s in fscanf expects.

That's your main problem, but while you're at it, you should fix two other things which will be causing you other problems soon enough:

  1. You need to check the return value from fscanf.

  2. You need to not call while(!feof(fp)). (See here.) Once you're checking the return value from fscanf, you should use it to terminate your loop, if fscanf returns less than the number of items you ask for, 3 or 4.

Your next question will be, how do you declare dates, etc. as strings? If you do something like

char *dates;

that'll fail, too, because dates doesn't point anywhere yet. Easier would be

char dates[100];

or, if you want to use pointers, you can retain char *dates, as long as you then do something like

dates = malloc(100);

followed by a check to make sure malloc didn't return NULL. And then, in either case, for safety, you should change the relevant part of the format string to be %99s, to make sure fscanf doesn't try to read more into the string than it can hold (and where you use %99s, not %100s, because you have to leave space for the \0 terminator).

And then your next question will be, if dates and the rest are strings, how can you pull the numbers out of them? Functions like atoi, atof, strtol, and strtod may help, or extra/different scanf formats.

For example, you could do something like

int y, m, d;
if(sscanf(dates, "%d-%d-%d", &y, &m, &d) == 3) {
    /* now have broken-down date in y, m, d *.
}

Of, you could do a completely different top-level parse:

int year, month, day, hour, minute, second;
double lat, lon;

while(fscanf(fp, "%d-%d-%d %d:%d:%d (%lf,%lf)", year, month, day, hour, minute, second, &lat, &lon) == 8) {
    ...
}
Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • If the string you're trying to parse is a date/time, you're better off using `strptime` rather than `sscanf` – Chris Dodd Nov 26 '19 at 04:45