

\documentstyle{article}

\setlength{\oddsidemargin}{0in}
\setlength{\evensidemargin}{0in}
\setlength{\topmargin}{0.0in}
\setlength{\headheight}{0in}
\setlength{\headsep}{0in}
\setlength{\textwidth}{6.5in}
\setlength{\textheight}{9.6in}
\setlength{\parindent}{0in}
\setlength{\parskip}{0.12in}

\begin{document}

\newcommand{\bfs}[1]{{\bf #1}}

\begin{center}
{\LARGE\bf 

Introduction to C File Access

}
\end{center}

\bigskip

The program below will serve as a first introduction to reading
and writing files (directly, not through the $<$ and $>$ redirection
methods) in C.  It will also introduce a few new C constructs.

The sample program first prompts the user to specify the names of
input and output files.  The program will then read the input file,
and search for any single-digit, nonzero words; it will then produce
an identical output file, except that the single-digit words have
been changed from arabic to roman numerals.  Here is a \bfs{script}
record of a sample run:

\begin{verbatim}
     1  Script started on Wed Apr 15 23:52:27 1992
     2  heather% cat x
     3  
     4  She has 7 sisters, 
     5  2 brothers, 6 nieces and 3 
     6  nephews.
     7  
     8  heather% a.out
     9  enter name of input file
    10  x
    11  enter name of output file
    12  y
    13  heather% cat y
    14  
    15  She has VII sisters, 
    16  II brothers, VI nieces and III 
    17  nephews.
    18  
    19  heather% e
    20  heather% 
    21  script done on Wed Apr 15 23:52:46 1992
\end{verbatim}

Here is the program.  Some remarks will follow it.

\begin{verbatim}
     1  
     2  
     3  /* converts single-digit, nonzero arabic numbers to roman numbers 
     4     in a given text file */
     5  
     6  
     7  /* WARNING:  for demonstration purposes only; not completely general */
     8  
     9  
    10  #include <stdio.h>
    11  
    12  
    13  char InFile[1000],  /* the entire input file */
    14       InFileName[20],  /* name of the input file */
    15       OutFileName[20],  /* name of the output file */
    16       Word[25];  /* current word */
    17  
    18  
    19  int InFileSize,  /* number of characters in the input file */
    20      CurrInFilePlace,  /* current scanning place in the input file; 
    21                           used when we are alternating looking for
    22                           blanks and words */
    23      WordLength;  /* length of the current word */
    24  
    25  
    26  FILE *InFilePtr,  /* pointer to input file */
    27       *OutFilePtr;  /* pointer to output file */
    28  
    29  
    30  ReadInFile()  /* read the input file into the array InFile */
    31  
    32  {  char C;
    33  
    34     InFileSize = 0;
    35     while (fscanf(InFilePtr,"%c",&C) != -1) InFile[InFileSize++] = C;
    36  }
    37  
    38  
    39  PrintFile()  /* for debugging purposes only */
    40  
    41  {  int I;
    42  
    43     for (I = 0; I < InFileSize; I++) printf("%c",InFile[I]);
    44  }
    45  
    46  
    47  GetFile()
    48  
    49  {  printf("enter name of input file\n");
    50     scanf("%s",InFileName);
    51     printf("enter name of output file\n");
    52     scanf("%s",OutFileName);
    53     InFilePtr = fopen(InFileName,"r");
    54     OutFilePtr = fopen(OutFileName,"w");
    55     ReadInFile();
    56  }
    57  
    58  
    59  WriteBlanksAndEOLs()  /* starting at the current position in the
    60                           input file, keep scanning until get a
    61                           "real" character, i.e. not a blank or
    62                           end-of-line; meanwhile, copy all the
    63                           blanks and EOLs to the output file */
    64  
    65  {  char C;
    66  
    67     while (1)  {
    68        C = InFile[CurrInFilePlace];
    69        if (C == ' ' || C == '\n')  {
    70           fprintf(OutFilePtr,"%c",C);
    71           CurrInFilePlace++;
    72        }
    73        else break;
    74     }
    75  }
    76  
    77  
    78  int GetWord()  /* scans this word until hit its end; copy the word
    79                    to the array Word; also, return 1 if successful
    80                    in getting a word, 0 otherwise (failed due to
    81                    reaching the end of the file) */
    82  
    83  {  char C;
    84  
    85     WordLength = 0;
    86  
    87     while (1)  {
    88        /* if reach the end of the file, leave, reporting failure */
    89        if (CurrInFilePlace == InFileSize) return 0;
    90        C = InFile[CurrInFilePlace];
    91        /* if hit the end of the word, leave, otherwise record the
    92           current character in the array Word */
    93        if (C == ' ' || C == '\n')  break;
    94        else  {
    95           Word[WordLength++] = C;
    96           CurrInFilePlace++;
    97        }
    98     }
    99  
   100     return 1;
   101  }
   102  
   103  
   104  
   105  ConvertWord()  /* the arabic-to-roman conversion is done here */
   106  
   107  {  switch (Word[0])  {
   108        case '1': Word[0] = 'I';                        /* 1 = I */
   109                  WordLength = 1; break;
   110        case '2': Word[0] = Word[1] = 'I';              /* 2 = II */
   111                  WordLength = 2; break;
   112        case '3': Word[0] = Word[1] = Word[2] = 'I';    /* 3 = III */
   113                  WordLength = 3; break;
   114        case '4': Word[0] = 'I'; Word[1] = 'V';         /* 4 = IV */
   115                  WordLength = 2; break;
   116        case '5': Word[0] = 'V';                        /* 5 = V */
   117                  WordLength = 1; break;
   118        case '6': Word[0] = 'V'; Word[1] = 'I';         /* 6 = VI */
   119                  WordLength = 2; break;
   120        case '7': Word[0] = 'V';                        /* 7 = VII */
   121                  Word[1] = Word[2] = 'I';  
   122                  WordLength = 3; break;
   123        case '8': Word[0] = 'V';                        /* 8 = VIII */
   124                  Word[1] = Word[2] = Word[3] = 'I';
   125                  WordLength = 4; break;
   126        case '9': Word[0] = 'I'; Word[1] = 'X';         /* 9 = IX */
   127                  WordLength = 2; 
   128     }
   129  }
   130  
   131  
   132  WriteWord()
   133  
   134  {  int I;
   135  
   136     for (I = 0; I < WordLength; I++)  fprintf(OutFilePtr,"%c",Word[I]);
   137  }
   138  
   139  
   140  main()
   141  
   142  {  GetFile();
   143     CurrInFilePlace = 0;
   144     /* keep alternating this cycle:  scan through blanks and end-of-line
   145        characters, copying them to the output file */
   146     while (CurrInFilePlace < InFileSize)  {
   147        WriteBlanksAndEOLs();
   148        if (GetWord())  {
   149           if (Word[0] > '0' && Word[0] <= '9' && WordLength == 1) 
   150              ConvertWord();
   151           WriteWord();
   152        }
   153     }
   154  }
   155  
   156  
\end{verbatim}

\bfs{Analysis:}

\bfs{Line 10.}  This tells the C compiler (actually, the C preprocessor)
to include C source code from a file stdio.h.  Due to the presence of
the `$<$  $>$', the file will be searched for in some standard directories,
e.g. on most systems the directory /usr/include.  (You should take a
look at the file.)  This file contains a bunch of definitions which
are needed for the file manipulation we will be doing.

\bfs{Lines 26-27.}  Here we are declaring two \bfs{pointers} to a new
type called `FILE'.  That latter type is defined in the \#include file,
and we will discuss pointers soon, but for the time
being just take it on faith that we need them here.

\bfs{Line 35.}  Here we are using the \bfs{fscanf()} function, which is just
like \bfs{scanf()}, except that it can be used on any file, not just the
standard input.  It has one more argument, which is the pointer to
that file, in this case InFilePtr.  Note that we keep looping until
\bfs{fscanf()} tells us that we have reached the end of the file, which it
does by returning a value of -1, just as with \bfs{scanf()} in earlier
examples.

\bfs{Lines 50 and 52.}  Here we see the \%s format, for reading in
character strings, just like \%c format reads in a character and \%d
reads in an integer.  Note that we could have used a \bfs{for} loop, with
\%c format being used to read in the characters one at a time, but
\%s is much more convenient.

\bfs{Line 53.}  Before accessing a file, we must ``open'' it first,
using the \bfs{fopen()} function.  Here we are opening the file whose name
we have stored in the array InFileName, and we intend to read from
it, which is why we have specified ``r''.  The \bfs{fopen()} function then
returns a pointer for us, which we assign to InFilePtr; this is how
the pointer gets associated with the file.  From that point on, all
access to this file will be done via the pointer InFilePtr (as in
Line 35).\footnote{Just as \bfs{scanf()} returns a value which can be
used for error checking, so does \bfs{fopen()}.  If the latter returns
a 0 value, this means that it was not able to open the file.}

\bfs{Line 54.}  Same as Line 53, but with write access.

\bfs{Line 69.}  $||$ is the C symbol for \bfs{or}.

\bfs{Line 70.}  fprintf() is just like printf(), except for general
files, not just the standard output.  See remarks on \bfs{fscanf()} and
\bfs{scanf()} above.

\bfs{Lines 107-128.}  A C \bfs{switch} statement is like a Pascal \bfs{case}
statement.  It is quite similar, except that in the C version, all
cases past the one at hand will be done.  For example, suppose
Word[0] is `3'.  Then if we didn't put in the \bfs{break} (Line 113),
then the cases `4', `5', etc. would be done too!  So the \bfs{break} is
crucial; it says, ``Now leave this \bfs{switch} statement, and go to
the first statement following the end of the \bfs{switch}.''
\end{document}


