CS 2213 Advanced Programming
Input and Output


Read Chapter 7 of the text.
Start of Class Tuesday, November 28, 2000
Start of Class Thursday, November 30, 2000
Previous Topic: Structures
Next Topic: Not Ready Yet

7.1 Standard Input and Output

We already know about using
int getchar(void);
int putchar(int);
and about redirection of standard input and output.

It is also possible to take the standard output of one program and make it the standard input of another.
This is called piping or using a pipe.

Example:

ls | sort -r
Will list your directory sorted in reverse order.
ls -l | sort -n +4
Will give a long listing sorted by file size.


7.2 Formatted Output --- printf

We have already used printf in its simplest forms.
The prototype is
int printf(const char *format, arg1, arg2, arg3, ...);
The first parameter is a string which contains ordinary characters and conversion specifications. The ordinary characters are just sent to standard output and the conversion specifications cause some form of conversion.

Each conversion specification begins with % and ends with a conversion character. You are familiar with some of the conversion characters: c, s, d, etc.

Example:

printf("This is the value of x:%d\n",x);
Some format specifications can contain alignment information, field width or a precision.

These must be in the following order if they appear:

The field width and precision must be constant integers or a *. A * means that the value is taken from the next parameter.

Here are the ones you must know about:

For more information, try:
man -s3S printf
Example: Consider "hello world" printed with the following format specifications:
:%s:          :hello, world:
:%10s:        :hello, world:
:%.10s:       :hello, wor:
:%-10s:       :hello, world:
:%15s:        :   hello, world:
:%.15s:       :hello, world:
:%-15s:       :hello, world   :
:%15.10s:     :     hello, wor:
:%-15.10s:    :hello, wor     :
Here is an example of using the * as a filed width:
void printTree(int vals[], int size) {
   int currentsize = 40;
   int numperline = 1;
   int perlinecount = 0;
   int i;

   printf("\n");
   if (size <= 0) return;
   for (i=0;i<size;i++) {
      if (perlinecount > 0)
         printf("%*s",currentsize,"");
      printf("%*d",currentsize,vals[i]);
      perlinecount++;
      if (perlinecount == numperline) {
         perlinecount = 0;
         numperline *= 2;
         currentsize /= 2;
         printf("\n");
      }  
   }   
   printf("\n");
}
You can output to a string rather than standard output with
int sprintf(char *string, char *format, arg1, arg2, ...);
You must make sure that enough space has been allocated for the output.

Both printf and sprintf return the number of bytes transmitted (not including the string terminator in the case of sprintf).


Variable-length Argument Lists

We will skip this for now.


Formatted Input --- scanf

It is easy to use scanf to read in using free format.
This means using only format specifications with no flags, widths, or precisions.

It skips over white space in the input.
Blanks and tabs are ignored in the format string.

int scanf(char *format, arg1, arg2, ...);
Note that since we are setting values, the args need to be pointers.

It returns the number of items matched or EOF if input ends before the first match.

Note that the f format specifier is for floats, not doubles.
To input a double you need to use lf.

Find as many errors as you can in the following:
Example: read in two integers, a double and a string:

int n,m;
double x;
char *s;
scanf("%d%d%f%s",n,m,x,s);






int n,m;
double x;
char s[100];
scanf("%d%d%lf%s",&n,&m,&x,s);


7.5 File Access

