ECE 209 -- Files

Definitions

file A sequence of bytes with a name maintained by the operating system. Example filenames include /home/clauss/NiceList.txt and C:\Users\Claus\NiceList.txt.
FILE pointer The C value of type FILE *. This mysterious value is created when files are opened and is used in almost all C I/O operations. Evidently, a lot of stuff is stored in a FILE, but no one wants to know what it is.
stream Not everything with a filename is a real file. Examples include named pipes in Unix and Windows and special devices like /dev/null and /dev/random in Unix and NUL and COM1 in Windows. C and, especially, C++ gurus always call opened files or file-like entities streams. In fact, the terms stream and FILE pointer are often used as synonyms.
mode The way in which a stream is being used: reading, writing, or both. Can also apply to opening files as binary or text. (In Unix, mode also confusingly refers to the protection associated with a file.)
sequential access Reading or writing a stream in serial order, that is, from the first byte to the last. These is how files are usually processed.
random access Reading or writing a stream by moving directly to specific points within the file. These points are not random. They are chosen by the C program. This is rarely done.
file offset Thus is the point where an application is presently reading or writing in a stream. It is expressed as the number of bytes from the beginning of a file. Sometimes called the file position indicator or even (especially by non-C programmers) the file pointer which is easily confused with the FILE pointer.
binary mode The values written and read by the C application are exactly those the are stored within the file as maintained by the operating system. When C is run on a Unix computer, all file operations are performed in binary mode. For this reason, Unix does not distinguish between binary and text mode.
text mode In Unix and Mac OS X operating systems, lines of text are separated by a single character, the linefeed (aka, '\n' or new line or ASCII 10). In the Windows operating system, lines of text are separated by two characters, the carriage return (aka, '\r' or ASCII 13) and then the linefeed. To maintain program portability, when a C program writes "\n" to a text file in Windows, the C I/O library actually writes "\r\n". A similar trick is done when reading text files. (C++ "solves" this problem by using a special constant endl to write the end-of-line sequence.)
standard files When Unix programs are started they are given three opened file descriptors (the operating system view of a file): standard input, for reading characters; standard output, for writing characters; and standard error, for writing argent output. Windows and Mac OS provide similar file descriptors. In C, these are represented by three FILE pointers: stdin, stdout, and stderr.
buffer When a C program reads or writes data, the C I/O system does not directly request data from the operating system. Instead it maintains a buffer of data that is sent to the operating system when appropriate. This approach significantly improves program performance. Unfortunately, there are time when it is necessary to flush the buffer to make sure changes are written to the file system or the terminal.

Opening and closing files

fopen Opens a file and associates a stream with it. Returns NULL if the open fails.
fclose Closes a stream. Very rarely fails.
stdin Standard input stream
stdout Standard output stream
stderr Standard error stream

Reading streams

fscanf Complicated function for formatted input.
fgetc Reads a single character.
fgets Reads a line.
fread Reads raw bits from the stream.

Writing streams

fprintf Complicated function for formatted output.
fputc Writes a single character.
fputs Write a line.
fwrite Writes raw bits to the stream.

Random access I/O

fseek Moves the offset of a stream.
ftell Returns the current offset within a stream.

Buffering

fflush Really writes buffered output of stream.
ungetc Unreads a characters. Allows peeking ahead. Best to avoid.

Example program for writing a file

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

int main(int argc, char **argv) {
  int arraySize ;
  FILE *fileStream ;
  int row, col ;

  if (argc != 3
      || (arraySize = atoi(argv[2])) <= 0) {
    fprintf(stderr, "Usage: %s OutputFileName SizeOfArray\n", argv[0]) ;
    return(EXIT_FAILURE) ;
  }
  if ((fileStream = fopen(argv[1], "w")) == NULL) {
    fprintf(stderr, "Unable to open %s for output\n", argv[1]) ;
    return(EXIT_FAILURE) ;
  }

  for (row=0; row<arraySize; ++row) {
    for (col=0; col<row; ++col) {
      fputs("0,", fileStream) ;
    }
    fputs("1,",fileStream) ;
    for (col=row+1; col<arraySize; ++col) {
      fputs("0,", fileStream) ;
    }
    fputc('\n', fileStream) ;
  }

  fclose(fileStream) ;

  return(EXIT_SUCCESS) ;
}

Initializing a password check file

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

int main(int argc, char **argv) {
  int numberOfSlots ;
  FILE *fileStream ;
  int wordNum ;
  unsigned int zilch = 0 ;

  if (argc != 3
      || (numberOfSlots = atoi(argv[2])) <= 0) {
    fprintf(stderr, "Usage: %s OutputFileName NumberOfSlots\n", argv[0]) ;
    return(EXIT_FAILURE) ;
  }
  if ((fileStream = fopen(argv[1], "w")) == NULL) {
    fprintf(stderr, "Unable to open %s for output\n", argv[1]) ;
    return(EXIT_FAILURE) ;
  }

  for (wordNum=0; wordNum<numberOfSlots; ++wordNum) {
    if (fwrite((void *)&zilch, sizeof zilch, 1, fileStream) != 1) {
      fprintf(stderr, "Failure writing to %s\n", argv[1]) ;
      return(EXIT_FAILURE) ;
    }
  }

  fclose(fileStream) ;

  return(EXIT_SUCCESS) ;
}

Updating a password check file

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

int HashIt(char *String, int Range) {
  unsigned int R = 0 ;
  while (*String) {
    R = R*209 + *String ;
    ++String ;
  }
  return ((int) (R % Range)) ;
}

int main(int argc, char **argv) {
  int numberOfSlots ;
  FILE *fileStream ;
  char line[100] ;

  if (argc != 2) {
    fprintf(stderr, "Usage: %s PasswordCheckFile\n", argv[0]) ;
    return(EXIT_FAILURE) ;
  }
  if ((fileStream = fopen(argv[1], "r+")) == NULL) {
    fprintf(stderr, "Unable to open check file %s\n", argv[1]) ;
    return(EXIT_FAILURE) ;
  }

  if (fseek(fileStream, 0L, SEEK_END)
      || (numberOfSlots = ftell(fileStream)/sizeof(int)) <= 0) {
    fprintf(stderr, "Excuse me, Dave.  There's a problem with %s\n", argv[1]) ;
    return(EXIT_FAILURE) ;
  }

  while(fgets(line, sizeof line - 1, stdin) != NULL) {
    int numberTimes ;
    int hashVal = HashIt(line, numberOfSlots ) ;
    if (fseek(fileStream, hashVal*sizeof(int), SEEK_SET) 
	|| fread((void *)&numberTimes, sizeof(int), 1, fileStream) != 1) {
      fprintf(stderr, "Excuse me, Dave.  There's a problem reading %s\n",
	      argv[1]) ;
      return(EXIT_FAILURE) ;
    }
    ++numberTimes ;
    fprintf(stdout, " used about %d times\n", numberTimes) ;
    if (fseek(fileStream, -sizeof(int), SEEK_CUR) 
	|| fwrite((void *)&numberTimes, sizeof(int), 1, fileStream) != 1) {
      fprintf(stderr, "Excuse me, Dave.  There's a problem writing %s\n",
	      argv[1]) ;
      return(EXIT_FAILURE) ;
    }
  }

  return(EXIT_SUCCESS) ;
}