File I/O

Files and Streams

Input/Output To use files for input/output, you need to include <stdio.h>.


Simple file output examples:

1.  FILE *fp; /* For reading/writing to a file */
2.
3.  fp = fopen("myfile", "wt");      /* Open the file for write */
4.    fputs("Line number 1\n", fp);  /* Write text to file      */
5.    fputs("Line number 2\n", fp);  /* Write more text to file */
6.    fputs("Line number 3\n", fp);  /* Write even more text    */
7.  fclose(fp);                      /* Close the file          */
Descriptions: Reading from a file and writing to a file generally follow these steps:
  1. Open the file (fopen)
  2. Read/Write the file (fgetc, fgets, fputc, fputs, etc.)
  3. Close the file (fclose)
There is actually a very important piece missing from the code above. Corrected code:
 1.  FILE *fp; /* For reading/writing to a file */
 2.
 3.  fp = fopen("myfile", "wt");      /* Open the file for write */
 4.  if (fp != NULL)                  /* Check for success/fail  */
 5.  {
 6.    fputs("Line number 1\n", fp);  /* Write text to file      */
 7.    fputs("Line number 2\n", fp);  /* Write more text to file */
 8.    fputs("Line number 3\n", fp);  /* Write even more text    */
 9.    fclose(fp);                    /* Close the file          */
10.  }
You must always check the return value from fopen. If it fails, a NULL pointer is returned.

Writing to a fileWriting to a file and the screen
FILE *fp;

fp = fopen("myfile.txt", "wt");
if (fp)
{
  int i;
  for (i = 0; i < 10; i++)
    fprintf(fp, "Line number %i\n", i);
  fclose(fp);
}




Output (in the file):
Line number 0
Line number 1
Line number 2
Line number 3
Line number 4
Line number 5
Line number 6
Line number 7
Line number 8
Line number 9
FILE *fp;

fp = fopen("myfile.txt", "wt");
if (fp)
{
  int i;
  for (i = 0; i < 10; i++)
  {
    fprintf(fp, "Line number %i\n", i); /* To file   */
    printf("Line number %i\n", i);      /* To screen */
  }
  fclose(fp);
}

Output (on screen and in the file):
Line number 0
Line number 1
Line number 2
Line number 3
Line number 4
Line number 5
Line number 6
Line number 7
Line number 8
Line number 9

Basic Input

The simplest facilities for unformatted input in C are getchar and gets:
int getchar(void);
char *gets(char *buffer);
Notes: File versions of the above:
int fgetc(FILE *stream);
char *fgets(char *string, int n, FILE *stream);
Notes: Simple input examples: Suppose we have a text file named poem.txt and it contains this:
Roses are red.<NL>
Violets are blue.<NL>
Some poems rhyme.<NL>
But not this one.<NL>
The <NL> indicates the (invisible) newline in the file. Looking at the file with the od program (octal dump) on Linux or Mac OS X:

mmead@sabrina:~/data/digipen/cs120> od -a poem.txt
0000000   R   o   s   e   s  sp   a   r   e  sp   r   e   d   .  nl   V
0000020   i   o   l   e   t   s  sp   a   r   e  sp   b   l   u   e   .
0000040  nl   S   o   m   e  sp   p   o   e   m   s  sp   r   h   y   m
0000060   e   .  nl   B   u   t  sp   n   o   t  sp   t   h   i   s  sp
0000100   o   n   e   .  nl
0000105
The same file under Windows (Cygwin):
E:\Data\Courses\Notes\CS120\Code\FileIO>od -a poem.txt
0000000   R   o   s   e   s  sp   a   r   e  sp   r   e   d   .  cr  nl
0000020   V   i   o   l   e   t   s  sp   a   r   e  sp   b   l   u   e
0000040   .  cr  nl   S   o   m   e  sp   p   o   e   m   s  sp   r   h
0000060   y   m   e   .  cr  nl   B   u   t  sp   n   o   t  sp   t   h
0000100   i   s  sp   o   n   e   .  cr  nl
0000111
This example program reads in the text file:
#define BUFFER_SIZE 50    /* How big our buffer will be         */
char buffer[BUFFER_SIZE]; /* To hold each line from the file    */
FILE *fp;                 /* The pointer to manipulate the file */

fp = fopen("poem.txt", "rt");       /* Try to open the file for reading */
if (fp)                             /* The file was opened successfully */
{
  while (!feof(fp))                 /* While not at the end of the file */
  {
    fgets(buffer, BUFFER_SIZE, fp); /* Read in the next line            */
    printf(buffer);                 /* Print it out on the screen       */
  }
  fclose(fp);                       /* Close the file                   */
}

