Clearest way to read and print .txt file lines in C

Dlinet picture Dlinet · Dec 28, 2012 · Viewed 16.7k times · Source

There are a bunch of ways describing how to use various methods to print out lines of a text file on this site:

They all seem to be tailored to a specific example.

It would be great to have the Clearest and Most Concise and Easiest way to simply: print each line of any text file to the screen. Preferably with detailed explanations of what each line does.

Points for brevity and clarity.

Answer

Jonathan Leffler picture Jonathan Leffler · Dec 28, 2012
#include <stdio.h>

static void cat(FILE *fp)
{
    char   buffer[4096];
    size_t nbytes;
    while ((nbytes = fread(buffer, sizeof(char), sizeof(buffer), fp)) != 0)
         fwrite(buffer, sizeof(char), nbytes, stdout);
}

int main(int argc, char **argv)
{
    FILE *fp;
    const char *file;
    while ((file = *++argv) != 0)
    {
        if ((fp = fopen(file, "r")) != 0)
        {
            cat(fp);
            fclose(fp);
        }
    }
    return(0);
}

The cat() function is not strictly necessary, but I'd rather use it. The main program steps through each command line argument and opens the named file. If it succeeds, it calls the cat() function to print its contents. Since the call to fopen() does not specify "rb", it is opened as a text file. If the file is not opened, this code silently ignores the issue. If no files are specified, nothing is printed at all.

The cat() function simply reads blocks of text up to 4096 bytes at a time, and writes them to standard output ('the screen'). It stops when there's no more to read.

If you want to extend the code to read standard input when no file is specified, then you can use:

if (argc == 1)
    cat(stdin);
else
{
    ...while loop as now...
}

which is one of the reasons for having the cat() function written as shown.

This code does not pay direct attention to newlines — or lines of any sort. If you want to process it formally one line at a time, then you can do several things:

static void cat(FILE *fp)
{
    char buffer[4096];
    while (fgets(buffer, sizeof(buffer), fp) != 0)
         fputs(buffer, stdout);
}

This will read and write one line at a time. If any line is longer than 4095 bytes, it will read the line in two or more operations and write it in the same number of operations. Note that this assumes a text file in a way that the version using fread() and fwrite() does not. On POSIX systems, the version with fread() and fwrite() will handle arbitrary binary files with null bytes ('\0') in the data, but the version using fgets() and fputs() will not. Both the versions so far are strictly standard C (any version of the standard) as they don't use any platform-specific extensions; they are about as portable as code can be.

Alternatively again, if you have the POSIX 2008 getline() function, you can use that, but you need #include <stdlib.h> too (because you end up having to release the memory it allocates):

static void cat(FILE *fp)
{
    char *buffer = 0;
    size_t buflen = 0;
    while (getline(&buffer, &buflen, fp) != -1)
         fputs(buffer, stdout);
    free(buffer);
}

This version, too, will not handle binary data (meaning data with null bytes in it). It could be upgraded to do so, of course:

static void cat(FILE *fp)
{
    char *buffer = 0;
    size_t buflen = 0;
    ssize_t nbytes;
    while ((nbytes = getline(&buffer, &buflen, fp)) != -1)
         fwrite(buffer, sizeof(char), nbytes, stdout);
    free(buffer);
}

The getline() function reports how many bytes it read (there's a null byte after that), but the fwrite() function is the only one that takes a stream of arbitrary bytes and writes them all to the given stream.