File access is usually done using what is called a file handle.
The general procedure to read a file is as follows:
In Unix, there are two ways of using file handles:
  • file descriptors: discussed in Systems Programming is specific to Unix
  • file pointers: part of the C programming language

    In C, a file pointer is a pointer to a structure of type FILE.
    We are not concerned with the internal structure of this data type.

    To create a file pointer from a file name, use the fopen function:

    FILE *fopen(char *name, char *mode)
    
    The first parameter is the name of the file.

    The second parameter is a short string that describes what you want to do with the file.
    Here are the most-used options:

  • "r" for reading
  • "w" for writing (destroys old contents if it existed)
  • "a" for writing to the end
  • "r+" for update (reading and writing)
  • "w+" for update (destroys old contents if it existed)
  • "a+" for update writing to end (destroys old contents if it existed)

    On some systems (not Unix) you need to also specify b as part of the mode string for binary files.

    There are version of some of the functions we already know about that can be used for file I/O:

    int getchar(void)int getc(FILE *fp)
    int putchar(int c)int putc(int c, FILE *fp)
    int printf(char *format, ...)int fprintf(FILE *fp, char *format, ...)
    int scanf(char *format, ...)int fscanf(FILE *fp, char *format, ...)

    You can use these new ones to do I/O to standard input and standard output without fopen by using the predefined file pointers stdin and stdout.

    In fact, in some systems we have:

    #define getchar() getc(stdin)
    #define putchar(c) putc((c),stdout)
    
    with similar definitions for printf and scanf.

    There is also another predefined file pointer called stderr.

    When you are done with file I/O you should close the corresponding file with

    int fclose(FILE *fp):
    
    This is automatically done when you do a return from main.

    Example: concatenating files

    The Unix command cat can take any number of command line parameters which are names of files.
    The files are sent to standard output in the order they appear on the command line.
    If no command line parameters are given, the input comes from standard input.

    Example:
    cat file1 file2 file3 > file4

    Here is a simple version of cat

    #include <stdio.h>
    main (int argc, char *argv[]) {
       FILE *fp;
       void filecopy(FILE *, FILE *);
    
       if (argc == 1)
          filecopy(stdin,stdout);
       else
          while (--argc > 0)
             if ( (fp = fopen(*++argv, "r")) == NULL) {
                printf("cat: can't open %s\n",*argv);
                return 1;
             }
             else {
                filecopy(fp,stdout);
                fclose(fp);
             }
       return 0;
    }
    void filecopy(FILE *ifd, FILE *ofd) {
       int c;
       while (c=getc(ifp) != EOF)
          putc(c, ofp);
    }
    
    One of the problems with this version is that if you redirect the output you never see the output.


    Error Handling --- stderr and exit

    Another predefined file pointer is stderr.
    This will normally output to the screen just like stdout but it will not be redirected with >.

    Another useful function is

    int ferror(FILE *fp);
    
    which returns true if an error has previously occurred reading or writing to the named file pointer.

    Here is another version of out cat

    main (int argc, char *argv[]) {
       FILE *fp;
       void filecopy(FILE *, FILE *);
       char *prog = argv[0];
    
       if (argc == 1)
          filecopy(stdin,stdout);
       else
          while (--argc > 0)
             if ( (fp = fopen(*++argv, "r")) == NULL) {
                fprintf(stderr,"%s: can't open %s\n",prog,*argv);
                return 1;
             }
             else {
                filecopy(fp,stdout);
                fclose(fp);
             }
       if (ferror(stdout)) {
          fprintf(stderr,"%s: error writing stdout\n",prog);  
          return 2;
       }
       return 0;
    }
    

    Another useful function is exit which exits your program. This can be called from a function other than main.

    In general you should avoid doing this unless absolutely necessary.

    When called from main, exit(expr) does exactly the same thing as return expr.


    Miscellaneous Functions

    String Functions You should know about the following defined in string.h:
    strcat(s,t)
    strncat(s,t,n)
    strcmp(s,t)
    strncmp(s,t,n)
    strcpy(s,t)
    strncpy(s,t,n)
    strlen(s)
    strchr(s,c)     return a pointer to the first c in s or NULL
    strrchr(s,c)    return a pointer to the last c in s or NULL
    
    Other useful functions
    These are defined in stdlib.h:
    int atoi(const char *str);
    double atof(const char *str);
    

    Mathematical Functions
    These are defined in math.h and you will need to add -lm to the end of your compile line.
    All take double parameters and return double:

    sin(x)
    cod(x)
    atan2(y,x)
    exp(x)
    log(x)
    log10(x)
    pow(x,y)
    sqrt(x)
    fabs(x)
    
    
    
    
    
    Practice with doubly linked lists:
    typedef struct nodeentry {
       stringval sval;
       struct nodeentry *next;
       struct nodeentry *prev;
    } nodeentry;
    
    typedef struct {
       nodeentry *front;
       nodeentry *rear;
    } doublelist;
    
    static doublelist q;

    int insert_after(stringval sval, nodeentry *nentryp);
    int insert_before(stringval sval, nodeentry *nentryp);
    stringval remove(nodeentry *nentryp);