Simple C program to read a file line by line

In C you have character oriented input functions (e.g. getcharfgetc), you have formatted input functions (e.g. the scanf family) and then you have line oriented input functions. (e.g. fgets and POSIX getline). When you are reading lines of data, line oriented input functions are the proper tool for the job. (taking user input with scanf has many pitfalls that new (and even not so new) C programmers fall into)

All line oriented functions read and include the '\n' in the buffer they fill. You can, and should, remove the newline from the resulting buffer if it will be used later on in your code. A simple

size_t n = strlen (buf);
if (buf[n-1] == '\n') 
    buf[--n] = 0;

is all you need to overwrite the trailing '\n' with a nul-terminating character. If you are just printing the line immediately and not storing it for later use, then it’s not worth removing the newline (just account for it in your output format string).

Putting those pieces together, you can read each line, handle the first by simply outputting it, and for each remaining line, parse the time (presumable some elapsed time) from the full string read by fgets with sscanf and format the output as you specify. E.g.

#include <stdio.h>

#define MAXC 64     /* define constants, don't use magic number in code */

int main (int argc, char **argv) {

    char buf[MAXC] = "";    /* buffer to hold each line -- size as reqd */
    int line = 1;
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    while (fgets (buf, sizeof buf, fp)) {   /* read each line in file  */
        char et[MAXC] = "";                 /* buffer for holding time */
        if (line == 1)                      /* if 1st line, just print */
            printf ("%d : %s", line, buf);  /* note: \n included by fgets */
        else {
            if (sscanf (buf, "%s", et) != 1) { /* parse up to first whitespace */
                fprintf (stderr, "error: invalid conversion, line %d\n", line);
                return 1;
            }
            printf ("%d : %s\n", line, et); /* output elapsed time only  */
        }
        line++;                             /* increment line count */
    }

    if (fp != stdin) fclose (fp);           /* close file if not stdin */

    return 0;
}

note: you should protect against buffer overrun on parse by including a field-width specifier in the sscanf format string (e.g. sscanf (buf, "%63s", et), and that is one place that all you can do is include magic numbers in your code because there is no way to directly specify a variable width specifier for sscanf — unless you creatively use sprintf to create the format string ahead of time — but that’s for another day..

Example Input File

$ cat dat/et.txt
My test file
00:19.1 123456
00:35.4 testing whitespace end

Example Use/Output

$ ./bin/et <dat/et.txt
1 : My test file
2 : 00:19.1
3 : 00:35.4

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

(note: I take the filename as the first argument to the program, or read from stdin if no filename is given. C provides for command line arguments — use them. It’s fine to prompt for input if needed, otherwise, its far easier just to specify arguments on the command line :)

Leave a Comment