v3.0.1 / chapter 3 of 5 / 01 aug 05 / greg goebel / public domain
* This chapter covers console (keyboard/display) and file I/O. You've already seen one console-I/O function, "printf()", and there are several others. C has two separate approaches toward file I/O, one based on library functions that is similar to console I/O, and a second that uses "system calls". These topics are discussed in detail below.
* Console I/O in general means communications with the computer's keyboard
and display. However, in most modern operating systems the keyboard and
display are simply the default input and output devices, and user can easily
redirect input from, say, a file or other program and redirect output to,
say, a serial I/O port:
type infile > myprog > com
Console I/O requires the declaration:
#include <stdio.h>
printf(): Print a formatted string to stdout.
scanf(): Read formatted data from stdin.
putchar(): Print a single character to stdout.
getchar(): Read a single character from stdin.
puts(): Print a string to stdout.
gets(): Read a line from stdin.
#include <conio.h>
getch(): Get a character from the keyboard (no need to press Enter).
getche(): Get a character from the keyboard and echo it.
kbhit(): Check to see if a key has been pressed.
printf( "This is a test!\n" );
printf( "Value1: %d Value2: %f\n", intval, floatval );
%d: decimal integer
%ld: long decimal integer
%c: character
%s: string
%e: floating-point number in exponential notation
%f: floating-point number in decimal notation
%g: use %e and %f, whichever is shorter
%u: unsigned decimal integer
%o: unsigned octal integer
%x: unsigned hex integer
%10d
%-10d
%6.3f
/* prtint.c */
#include <stdio.h>
void main()
{
printf( "<%d>\n", 336 );
printf( "<%2d>\n", 336 );
printf( "<%10d>\n", 336 );
printf( "<%-10d>\n", 336 );
}
<336>
<336>
< 336>
<336 >
/* prfloat.c */
#include <stdio.h>
void main()
{
printf( "<%f>\n", 1234.56 );
printf( "<%e>\n", 1234.56 );
printf( "<%4.2f>\n", 1234.56 );
printf( "<%3.1f>\n", 1234.56 );
printf( "<%10.3f>\n", 1234.56 );
printf( "<%10.3e>\n", 1234.56 );
}
<1234.560000>
<1.234560e+03>
<1234.56>
<1234.6>
< 1234.560>
< 1.234e+03>
/* prtstr.c */
#include <stdio.h>
void main()
{
printf( "<%2s>\n", "Barney must die!" );
printf( "<%22s>\n", "Barney must die!" );
printf( "<%22.5s>\n", "Barney must die!" );
printf( "<%-22.5s>\n", "Barney must die!" );
}
<Barney must die!>
< Barney must die!>
< Barne>
<Barne >
'\a': alarm (beep) character
'\p': backspace
'\f': formfeed
'\n': newline
'\r': carriage return
'\t': horizontal tab
'\v': vertical tab
'\\': backslash
'\?': question mark
'\'': single quote
'\"': double quote
'\0NN': character code in octal
'\xNN': character code in hex
'\0': null character
/* cscanf.c */
#include <stdio.h>
void main()
{
int val;
char name[256];
printf( "Enter your age and name.\n" );
scanf( "%d %s", &val, name );
printf( "Your name is: %s -- and your age is: %d\n", name, val );
}
If you include characters in the format code, "scanf()" will read in that
character and discard it. For example, if the example above were modified as
follows:
scanf( "%d,%s", &val, name );
If you precede a format code with an asterisk, the data will be read and
discarded. For example, if the example were changed to:
scanf( "%d%*c%s", &val, name );
The "scanf()" function will return the value EOF (an "int"), defined in "stdio.h", when its input is terminated.
* The "putchar()" and "getchar()" functions handle single character I/O. For
example, the following program accepts characters from standard input one at
a time:
/* inout.c */
#include <stdio.h>
void main ()
{
unsigned int ch;
while ((ch = getchar()) != EOF)
{
putchar( ch );
}
}
One word of warning on single-character I/O: if you are entering characters from the keyboard, most operating systems won't send the characters to the program until you press the "Enter" key, meaning that you can't really do single-character keyboard I/O this way.
The little program above is the essential core of a character-mode text
"filter", a program that can perform some transformation between standard
input and standard output. Such a filter can be used as an element to
construct more sophisticated applications:
type file.txt > filter1 | filter2 > outfile.txt
In SEEK state, it scans through whitespace (space, tab, or newline), echoing characters. If it finds a printing character, it converts it to uppercase and goes to REPLACE state. In REPLACE state, it converts characters to lowercase until it hits whitespace, and then goes back to SEEK state.
The program uses the "tolower()" and "toupper()" functions to make case
conversions. These two functions will be discussed in the next chapter.
/* caps.c */
#include <stdio.h>
#include <ctype.h>
#define SEEK 0
#define REPLACE 1
void main()
{
int ch, state = SEEK;
while(( ch = getchar() ) != EOF )
{
switch( state )
{
case REPLACE:
switch( ch )
{
case ' ':
case '\t':
case '\n': state = SEEK;
break;
default: ch = tolower( ch );
break;
}
break;
case SEEK:
switch( ch )
{
case ' ':
case '\t':
case '\n': break;
default: ch = toupper( ch );
state = REPLACE;
break;
}
}
putchar( ch );
}
}
puts( "Hello world!" );
/* cgets.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void main()
{
char word[256],
*guess = "blue";
integer i, n = 0;
puts( "Guess a color (use lower case please):" );
while( gets( word ) != NULL )
{
if( strcmp( word, guess ) == 0 )
{
puts( "You win!" );
exit( 0 );
}
else
{
puts( "No, try again." );
}
}
}
You can use these functions to implement filters that operate on lines of
text, rather than characters. A core program for such filters follows:
/* lfilter.c */
#include <stdio.h>
void main ()
{
char b[256];
while (( gets( b ) ) != NULL )
{
puts( b );
}
}
* The PC-based console-I/O functions "getch()" and "getche()" operate much as "getchar()" does, except that "getche()" echoes the character automatically.
The "kbhit()" function is very different in that it only indicates if a key has been pressed or not. It returns a nonzero value if a key has been pressed, and zero if it hasn't. This allows a program to poll the keyboard for input rather than hanging on keyboard input and waiting for something to happen. These functions require the "conio.h" header file, not the "stdio.h" header file.
* The file-I/O library functions are much like the console-I/O functions. In
fact, most of the console-I/O functions can be thought of as special cases of
the file-I/O functions. The library functions include:
fopen(): Create or open a file for reading or writing.
fclose(): Close a file after reading or writing it.
fseek(): Seek to a certain location in a file.
rewind(): Rewind a file back to its beginning and leave it open.
rename(): Rename a file.
remove(): Delete a file.
fprintf(): Formatted write.
fscanf(): Formatted read.
fwrite(): Unformatted write.
fread(): Unformatted read.
putc(): Write a single byte to a file.
getc(): Read a single byte from a file.
fputs(): Write a string to a file.
fgets(): Read a string from a file.
#include <stdio.h>
* The "fopen()" function opens and, if need be, creates a file. Its syntax
is:
<file pointer> = fopen( <filename>, <access mode> );
FILE *<file pointer>;
r: Open for reading.
w: Open and wipe (or create) for writing.
a: Append -- open (or create) to write to end of file.
r+: Open a file for reading and writing.
w+: Open and wipe (or create) for reading and writing.
a+: Open a file for reading and appending.
It is often useful to use the same statements to communicate either with
files or with standard I/O. For this reason, the "stdio.h" header file
includes predefined file pointers with the names "stdin" and "stdout". You
don't need to do an "fopen()" on them, you can just assign them to a file
pointer:
fpin = stdin;
fpout = stdout;
The "fclose()" function simply closes the file given by its file pointer
parameter. It has the syntax:
fclose( fp );
fseek( <file_pointer>, <offset>, <origin> );
SEEK_SET: Start of file.
SEEK_CUR: Current location.
SEEK_END: End of file.
The "rewind()", "rename()", and "remove()" functions are straightforward.
The "rewind()" function resets an open file back to its beginning. It has
the syntax:
rewind( <file_pointer> );
rename( <old_file_name_string>, <new_file_name_string> );
remove( <file_name_string> )
fprintf( <file pointer>, <string>, <variable list> );
/* fprpi.c */
#include <stdio.h>
void main()
{
int n1 = 16;
float n2 = 3.141592654f;
FILE *fp;
fp = fopen( "data", "w" );
fprintf( fp, " %d %f", n1, n2 );
fclose( fp );
}
16 3.14159
%d: decimal integer
%ld: long decimal integer
%c: character
%s: string
%e: floating-point number in exponential notation
%f: floating-point number in decimal notation
%g: use %e and %f, whichever is shorter
%u: unsigned decimal integer
%o: unsigned octal integer
%x: unsigned hex integer
The "fscanf()" function is to "fprintf()" what "scanf()" is to "printf()":
it reads ASCII-formatted data into a list of variables. It has the syntax:
fscanf( <file pointer>, <string>, <variable list> );
/* frdata.c */
#include <stdio.h>
void main()
{
int n1;
float n2;
FILE *fp;
fp = fopen( "data", "r" );
fscanf( fp, "%d %f", &n1, &n2 );
printf( "%d %f", n1, n2 );
fclose( fp );
}
Numeric modifiers can be used, of course. The "fscanf()" function returns the number of items that it successfully read, or the EOF code, an "int", if it encounters the end of the file or an error.
The following program demonstrates the use of "fprintf()" and "fscanf()":
/* fprsc.c */
#include <stdio.h>
void main()
{
int ctr, i[3], n1 = 16, n2 = 256;
float f[4], n3 = 3.141592654f;
FILE *fp;
fp = fopen( "data", "w+" );
/* Write data in: decimal integer formats
decimal, octal, hex integer formats
floating-point formats */
fprintf( fp, "%d %10d %-10d \n", n1, n1, n1 );
fprintf( fp, "%d %o %x \n", n2, n2, n2 );
fprintf( fp, "%f %10.10f %e %5.4e \n", n3, n3, n3, n3 );
/* Rewind file. */
rewind( fp );
/* Read back data. */
puts( "" );
fscanf( fp, "%d %d %d", &i[0], &i[1], &i[2] );
printf( " %d\t%d\t%dn", i[0], i[1], i[2] );
fscanf( fp, "%d %o %x", &i[0], &i[1], &i[2] );
printf( " %d\t%d\t%d\n", i[0], i[1], i[2] );
fscanf( fp, "%f %f %f %f", &f[0], &f[1], &f[2], &f[3] );
printf( " %f\t%f\t%f\t%f\n", f[0], f[1], f[2], f[3] );
fclose( fp );
}
16 16 16
256 256 256
3.141593 3.141593 3.141593 3.141600
fwrite( <array_pointer>, <element_size>, <count>, <file_pointer> );
The "fread()" function similarly has the syntax:
fread( <array_pointer>, <element_size>, <count>, <file_pointer> );
The following program stores an array of data to a file and then reads it
back using "fwrite()" and "fread()":
/* fwrrd.c */
#include <stdio.h>
#include <math.h>
#define SIZE 20
void main()
{
int n;
float d[SIZE];
FILE *fp;
for( n = 0; n < SIZE; ++n ) /* Fill array with roots. */
{
d[n] = (float)sqrt( (double)n );
}
fp = fopen( "data", "w+" ); /* Open file. */
fwrite( d, sizeof( float ), SIZE, fp ); /* Write it to file. */
rewind( fp ); /* Rewind file. */
fread( d, sizeof( float ), SIZE, fp ); /* Read back data. */
for( n = 0; n < SIZE; ++n ) /* Print array. */
{
printf( "%d: %7.3f\n", n, d[n] );
}
fclose( fp ); /* Close file. */
}
putc( <character>, <file pointer> );
<character variable> = getc( <file pointer> );
* The "fputs()" function writes a string to a file. It has the syntax:
fputs( <string / character array>, <file pointer> );
fputs( "This is a test", fptr );
fgets( <string>, <max_string_length>, <file_pointer> );
The following example program simply opens a file and copies it to another
file, using "fgets()" and "fputs()":
/* fcopy.c */
#include <stdio.h>
#define MAX 256
void main()
{
FILE *src, *dst;
char b[MAX];
/* Try to open source and destination files. */
if ( ( src = fopen( "infile.txt", "r" )) == NULL )
{
puts( "Can't open input file." );
exit();
}
if ( (dst = fopen( "outfile.txt", "w" )) == NULL )
{
puts( "Can't open output file." );
fclose( src );
exit();
}
/* Copy one file to the next. */
while( ( fgets( b, MAX, src ) ) != NULL )
{
fputs( b, dst );
}
/* All done, close up shop. */
fclose( src );
fclose( dst );
}
* File-I/O through system calls is simpler and operates at a lower level than
making calls to the C file-I/O library. There are seven fundamental file-I/O
system calls:
creat(): Create a file for reading or writing.
open(): Open a file for reading or writing.
close(): Close a file after reading or writing.
unlink(): Delete a file.
write(): Write bytes to file.
read(): Read bytes from file.
Use of these system calls requires a header file named "fcntl.h":
#include <fcntl.h>
<file descriptor variable> = creat( <filename>, <protection bits> );
The "filename" parameter gives the desired filename for the new file. The "permission bits" give the "access rights" to the file. A file has three "permissions" associated with it:
Allows data to be written to the file.
Allows data to be read from the file.
Designates that the file is a program that can be run.
These permissions can be set for three different levels:
Permissions apply to individual user.
Permissions apply to members of user's defined "group".
Permissions apply to everyone on the system.
For the "creat()" system call, the permissions are expressed in octal, with
an octal digit giving the three permission bits for each level of
permissions. In octal, the permission settings:
0644
0777
For example, to create a file named "data" with read and write permission for
everyone on the system, you would write:
#define RD_WR 0666
...
int fd; /* Define file descriptor. */
fd = creat( "data", RD_WR );
<file descriptor variable> = open( <filename>, <access mode> );
O_RDONLY: Open for reading only.
O_WRONLY: Open for writing only.
O_RDWR: Open for reading and writing.
int fd;
fd = open( "data", O_WRONLY );
The "close()" system call is very simple. All it does is "close()" an open
file when there is no further need to access it. The "close()" system call
has the syntax:
close( <file descriptor> );
The "unlink()" system call deletes a file. It has the syntax:
unlink( <file_name_string> );
* The "write()" system call writes data from a open file. It has the syntax:
write( <file descriptor>, <buffer>, <buffer length> );
While different data types may have different byte lengths on different
systems, the "sizeof()" statement can be used to provide the proper buffer
length in bytes. A "write()" call could be specified as follows:
float array[10];
...
write( fd, array, sizeof( array ) );
The "read()" system call reads data from a open file. Its syntax is exactly
the same as that of the "write()" call:
read( <file descriptor>, <buffer>, <buffer length> );