1. | Welcome | ToC | FAQ | Resources | Courses | Projects | Mail Lists | Members | Misc |
2. | Fundamentals | Languages | Tools | Net | Core | Advanced |
3. | EoC | Java |
4. | Lessons |
5. | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 |


Essence of C
9 - DISK FILE INPUT/OUTPUT

Streams and Files, File Pointer, Opening, Closing, Reading, Writing, Erasing, Random Access, Standard Streams

Introduction

As previously stated, there are no input/output (I/O) statements in C. Instead, all I/O operations take place through calls to functions in the standard C library, an approach which makes C's file system extremely powerful and flexible. C's I/O system is also unique in that data may be transferred in either:


ANSI C Versus UNIX

The ANSI C standard defines a complete set of I/O functions which may be used to read and write data of any type.

The old UNIX C standard contains two distinct systems of routines for I/O operations, viz:

Streams and Files

The C I/O system supplies a consistent interface to the C programmer independent of the actual device being accessed. Put another way, the I/O system provides a level of abstraction between the programmer and the device being used. This abstraction is called the stream and the actual device is called the file.


Streams

Even though different devices are involved (terminals, disk drives, etc), the buffered file system transforms each into a logical device called a stream. Because streams are device-independent, the same function can write to a disk file or to another device, such as a console. There are two types of streams, ie:


Files

In C, a file may be anything from a disk file to a terminal or printer. A stream can be associated with a file by performing an open operation, following which information may be exchanged between the file and the program.

All streams are the same but all files are not. For example, a disk file can support random access while a keyboard cannot. If a file can support random access (sometimes referred to as position requests), opening a file also initializes the file position indicator to the start of a file. This indicator is incremented as each character is read from, or written to, the file.

The close operation disassociates a file from a specific stream. If the file was opened for output, the close operation will write the contents (if any) of the stream to the device. This process is usually called flushing the stream.

All files are closed automatically when the program terminates, but not when it crashes.

Each stream associated with a file has a file control structure of type FILE.

File System Basics

The ANSI file system comprises several interrelated functions. These are:

Function Description
fopen()> Opens a file.
fclose()> Closes a file.
putc()> Writes a character.
fputc()> Writes a character.
getc()> Reads a character.
fgetc()> Reads a character.
fseek()> Seeks a specified byte in a file.
fprintf()> Is to a file what printf() is to the console.
fscanf()> Is to a file what scanf() is to a console.
feof()> Returns TRUE if end-of-file is reached.
ferror()> Returns TRUE if an error is detected.
rewind()> Resets file position to beginning of file.
remove()> Erases a file.
fflush()> Flushes a file.

Most of these functions begin with the letter "f". This is a holdover from the UNIX C standard, which, as previously stated, defined two file systems. The UNIX I/O functions did not begin with a prefix and most of the formatted I/O system functions were prefixed with an "f". The ANSI standard maintained the convention in the interests of continuity.

The header file stdio.h provides the prototypes for the I/O functions and defines these three types:

     typedef unsigned long    size_t
     typedef unsigned long    fpos_t
     typedef struct _FILE     FILE

stdio.h also defines the following:

     EOF       -1  // value returned at end of file
     SEEK_SET   0  // from beginning of file
     SEEK_CUR   1  // from current position
     SEEK_END   2  // from end of file

The latter three are used with fseek(), which is the function which performs random access on a file.


The File Pointer

The file pointer is the common thread which unites the buffered I/O system. This is a pointer to information which defines various things about a file (including name, status and current position). In essence, the file pointer identifies a specific disk file and is used by the associated stream to direct the operation of the buffered I/O functions.

A file pointer is a variable of type FILE that is defined in stdio.h. To obtain a file pointer variable, a statement like the following is used:

     FILE *fp;


Opening a File

fopen()opens a stream for use, links a file with that stream and returns a pointer associated with that file. The fopen() function has this prototype:

     FILE *fopen(const char * filename, const char * mode);

