Lab: File Input/Output

Today’s lab will help you practice writing programs in C that interact with files. There is no provided starter code for today, but you can follow these commands to create and open a directory for today’s lab:

$ mkdir ~/csc161/labs/file-io
$ cd ~/csc161/labs/file-io
$ code .

A. Basic File Operations

Today’s reading introduced a handful of useful file functions. Use those functions (possibly with help from the reading) to complete the following exercises in a new source file, print-file.c.

Exercises

  1. Write a main function that takes the argc and argv parameters introduced in our previous lab.
  2. The first argument to your program will be the name of a file. Open that file for reading, but pay close attention to which index in argv contains the first argument.

  3. Write a conditional to check to make sure you successfully opened the file. If the open succeeded, print a success message. If not, use the perror function to print an error message and then exit. You should review the manpage for this function, but here is an example use of perror:
    perror("Failed to open file");
    

    If the error occurred because the file did not exist, that will print this message:

    Failed to open file: No such file or directory
    
  4. You should be able to compile your source to a program named print-file and test it. You may want to try asking the program to open your source file:
    $ ./print-file print-file.c
    Successfully opened print-file.c
    

    Make sure you also test the error-handling of the print-file program.

  5. What happens if your program is run without a command-line argument? Add a check to make sure the user passes one in. If the user didn’t provide a filename, use fprintf to display an error to stderr and exit.

  6. Why do you think we used fprintf instead of perror in the previous step? Make a guess, then try replacing your error message with a perror call and see what happens.

  7. It is a good idea to close files after you are done with them. Modify your main function so it will close the input file before returning from main.

  8. Finally, write a loop that reads from the file one character at a time until you reach the end of the file content. Print each character to stdout.

B. More Reading and Writing

The exercises below will build on the program you wrote in part A to do slightly more complex tasks using files.

Exercises

  1. Starting with your print-file.c code, create a program called wordcount.c that counts the number of words in an input file. Assume a word is a sequence of non-whitespace characters at the beginning of a file or immediately after a whitespace character. You will need to create some plain text files (use VSCode or a programming text editor) with a range of words in them to test your program. You can also test your program on the complete works of William Shakespeare (used with license from Project Gutenberg).

  2. Create a program called reverse-lines.c that takes an input file as a command-line argument. The program should create a new output file called reversed.txt. Open the file in a mode that will create the file if necessary, or remove its contents if it already exists. Read the input file line-by-line, reverse the characters in the line, and then write the line to reversed.txt. You may assume a line will never be longer than 1024 characters.

  3. There are several different ways to open a file for writing. One option, called append mode, will leave the file content in place, adding any output written to the file to the end. You can read about the different file modes in the manpage for fopen. Write a program that opens a file named log.txt (creating it if necessary) and writes a short log message containing the time to the end of that file. You can get the current time in seconds since January 1st, 1970 with the time function. Here is an example use of the time function to create a log message:
    #include <time.h>
    ...
    main() {
      ...
      fprintf(logfile, "Program was run at time %ld\n", time(NULL));
      ...
    }
    
  4. An abecedarian word is any word whose letters are in alphabetical order. For example, “arty” is an abecedarian word but “artsy” is not because “s” precedes “t” in the alphabet. There is a file at the path /usr/local/dict/american_english that contains over 100,000 words with one word on each line. Write a program that opens this file and prints all of the abecedarian words.

    Your implementation should convert all letters to lowercase, and should skip over any punctuation or other non-alphabetic characters (the word “Al’s” is abecedarian, despite the apostrophe). You may assume no word is longer than 100 letters.