1

I start learning C last week and even the simple tasks can be a challenge for me. Currently I am facing this problem: I have a txt file with many lines, each of them starting with a key word (NMEA format for those who know it):

$GPGGA,***001430.00***,.......

$GPRMC,001430.00,.......

$GPGSV,................    1st time the message arrives

$GPGSA,................


----------


$GPGGA,***005931.00***,...............

$GPRMC,005931.00,............... last time 

$GPGSV,.........................

$GPGSA,.........................

I want to extract the time stamp for the last occurrance of $GPGGA line. Till now I was able just to extract the first and the very last line of the file (unfortunately the last line is not the GPGGA message). I tried to look in the file for the key word $GPGGA and then to sscanf the string from a specific byte and store the value in some variables(hour,min,sec).

Any suggestion would be greatly appreciated. Thank you very much for your time and help.

Here is my code:

entint main(int argc, char **argv) {         
    FILE *opnmea;
    char first[100], last[100];   //first and last time the message arrives
    int hour,min;
    float sec;      // time of observation
    char year[4], month[2], day[2];    // date of obs campaign
    char time[7];

    opnmea = fopen(argv[1], "r");   
    if (opnmea != NULL ) {
        fgets(first, 100, opnmea); // read only first line of the file
        if (strstr(first, "$GPGGA") != NULL)
        {
            sscanf(first + 7, "%2d%2d%f", &hour, &min, &sec);
            printf("First arrival of stream: %d%d%f \n", hour, min, sec);
        }
        fseek(opnmea, 0, SEEK_SET);
        while (!feof(opnmea)) {
            if (strstr(first, "$GPGGA") != NULL) {
                memset(last, 0x00, 100); // clean buffer
                fscanf(opnmea, "%s\n", last); 
            }
        }
        printf("Last arrival of stream: %s\n", last);
        fclose(opnmea);
    }
    else
    {
        printf("\nfile %s not found", argv[1]);
    }
    return 0;
}
Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • Why do you `strstr(first, ...)` while `fscanf(..., last)`? – EOF Sep 24 '15 at 10:40
  • Please indent your code consistently. – Iharob Al Asimi Sep 24 '15 at 10:41
  • Never use `while(!feof(...))`, it's a broken pattern. – unwind Sep 24 '15 at 10:42
  • 1
    To expand on what @unwind said: you should read a whole line at a time using `fgets` then parse it using `sscanf` instead. This will be more robust if a single line has an unexpected format. – John Zwinck Sep 24 '15 at 10:43
  • Adding to @[unwind](http://stackoverflow.com/questions/32759185/print-only-first-and-last-line-that-contains-a-key-word-in-c#comment53357623_32759185)'s comment [`while(!feof(file))` is always wrong](http://stackoverflow.com/a/26557243/1983495) – Iharob Al Asimi Sep 24 '15 at 10:44
  • @JohnZwinck: He's not using `fscanf()` to parse the line. – EOF Sep 24 '15 at 10:44
  • 1
    @EOF: he is using `fscanf()` in the code, take a look. – John Zwinck Sep 24 '15 at 10:46
  • 1
    @JohnZwinck True, but in the current code as is, there is no protection against malformed lines, leading to possible undefined behavior here `printf("First arrival of stream: %d%d%f \n", hour, min, sec);` for instance. So recommending `fgets()` + `sscanf()` though a great recommendation, will not make the code more robust because the OP is ignoring `fscanf()` or `sscanf()`'s return value. – Iharob Al Asimi Sep 24 '15 at 10:48
  • @JohnZwinck: Provided the lines are all formated as the examples given, the `fscanf()` in the code is the same as `fgets()`, except not putting the `'\n'` in the buffer and not providing a buffer-size. – EOF Sep 24 '15 at 10:53
  • 1
    @EOF: the whole point of my comment is that code should not produce unexpected results if the input format is unexpected. That's all, we can drop it now. – John Zwinck Sep 24 '15 at 11:00
  • @EOF I put strstr(first,...) while fscanf(..., last) because with the code from above is the only way the last line will be printed...I do my best to learn C reading in books or online but at this moment many things are not clear. – Florin-Catalin Grec Sep 24 '15 at 13:42
  • @ iharob, thanks for editing my post – Florin-Catalin Grec Sep 24 '15 at 13:43

1 Answers1

3

The scanf family of functions does not know about newlines, they are treated like regular white space. If you have a line-based format, it is better to read while lines with fgets and then further process these lines, perhaps with sscanf, which scans a string.

There's no need to use feof. The library functions for reading provide special values that indicate the end of the file. For example, fgets will return NULL on error or if the end of the file has been reached.

If you are interested in the timestamps only, you don't have to save the lines. Especially, there's no need for two line buffers. It is enough to extract the information and store it.

Here's an example that shows the basic workings of finding the first and last occurrence of a timestamp. I've conflated the hours, minutes and seconds into a single value for seconds.

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



int main(int argc, char **argv)
{
    FILE *opnmea;
    float first = -1;         // first and ...
    float last = -1;          // ... last timestamp in seconds
    char line[100];

    if (argc != 2) {
        fprintf(stderr, "Usage: prog nmeafile\n");
        exit(1);
    }

    opnmea = fopen(argv[1], "r");

    if (opnmea == NULL) {
        fprintf(stderr, "Can't open '%s'.\n", argv[1]);
        exit(1);
    }

    while (fgets(line, sizeof(line), opnmea)) {
        int hrs, min;
        float sec;

        if (sscanf(line, "$GPGGA,%2d%2d%f", &hrs, &min, &sec) == 3) {
            sec = hrs * 3600.0 + min * 60.0 + sec;

            if (first < 0) first = sec;
            last = sec;
        }
    }

    printf("first: %8.2f sec\n", first);
    printf("last:  %8.2f sec\n", last);
    printf("diff:  %8.2f sec\n", last - first);

    fclose(opnmea);

    return 0;
}
M Oehm
  • 28,726
  • 3
  • 31
  • 42