where filename is a pointer to a string of characters that make a valid filename (and may include a path specification) and mode determines how the file will be opened. The legal values for mode are as follows:

Value Description
r Open a text file for reading.
w Create a text file for writing.
a Append to a text file.
rb Open a binary file for reading.
wb Create a binary file for writing.
ab Append to a binary file.
r+ Open a text file for read/write.
w+ Create a text file for read/write.
a+ Append or create a text file for read/write.
r+b Open a binary file for read/write.
w+b Create a binary file for read/write.
a+b Append a binary file for read/write.

As shown, a file may be opened in text or binary mode. In most implementations, in text mode, CR/LF sequences are translated to newline characters on input. On output, the reverse occurs. No such translation occurs on binary files.

The following opens a file named TEST for writing:

     FILE *fp;

     fp = fopen("test","w");

However, because fopen() returns a null pointer if an error occurs when a file is opened, this is better written as:

     FILE *fp;
     if((fp = fopen("test","w")) == NULL)
     {
       printf("cannot open file\n");
       exit(1);
     }


Closing a File

fclose() closes the stream, writes any data remaining in the disk buffer to the file, does a formal operating system level close on the file, and frees the associated file control block. fclose() has this prototype:

     int fclose(FILE *fp);

A return value of zero signifies a successful operation. Generally, fclose() will fail only when a disk has been prematurely removed or a disk is full.


Writing a Character

Characters are written using putc() or its equivalent fputc(). The prototype for putc() is:

     int putc(int ch, FILE *fp);

where ch is the character to be output. For historical reasons, ch is defined as an int, but only the low order byte is used.

If the putc() operation is successful, it returns the character written, otherwise it returns EOF.


Reading a Character

Characters are read using getc() or its equivalent fgetc(). The prototype for getc() is:

     int getc(FILE *fp);

For historical reasons, getc() returns an integer, but the high order byte is zero. getc() returns an EOF when the end of file has been reached. The following code reads a text file to the end:

     do
     {
       ch = getc(fp);
     } while(ch != EOF);


Using feof()

As previously stated, the buffered file system can also operate on binary data.

When a file is opened for binary input, an integer value equal to the EOF mark may be read, causing the EOF condition. To solve this problem, C includes the function feof(), which determines when the end of the file is reached when reading binary data. The prototype is:

     int feof(FILE *fp);

The following code reads a binary file until end of file is encountered:

     while(!feof(fp))
     ch = getc(fp);

Of course, this method can be applied to text files as well as binary files.


Working With Strings - fputs() and fgets()

In addition to getc() and putc(), C supports the related functions fputs() and fgets(), which read and write character strings. They have the following prototypes:

     int fputs(const char *str, FILE *fp);

     char *fgets(char *str, int length, FILE *fp);

The function fputs() works like puts() but writes the string to the specified stream. The fgets() function reads a string until either a newline character is read or length-1 characters have been read. If a newline is read, it will be part of the string (unlike gets()). The resultant string will be null-terminated.


rewind()

rewind() resets the file position indicator to the beginning of the file.


ferror()

ferror() determines whether a file operation has produced an error. It returns TRUE if an error has occurred, otherwise it returns FALSE. ferror() should be called immediately after each file operation, otherwise an error may be lost.


Erasing Files

remove() erases a specified file. It returns zero if successful.


Flushing a Stream

fflush() flushes the contents of an output stream. fflush() writes the contents of any unbuffered data to the file associated with fp. It returns zero if successful.


fread() and fwrite()

To read and write data types which are longer than one byte, the ANSI standard provides fread() and fwrite(). These functions allow the reading and writing of blocks of any type of data. The prototypes are:

     size_t fread(void *buffer,size_t num_bytes, size_t count, FILE *fp);
     size_t fwrite(const void *buffer,size_t num_bytes, size_t count, FILE *fp);