Output:
Roses are red.
Violets are blue.
Some poems rhyme.
But not this one.
But not this one.
The corrected code:
void f5(void)
{
  #define BUFFER_SIZE 50    /* How big our buffer will be         */
  char buffer[BUFFER_SIZE]; /* To hold each line from the file    */
  FILE *fp;                 /* The pointer to manipulate the file */

  fp = fopen("poem.txt", "rt");       /* Try to open the file for reading */
  if (fp)                             /* The file was opened successfully */
  {
    while (!feof(fp))                 /* While not at the end of the file */
    {
      if (fgets(buffer, BUFFER_SIZE, fp)) /* Read in the next line            */
        printf(buffer);                   /* Print it out on the screen       */
    }
    fclose(fp);                           /* Close the file                   */
  }
}

Output:
Roses are red.
Violets are blue.
Some poems rhyme.
But not this one.

Default open streams:

They are part of the standard I/O library in stdio.h so you don't need to declare them.

You will probably never have to deal with the internal structure of a FILE and can just assume that the standard I/O devices are declared like this:

FILE *stdin;
FILE *stdout;
FILE *stderr;

The definition of a FILE used by Microsoft's compiler:

struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
        };
typedef struct _iobuf FILE;

_CRTIMP extern FILE _iob[];

#define stdin  (&_iob[0])
#define stdout (&_iob[1])
#define stderr (&_iob[2])

The example program above modified to write to stdout. Don't have to open it, it's always open:
CodeOutput to screen/stdout
int i;
for (i = 0; i < 10; i++)
  fprintf(stdout, "Line number %i\n", i);
Line number 0
Line number 1
Line number 2
Line number 3
Line number 4
Line number 5
Line number 6
Line number 7
Line number 8
Line number 9

In fact, these two lines are essentially equivalent:

printf("Line number %i\n", i);          /* Write to stdout/screen */
fprintf(stdout, "Line number %i\n", i); /* Write to stdout/screen */
Notes:

Redirecting stdout and stderr

Suppose we had a function that did this:

void ShowOutErr(void)
{
  fputs("This is going to stdout\n", stdout);
  fputs("This is going to stderr\n", stderr);
}
By default, running this program would produce this output on the console (display):
This is going to stdout
This is going to stderr
We can redirect these messages to a file by using the output redirection operator at the console. (Assume the function above compiles to a program called myprog.exe.)
myprog > out.txt
When we run the program, we see this printed to the screen:
This is going to stderr
What happened to the line "This is going to stdout"? Well, it was redirected to a file named out.txt. If we look at the contents of this file we see:
This is going to stdout
To redirect stderr, we need to do this:
myprog 2> err.txt
This produces the output:
This is going to stdout
The redirection also created a file named err.txt that contains the other line of text.

To redirect both, we do this:

myprog > out.txt 2> err.txt
which produces no output on the screen. Both lines of text have been redirected to their respective files (out.txt and err.txt).

If we want both stdout and stderr redirected to the same file (both.txt), we would do this:

myprog > both.txt 2>&1
Notes:

More Details for Input/Output

Text streams have certain attributes that may vary among different systems: Binary files have no restrictions or limitations. It is up to the programmer to decide when to interpret a file as a text file, and when to interpret it as a binary file.

Like most languages, reading from a file and writing to a file follow these steps:

  1. Open the file (fopen)
  2. Read/Write the file (fgetc, fgets, fputc, fputs, fprintf, fscanf, etc.)
  3. Close the file (fclose)
These two functions are required in all cases:
FILE *fopen( const char *filename, const char *mode );
int fclose( FILE *stream );
Notes: This text below has been saved in 3 different formats here: Windows, Linux (and Mac OS X), and Macintosh (pre OS X). You'll have to download the files and view them in a hex editor (or use the octal dump, od, command or dumpit on Digipen computers) to see the differences. (Most web browsers can handle all types of EOL characters so they'll display it correctly.)

When in the Course of human events, it becomes necessary for one people to dissolve the political bands which have connected them with another, and to assume among the powers of the earth, the separate and equal station to which the Laws of Nature and of Nature's God entitle them, a decent respect to the opinions of mankind requires that they should declare the causes which impel them to the separation.

Example

