Topic 8: I/O Reading: Chapter 7 in Kernighan & Ritchie more details in Appendix B (optional) even more details in GNU C Library manual (optional) No C language primitives for I/O; all done via function calls. 1
Small Digression: C Library vs. System Calls System calls: functions provided with the Linux kernel, so that other programs may access system functionality. C Library includes access to these system calls. Also provides higher-level functions written for use in C programs. This topic: I/O using higher-level text-oriented C library functions defined in <stdio.h>. Also available: low-level I/O using system calls directly to read & write in binary format. 2
Example: Opening a Files To read from or write to a file (other than standard input/output): Need a "file pointer" type FILE *. To create a file pointer from a file name: FILE *fp = fopen(name, mode); File name: a string -- absolute or relative file name Mode: a string saying how the file will be used "r" = read "w" = write "a" = append In case of error: fopen returns NULL. 3
Reacting To Errors Goals for a useful program/function: legal input -> correct output illegal input -> informative output / return value Writing OS and other system programs: "garbage in, garbage out" is a very bad attitude! 4
Errors From C Library Functions Most C library functions deal with errors by doing two things: 1. returning a special value (like NULL for fopen) 2. setting the system variable errno errno is zero at start of program errno is not always reset when library functions are successful. to query errno, include <errno.h> User program should never change errno. 5
Error-Handling Steps Situation: You call a C library function like fopen, where an error is possible. 1. Check return value to see if error may have occurred (NULL for fopen). 2. If return value indicates possible error, check errno to verify error & find out what kind. 3. Report error to user and recover or end program. 6
Your Choices For Reporting Errors 1. ignore possibility of error bad idea except for quick throwaway programs! OR: check return value, and if it tells you there was an error: 2. write a general error message "could not open file", etc. OR: 3. compare errno with applicable error codes & write your own error message OR: 4. use library function to write an error message based on errno 7
Library Functions for Dealing With errno char *strerror(int errnum) Returns a string describing the error. Call this with errno as parameter and use the result in an error message. void perror(char *msg) Prints an error message to standard error, based on errno. If msg is not null, prints it first Libraries to include: <errno.h>: defines errno and macros for specific error values <string.h>: strerror & perror functions demo: IOtest1.c 8
My Advice Check results from library functions. If result indicates an error: report with perror. If simple way to recover, do it. Otherwise, abort program. What's important: realize that errors can occur error might be because of bug in program or bad input user needs to be told about nature of error aborting with informative error message is sometimes OK re-prompting in interactive program is OK crashing or behaving incorrectly because of error is not acceptable in a production program 9
Predefined File Pointers stdin, stdout, stderr: all declared in <stdio.h> stdin takes input from keyboard unless program is being run with standard input redirected stdout & stderr: writes to the screen unless program is being run with standard output/error redirected 10
Character-By-Character Input To read one character from a file: int getc(file *fp) Reads a single character from the file, not skipping white space. In case of error or end of file, returns EOF. How to tell the difference? errno To read one character from standard input: getchar() is equivalent to getc(stdin) demo: (IOtest2.c) 11
"ungetting" Characters int ungetc (int c, FILE *stream) "pushes" a character back onto the input stream Normally, c is the last character read from the file, but that's not required Most implementations only support one character pushback. Doesn't change the file, just "remembers" the pushed-back char. Why is this useful? Often you have to read one character after the end of something to realise you reached the end. Demo: IOtest3.c 12
Writing Characters int putc(int c, FILE *stream) Return value is: EOF if there was an error c if character was written To write one character to standard output: putchar(c) is equivalent to putc(c, stdout) 13
Writing Lines int fputs (char *s, FILE *stream) Writes s to the file. Does not write null or end of line at the end. If error: return value is EOF. int puts (char *s) Writes s to the standard output file with '\n' at the end. If error: return value is EOF. 14
Reading Lines char* fgets (char *s, int count, FILE *stream) Reads characters from the file into s until it reaches the end of the line or has read count-1 characters. Does not skip white space in the input. Always includes a null character at the end. If error or already at end of file: returns NULL (how can you tell the difference?) Otherwise, returns s. Demo: IOtest5.c 15
Reading Lines From Standard Input char* gets (char *s) Reads a line from the standard input. Dangerous don't use! (Why?) 16
Formatted Output We've already discussed printf: formatted output to standard output Formatted output to a file: int fprintf (FILE *stream, char *template,...) Return value: number of items printed -- or negative value if error 17
Formatted Input We've already discussed scanf: formatted input from standard input. Return value: number of values read EOF if hit end of file before any values read EOF if I/O error Input from a file: int fscanf (FILE *stream, const char *template,...) 18
"Output" To a String int sprintf (char *s, char *template,...) Formats output according to a template and copies to s instead of writing to a file. Always writes a null character at the end. Difficult to guarantee safety! Use with extreme care. Safe use of sprintf: IOtest6.c There are alternative versions of sprintf: one takes a maximum length as a parameter one allocates space for a result string on the heap 19
Closing Files When you're all done with a file: fclose(file); Two reasons to call fclose: 1. For an output file, makes sure all buffered output gets to the file. 2. Releases resources used by the file (on the heap). Result of fclose is an int: 0 if file was closed successfully EOF if an error occured 20