For fread(), buffer is a pointer to a region of memory which will receive the data from the file. For fwrite(), buffer is a pointer to the information which will be written. The buffer may be simply the memory used to hold the variable, e.g., &l for a long integer.

The number of bytes to be read/written is specified by num_bytes. count determines how many items (each num_bytes in length) are read or written.

fread() returns the number of items read. This value may be less than count if the end of file is reached or an error occurs. fwrite() returns the number of items written.

One of the most useful applications of fread() and fwrite() involves reading and writing user-defined data types, especially structures. For example, given this structure:

     struct struct_type
     {
       float balance;
       char name[80];
     } cust;

the following statement writes the contents of cust:

     fwrite(&cust, sizeof(struct struct_type),1,fp);


fseek() and Random Access I/O

Random read and write operations may be performed with the help of fseek(), which sets the file position locator. The prototype is:

    int fseek(FILE *fp, long numbytes, int origin);

in which numbytes is the number of bytes from the origin, which will become the new current position, and origin is one of the following macros defined in stdio.h:

Origin Macro Name
Beginning of file SEEK_SET
Current position SEEK_CUR
End-of-file SEEK_END

fseek() returns 0 when successful and a non-zero value if an error occurs. fseek() may be used to seek in multiples of any type of data by simply multiplying the size of the data by the number of the item to be reached, eg:

     fseek(fp, 9*sizeof(struct list), SEEK_SET);

which seeks the tenth address.


fprint() and fscanf()

fprint() and fscanf() behave exactly like print() and scanf() except that they operate with files. The prototypes are:

     int fprintf(FILE *fp, const char *control_string, ...);
     int fscanf(FILE *fp, const char *control_string, ...);

Although these functions are often the easiest way to read and write assorted data, they are not always the most efficient. Because formatted ASCII data is being written as it would appear on the screen (instead of in binary), extra overhead is incurred with each call. If speed or file size is of concern, use fread() and fwrite().


The Standard Streams

Whenever a C program starts execution, three streams are opened automatically. These are:

Normally, these streams refer to the console, but they may be redirected by the operating system to some other device or environment. Because the standard streams are file pointers, they may be used to provide buffered I/O operations on the console, e.g.:

     putchar(char c)
     {
       putc(c,stdout);
     }

Demonstration File I/O Programs

The following programs demonstrate the use of C's file I/O functions.

// *******************************************************************************
// DEMO OF FOPEN(), FCLOSE(), GETC(), PUTC() 
// -------------------------------------------------------------------------------
// Specify filename on command line.  Input chars on keyboard 
// until $ is entered.  File is then closed.  File is then  
// re-opened and read.  
// *******************************************************************************

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

void main(int argc, char *argv[])
{
  FILE *fp;                                                        // file pointer
  char ch;

  if(argc!=2)
  {
    printf("You forgot to enter the filename\n");
  	exit(1);
  }

  if((fp=fopen(argv[1], "w"))==NULL)                        // open file
  {
    printf("Cannot open file\n");
    exit(1);
  }

  do                                               // get keyboard chars until `$'
  {
    ch=getchar();
    putc(ch,fp);
  }
  while(ch!='$');

  fclose(fp);                                                        // close file

  if((fp=fopen(argv[1], "r"))==NULL)                        // open file
  {
    printf("Cannot open file\n");
    exit(1);
  }

  ch=getc(fp);                                                   // read one chara

  while(ch!=EOF)
  {
    putchar(ch);                                                // print on screen
    ch=getc(fp);                                             // read another chara
  }

  fclose(fp);                                                        // close file
}