This example displays the contents of a given text file. (We're assuming we can interpret it as text.)

From 64-bit Linux: stdio.h  bits/stdio_lim.h

#define MAX_LINE 1024

void DisplayTextFile(void)
{
  char filename[FILENAME_MAX]; /* Name of the file on the disk. */
  FILE *infile;                /* The opened file (for reading) */

    /* Prompt the user for a filename */
  puts("Enter the filename to display: ");

    /* Get the user's input (unsafe function call!) */
  gets(filename);

    /* Open the file for read in text/translate mode */
  infile = fopen(filename, "rt");

    /* If successful, read each line and display it */
  if (infile)
  {
    char buffer[MAX_LINE];

      /* Until we reach the end */
    while (!feof(infile))
    {
        /* Get a line and display it (safer function call) */
      if (fgets(buffer, MAX_LINE, infile))
        fputs(buffer, stdout);
    }

      /* Close the file */
    fclose(infile);
  }
  else
    perror(filename);  /* couldn't open the file
}
If the fopen fails, we call perror to display the reason. If we try to open a non-existent file named foo.txt, we'll see this:
foo.txt: No such file or directory
Notes:

The printf Family

The most common function for displaying output on the screen is the printf function. It is kind of a Swiss Army Knife for outputting.
int printf( const char *format [, argument]... );
All format specifications are constructed as such:
%[flags] [width] [.precision] [{h | l | L}]type
Simple example:
void TestPrintf(void)
{
  int i = 227;
  long a = 123L;
  char c = 'M';
  double d = 3.1415926;
  char *s = "Digipen";

  printf("i=%i, i=%x, i=%X, a=%li, c=%c, c=%4i, d=%5.3f, s=%10s\n", 
         i, i, i, a, c, c, d, s);
}
Output:
i=227, i=e3, i=E3, a=123, c=M, c=  77, d=3.142, s=   Digipen
Notes

Exercise: Write a function that displays the following table using printf in a loop:

Value   Squared    Sqrt
-----------------------
   0         0    0.000
  10       100    3.162
  20       400    4.472
  30       900    5.477
  40      1600    6.325
  50      2500    7.071
  60      3600    7.746
  70      4900    8.367
  80      6400    8.944
  90      8100    9.487
 100     10000   10.000
 110     12100   10.488
 120     14400   10.954
 130     16900   11.402
The family of printf functions:
int printf( const char *format [, argument]... );
int fprintf( FILE *stream, const char *format [, argument ]...);
int sprintf( char *buffer, const char *format [, argument] ... );
Notes:

The scanf Family

scanf is analogous to printf, only used for input instead of output. The family of functions are these:
int scanf( const char *format [,argument]... );
int fscanf( FILE *stream, const char *format [, argument ]... );
int sscanf( const char *buffer, const char *format [, argument ] ... );
Notes: All format specifications are constructed as such:
%[*] [width] [{h | l | L}]type
Example:
void Testscanf(void)
{
  int i;
  char c;
  float f;
  double d;
  char s[20];

  scanf("%i %c %f %lf %s", &i, &c, &f, &d, s);
  printf("i=%i, c=%c, f=%f, d=%f, s=%s\n", i, c, f, d, s);
}
Given this input:
123 Z 4.56 7.8 Seattle
both functions display:
i=123, c=Z, f=4.560000, d=7.800000, s=Seattle
Because whitespace is ignored, the input could have been on separate lines:
123 
Z 
4.56 
7.8 
Seattle
Notes:

Binary Input/Output

For binary I/O, use fread and fwrite:
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
Info: Note that the return value is not the number of bytes written, but the number of elements written. The number of actual bytes written will be the number of elements written multiplied by the size of each element. Examples using fread and fwrite. (You can ignore the structures for now.)

Contents of a file after writing 5 integers to the file (from previous example):

Big endian
000000  12 34 56 78 12 34 56 79 12 34 56 7A 12 34 56 7B  4Vx4Vy4Vz4V{
000010  12 34 56 7C                                      4V|
Little endian
000000  78 56 34 12 79 56 34 12 7A 56 34 12 7B 56 34 12  xV4yV4zV4{V4
000010  7C 56 34 12                                      |V4
Compare those files to a text file containing those integers as strings.
12345678<NL>
12345679<NL>
1234567A<NL>
1234567B<NL>
1234567C<NL>
The binary files contain 20 bytes of data. The text file contains 45 or 50 (depending on the system).

Endian Refresher

More technically: Some processors and their "endianess":
Processor Family           Endian
---------------------------------
Pentium (Intel)            Little
Athlon (AMD)               Little
Alpha (DEC/Compaq)         Little
680x0 (Motorola)            Big
PowerPC (Motorola & IBM)    Big
SPARC (Sun)                 Big
MIPS (SGI)                  Big
Java Virtual Machine        Big
ARM                         Both
Network issues: Macros to swap 16-bit and 32-bit values:

#ifdef BIG_ENDIAN /* no-ops */

  #define htons(x) (x)  /* host-to-network short (16-bit) */
  #define htonl(x) (x)  /* host-to-network long (32-bit)  */
  #define ntohs(x) (x)  /* network-to-host short (16-bit) */
  #define ntohl(x) (x)  /* network-to-host long (32-bit)  */

#else /* assume little endian and swap bytes */

  #define htons(x) ((((x) & 0xff00) >> 8) | ((x) & 0x00ff) << 8)) 
  #define htonl(x) ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
                   (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
  #define ntohs htons
  #define ntohl htonl

#endif
More on Endianness.

Other Input/Output Functions

int ungetc( int c, FILE *stream );
int fflush( FILE *stream );
long ftell( FILE *stream );
int fseek( FILE *stream, long offset, int origin );
int feof( FILE *stream );
int rename( const char *oldname, const char *newname );
int remove( const char *path );
char *tmpnam( char *string );
Description: Notes: