C Loops

From XPUB & Lens-Based wiki

How to use the programs on this page

Save the sample code as a file ending in .c, for instance "readlines.c", then from the command line, cd to the directory containing the file, and type:

gcc readlines.c -o readlines

or to include debugging info (for use with gdb/ddd)

gcc -g readlines.c -o readlines


Then to run the program and feed it some text (such as itself), you could use:

./readlines < readlines.c

or perhaps, something like (just make sure the file is plain text):

./readlines < ~/Documents/alice.txt

Reading stdin line by line

fgets is a C function that reads a line of input from a file. It takes three arguments (inputs):

fgets(line, maxchars, file)
  1. line: the name of a variable in which to store the result, this should be a character array (pointer) with enough space to store the line,
  2. maxchars: the maximum number of characters possible in a line (the function will never try to read more than this many characters, even if a line is longer),
  3. file: the name of the file to read from, here we use stdin to read from whatever is piped to this program.

fgets returns a value indicating whether or not it was able to read any input. In this way, it's possible to use as the condition of a while loop to do something with all available input.

// read all of stdin line by line
while (fgets(line, MAXLINE, stdin)) {
    // do something with line
}
// loop stops when there's no more input

The following C program reads the content of stdin line by line and simply re-outputs it unchanged. In this way, this code could be the basis for a text filter.

Note how MAXLINE is defined as a compiler directive to tell C how much space to create for the line variable, both (1) when declaring the variable, and (2) when calling the fgets function. The "%s" format code means to output the contents of line as a string or sequence of characters (which it is).

#include "stdio.h"
#define MAXLINE 1000

int main () {
    char line[MAXLINE];

    while (fgets(line, MAXLINE, stdin)) {
        printf("%s", line);
    }

    // returning 0 from main is the convention to indicate that the program finished normally
    return 0;
}

The following program prints the message "line is ??? characters long" with ??? filled with the result of calling the strlen function. What do you notice about the value strlen returns, is it correct (and if not, could you guess why that is?) Note how the code includes the string.h header file to make use of the strlen function defined within it. The "%d" format code means to output the contents of linelen as a decimal number.

#include "stdio.h"
#include "string.h"
#define MAXLINE 1000

int main () {
    int i=0;
    int linelen;
    char line[MAXLINE];
    while (fgets(line, MAXLINE, stdin)) {
        linelen = strlen(line);
        printf("line is %d characters long\n", linelen);
    }
    return 0;
}

Exercise: Line Numbering

Exercise: Write a program that adds line numbers to each line given to it via stdin.

The first argument to printf is the "format" string, which works like a kind of template. It includes support for "padding" values:

printf("%04d\n", 27);
printf("%06d\n", 27);

would output:

0027
000027

Exercise: Use string padding to pad the line numbers of your line numbering program.

Processing each line character by character

To do some simple filtering of each line, it's possible to use a nested loop to do something with each character in a line.

#include "stdio.h"
#include "string.h"
#define MAXLINE 1000

int main () {
    char line[MAXLINE];
    int linelen;
    int i;
    
    while (fgets(line, MAXLINE, stdin)) {
        linelen = strlen(line);
        i=0;
        while (i<linelen) {
            printf("%c ", line[i]);
            i++;
        }
        printf("\n");
    }

    return 0;
}

The same program, with a for loop instead of the inner while. For is a convenient shortcut for a more general purpose while and makes it's harder to forget to initialize or to increment the loop variable, but it takes a little getting used to what it means.

#include "stdio.h"
#include "string.h"
#define MAXLINE 1000

int main () {
    char line[MAXLINE];
    int linelen;
    int i;
    
    while (fgets(line, MAXLINE, stdin)) {
        linelen = strlen(line);
        for (i=0; i<linelen; i++) {
            printf("%c ", line[i]);
        }
        printf("\n");
    }

    return 0;
}

Exercise: Punctuation only filter

Exercise: The ctypes.h header includes a function called ispunct, which returns True if a given character is punctuation. Modify the code above to have a program that prints only the punctuation of a file (it a character is not punctuation, print a space.

 if (ispunct(c)) {
   // do something!
 }