// ******************************************************************************* // DEMO OF FEOF() TO CHECK FOR EOF CONDITION IN // BINARY FILES // ------------------------------------------------------------------------------- // Specify filenames for input and output on command line. The // program copies the source file to the destination file. feof() // checks for end of file condition. (Note: feof() can also be used // for text files.) // ******************************************************************************* #include <stdio.h> #include <stdlib.h> void main(int argc, char *argv[]) { FILE *in, *out; // file pointers char ch; if(argc!=3) { printf("You forgot to enter the filenames\n"); exit(1); } if((in=fopen(argv[1], "rb"))==NULL) // open source file { // for read binary printf("Cannot open source file\n"); exit(1); } if((out=fopen(argv[2], "wb"))==NULL) // open dest file { // for write binary printf("Cannot open destination file\n"); exit(1); } while(!feof(in)) // here it is { ch=getc(in); if(!feof(in)) // and again putc(ch,out); } fclose(in); // close files fclose(out); }
// ******************************************************************************* // DEMO OF FPUTS(), FGETC AND REWIND() // ------------------------------------------------------------------------------- // Strings are entered from keyboard until blank line is entered. // Strings are then written to as file called `testfile'. (Since gets() // does not store the newline character, one is added before the // string is written so that the file can be read more easily.) The // file is then rewound, input and displayed. // ******************************************************************************* #include <stdio.h> #include <stdlib.h> #include <string.h> void main(void) { FILE *fp; // file pointer char str[80]; if((fp=fopen("testfile", "w+"))==NULL) // open file for { // text read & write printf("Cannot open file\n"); exit(1); } do // get strings until CR and write to file { printf("Enter a string (CR to quit):\n"); gets(str); strcat(str, "\n"); fputs(str,fp); } while(*str!='\n'); rewind(fp); // rewind while(!feof(fp) // read and display file { fgets(str,79,fp); printf(str); } fclose(fp); // close file }
// ******************************************************************************* // DEMO OF FREAD() AND FWRITE() (FOR DATA TYPES // LONGER THAN BYTE) // ------------------------------------------------------------------------------- // Writes, then reads back, a double, an int and a long. (Notice // how sizeof() is used to determine the length of each data // type.) Note that these functions are useful for reading and // writing user-defined data types, especially structures. // ******************************************************************************* #include <stdio.h> #include <stdlib.h> void main(void) { FILE *fp; double d=12.23; int i=101; long l=123023; if((fp=fopen("testfile", "wb+"))==NULL) // open for binary { // read & write printf("Cannot open file\n"); exit(1); } // parameters are: *buffer, number of bytes, count of items, // file pointer fwrite(&d, sizeof(double), 1, fp); fwrite(&i, sizeof(int), 1, fp); fwrite(&l, sizeof(long), 1, fp); rewind(fp); fread(&d, sizeof(double), 1, fp); fread(&i, sizeof(int), 1, fp); fread(&l, sizeof(long), 1, fp); printf("%2.3f %d %ld\n",d,i,l); fclose(fp); }
// ******************************************************************************* // DEMO OF FPRINTF() AND FSCANF() // ------------------------------------------------------------------------------- // Reads a string and an integer from the keyboard, writes them // to a file, then reads the file and displays the data. (Note // that these functions are an easy way to write assorted data to // files but are very slow compared with fread() and fwrite(). // ******************************************************************************* #include <stdio.h> #include <stdlib.h> #include <exec/io.h> void main(void) { FILE *fp; char s[80]; int t; if((fp=fopen("testfile", "w"))==NULL) // open for text write { printf("Cannot open file\n"); exit(1); } printf("Enter a string and a number: "); // parameters are: FILE *fp, const char *control_string, ... fscanf(stdin,"%s%d", s, &t); fprintf(fp,"%s %d", s, t); fclose(fp); if((fp=fopen("testfile", "r"))==NULL) // open for text read { printf("Cannot open file\n"); exit(1); } fscanf(fp,"%s%d",s,&t); fprintf(stdout, "%s %d\n", s, t); fclose(fp) }

Prior Chapter · Index · Next Chapter


Copyright © 1996, 1997, 1998. Last Update to This Page: 1998/10/24
This Page Maintained by: radar pangaean * * * Original Author: K.J.Bricknell
The MOST web site is built and maintained by the voluntary efforts/donations of our members.