Work with files (C)


Match word(s).

If you have any questions or comments,
please visit us on the Forums.

FAQ > How do I... (Level 2) > Work with files (C)

This item was added on: 2003/03/22

Most simple programs accept input from the standard input and write to standard output, with command line redirection these programs can also read from and write to files on the hard disk as well, but what if a program needs to read input from both stdin and a file? Redirection doesn't work this way and the program must handle the file itself. Consider a program that copies one file to another with a command line argument like this:

pcopy file1 file2


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

static FILE *open_file ( char *file, char *mode )
{
  FILE *fp = fopen ( file, mode );

  if ( fp == NULL ) {
    perror ( "Unable to open file" );
    exit ( EXIT_FAILURE );
  }

  return fp;
}

int main ( int argc, char *argv[] )
{
  int ch;
  FILE *in;
  FILE *out;

  if ( argc != 3 ) {
    fprintf ( stderr, "Usage: %s <readfile1> <writefile2>\n", argv[0] );
    exit ( EXIT_FAILURE );
  }

  in = open_file ( argv[1], "r" );
  out = open_file ( argv[2], "w" );

  while ( ( ch = fgetc ( in ) ) != EOF )
    fputc ( ch, out );

  fclose ( in );
  fclose ( out );

  return EXIT_SUCCESS;
}


First, two files are opened with the open_file function that performs error checking on the standard library function fopen, which does the real work. If a file cannot be opened, open_file reports an error and terminates the program.

Once the files are open, a while loop consisting of a call to fgetc to read a single character from in is entered. The loop will continue to read from in and write that character to out until the value of EOF is reached. EOF is a macro defined in stdio.h.

You'll notice that fopen takes two arguments, the file name as a string and a second string designated as the open mode. This determines how the file is to be used, for example reading or writing or both. The open modes for text files are:


"r"  /* open for reading */
"w"  /* create for writing, discard previous contents */
"a"  /* append, open or create for writing */
"r+" /* open for update */
"w+" /* create for update, discard previous contents */
"a+" /* append, open or create for update */


Binary files are differentiated from text files by a trailing "b" in the open mode and are most commonly (unfortunately) operated on with fread and fwrite:


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

int main ( void )
{
  FILE *in;
  char buf[BUFSIZ];

  in = fopen ( "input.txt", "rb" );

  if ( in == NULL ) {
    perror ( "Unable to open the file" );
    exit ( EXIT_FAILURE );
  }

  /* Read a string */
  fread ( buf, 1, sizeof buf, in );
  printf ( "%s\n", buf );

  fclose ( in );

  return 0;
}


The problem with this is that binary files are by nature nonportable and fread/fwrite do not work to alleviate this problem in the slightest. So while binary files tend to be smaller and faster, working with them in a portable manner is difficult to get right and can be very tedious. Consider a program that simply reads a four byte integer in little endian order from a binary file:


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

int main ( void )
{
  FILE *in;
  int num;

  in = fopen ( "input.txt", "rb" );

  if ( in == NULL ) {
    perror ( "Unable to open the file" );
    exit ( EXIT_FAILURE );
  }

  /* Read a four byte integer */
  num = getc ( in );
  num |= (unsigned long)getc ( in ) << 8;
  num |= (unsigned long)getc ( in ) << 16;
  num |= (unsigned long)getc ( in ) << 24;

  printf ( "%d\n", num );

  fclose ( in );

  return 0;
}


As you can see, this makes binary files seem less attractive when compared to text files. In my experience, the benefits of text files far outweigh the benefits of binary files in practical usage. Files are very easy to use when you consider that stdin, stdout, and stderr are all FILE pointers as well, you've been using them since your first day programming. The only difference between the standard streams and user defined streams is that you have to open and close them explicitly in your program.

The definitions of the file I/O functions are:


FILE *fopen ( const char *filename, const char *mode );
int  fclose ( FILE *stream );

int  fprintf( FILE *stream, const char *format, ... );
int  fscanf ( FILE *stream, const char *format, ... );
int  fgetc  ( FILE *stream );
char *fgets ( char *s, int n, FILE *stream );
int  fputc  ( int c, FILE *stream );
int  fputs  ( const char *s, FILE *stream );
int  getc   ( FILE *stream );
int  putc   ( int c, FILE *stream );
int  ungetc ( int c, FILE *stream );

size_t fread  ( void *ptr, size_t size, size_t nmemb, FILE *stream );
size_t fwrite ( const void *ptr, size_t size, size_t nmemb, FILE *stream );


More can be done with files such as moving to a specific offset or setting the input buffer size, or even creating and removing temporary files, but such operations will be discussed later to keep this "How-To" short.

Credit: Prelude

Script provided by SmartCGIs