[Univ of Cambridge] [Dept of Engineering]
next up previous contents
Next: Interactive Up: Input/Output Previous: Input/Output

File I/O under Unix

Some file operations work on file pointers and some lower level ones use small integers called file descriptors (an index into a table of information about opened files).

The following code doesn't do anything useful but it does use most of the file handling routines. The manual pages describe how each routine reports errors. If errnum is set on error then perror can be called to print out the error string corresponding to the error number, and a string the programmer provides as the argument to perror.

 

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>    /* the man pages of the commands say which
                         include files need to be mentioned */
#define TRUE 1
int bytes_read;
size_t  fp_bytes_read;
int fd;   /* File descriptors */
int fd2;
FILE *fp; /* File pointers */
FILE *fp2; 
char buffer[BUFSIZ]; /* BUFSIZ is set up in stdio.h */

main(){

  /* Use File descriptors */
  fd = open ("/etc/group", O_RDONLY);
  if (fd == -1){
     perror("Opening /etc/group");
     exit(1);
  }

  while (TRUE){
     bytes_read = read (fd, buffer,BUFSIZ);
     if (bytes_read>0)
        printf("%d bytes read from /etc/group.\n", bytes_read);
     else{ 
        if (bytes_read==0){
          printf("End of file /etc/group reached\n");
          close(fd);
          break;
        }
        else if (bytes_read == -1){
          perror("Reading /etc/group");
          exit(1);
        }
     }
   }


 /* now use file pointers */
 fp = fopen("/etc/passwd","r");
 if (fp == NULL){
    printf("fopen failed to open /etc/passwd\n");
    exit(1);
  }

 while(TRUE){
    fp_bytes_read= fread (buffer, 1, BUFSIZ, fp);
        printf("%d bytes read from /etc/passwd.\n", fp_bytes_read);
    if (fp_bytes_read==0)
       break;
 }

 rewind(fp); /* go back to the start of the file */

/* Find the descriptor associated with a stream */
  fd2 = fileno (fp);
  if (fd2 == -1)
    printf("fileno failed\n");

/* Find the stream associated with a descriptor */
  fp2 = fdopen (fd2, "r");
  if (fp2 == NULL)
    printf("fdopen failed\n");
  fclose(fp2);
 }

To take advantage of unix's I/O redirection it's often useful to write filters: programs that can read from stdin and write to stdout. In Unix, processes have stdin, stdout and stderr channels. In stdio.h, these names have been associated with file pointers. The following program reads lines from stdin and writes them to stdout prepending each line by a line number. Errors are printed on stderr. fprintf takes the same arguments as printf except that you also specify a file pointer. fprintf(stdout,....) is equivalent to printf(....).  

/* line_nums.c 
   Sample Usage :    line_nums < /etc/group
 */
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
int lineno = 0;
int error_flag = 0;
char buf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */

main(){
  while(TRUE){
    if (fgets(buf,BUFSIZ, stdin) == NULL){
       if (ferror(stdin) != 0){
          fprintf(stderr,"Error during reading\n");
          error_flag = 1;
       }
       if (feof(stdin) != 0)
          fprintf(stderr,"File ended\n");
       clearerr(stdin);
       break; /* exit the while loop */
    }
    else{
       lineno++;
       /* in the next line, "%3d" is used to restrict the
          number to 3 digits.
       */
       fprintf(stdout,"%3d: ", lineno);
       fputs(buf, stdout);
    }
  }

  fprintf(stderr,"%d lines written\n", lineno);
  exit(error_flag);
}

ferror() and feof() are intended to clarify ambiguous return values. Here that's not a problem since a NULL return value from fgets() can only mean end-of-file, but with for instance getw() such double checking is necessary.


next up previous contents
Next: Interactive Up: Input/Output Previous: Input/Output
Tim Love
1999-10-06