Part II • Managing Data in C
Listing 9.4. continued
fprintf(stdprn,
“Line %2d of 50 lines”
“ being written to stdprn by a program.\n\r”, i);
}
/* An explicit form feed is used to force a page eject * if the printer is a laser printer
*/
fprintf(stdprn, “\f”);
return (0);
}
This program shows how easy it is to use a printer from a C program. Listing 9.5 is a more flexible program—the user can choose the screen, the communications port, or the printer.
Listing 9.5. STDFILE.C.
/* STDFILE, written 1992 by Peter D. Hipson
*This program prints to the selected destination. It
*should be run under DOS on a PC.
*/ |
|
|
#include |
<stdio.h> |
// Make includes first part of file |
#include |
<string.h> |
// For string functions. |
#include |
<stdlib.h> |
// Standard include items. |
#include |
<process.h> // For exit(), etc. |
int main( |
|
// Define main() and the fact that this program |
int |
argc, |
// uses the passed parameters |
char |
*argv[], |
|
char |
*envp[] |
|
); |
|
|
Part II • Managing Data in C
Listing 9.5. continued
|
for (i = 0; i < 50; i++) |
|
|
{ |
|
/* |
stdprn is opened in the |
binary mode, so a CR/LF |
* |
must be supplied explicitly, using \n\r |
*/ |
|
|
|
fprintf(OutputFile, |
|
|
“Line %2d of 50 |
lines” |
|
“ being written |
to user-selected destination by a program.\n\r”, |
|
i); |
|
|
} |
|
/* |
Use an explicit form feed to force a page eject if the |
* printer is a laser printer. */
fprintf(OutputFile, “\f”);
return (0);
}
This program shows the effect of assigning standard file handles to a userspecified file. This technique enables you to have one output routine and several destinations. This is useful when you want the user to be able to preview a report or easily select a secondary printer connected to a communications port.
Low-Level I/O and File Handles
All file I/O functions described so far perform primarily high-level I/O, and all require stream files. (Functions that require stream files receive a FILE * structure pointer.)
You can also access a file more directly using low-level file I/O techniques. Before describing the details of these techniques, however, several important issues should be covered.
A file that has been opened as a stream file can be read and written to using lowlevel file I/O functions. To get the necessary integer file handle, you must use the fileno() function.
Disk Files and Other I/O |
C C C |
|
C9C |
|
C C C |
|
C |
A low-level file can be used with stream functions by opening it with the fdopen() function. Be careful not to take a file that has been opened as a stream file, get its file handle, then open it a second time with fdopen(). You would then have to close the file twice.
The low-level file I/O functions are shown in Table 9.2. These functions generally have a stream file function counterpart.
Table 9.2. Low-level file functions.
Function Description
close()
creat()
dup()
dup2()
Closes the specified file.
Creates a new file.
Creates a new, duplicate file handle.
Creates a new, duplicate file handle and sets the second (specified) file handle to the first file.
eof()
lseek()
open()
read()
sopen()
tell()
write()
Tests for an end-of-file.
Seeks (changes the read/write pointer) to a new place in the file.
Opens an existing file.
Reads an opened file.
Opens a file in shared mode.
Returns the value of the read/write pointer.
Writes data to a file that has been opened for output.
Before you use stream functions with a low-level file, be sure that you open the file using the correct stream function. Generally, it is best if you use one type of function with any specific file.
There are several reasons for using low-level functions, including the following:
•Low-level functions do not try to format data, read from a file, or write to a file.
•Low-level file I/O is not buffered. When an I/O statement is executed, what is written goes directly to the file. (This may slow the program’s execution.)
Part II • Managing Data in C
Most programs benefit from the use of stream functions. The capability to open, read, and write any data object is present in both low-level files and stream files. The problems caused by buffering, if important, can be circumvented using the file flushing routines.
Standard Low-Level File Handles
Because stdin, stdout, stdaux, stderr, and stdprn are stream files, they can be referenced using the fileno() function. These files can also be used with low-level I/O functions directly, however. The file handle numbers for the standard stream files follows:
stdin
stdout
stderr
stdaux
stdprn
These low-level file numbers should not be used if possible. The fileno() function is more portable, especially when a program must run on different systems.
Console and Port I/O
Much of the direct access to the computer’s terminal (the screen and keyboard) is system dependent. On a PC, you can have any of a number of keyboards, all of which have different keys, and different scan codes. Several functions interact more directly with the keyboard, and though not all are ANSI standard, they are often part of many C compilers. These functions are shown in Table 9.3.
You can easily simulate most console functions by using the stream functions and the predefined file handles. A few functions, however, have no equal. This section describes the console functions and how to use them.
Some of the most frequently used direct console functions are the character getting functions, getch() and getche(). The main difference between these two functions is that getch() does not echo the character pressed, and getche() does echo
Disk Files and Other I/O |
C C C |
|
C9C |
|
C C C |
|
C |
the character. Although the screen functions can be used to read a keypress and echo it to the screen, you must use the getch() function to read a keypress without echoing it to the screen.
Table 9.3. Console I/O functions.
Console function |
Description |
cgets()
cprintf()
cputs()
cscanf()
Gets a string from the console.
Performs a formatted print to the console.
Writes a string to the screen.
Performs a formatted read from the console (keyboard).
getch() |
Gets a character from the keyboard but does not echo |
|
the character to the screen. |
getche() |
Gets a character from the keyboard and echoes the |
|
character to the screen. |
kbhit() |
Returns immediately with the return code indicating |
|
whether a key has been pressed. Will not wait for a |
|
keypress. |
Writes a character to the screen.
Allows one character to be pushed back to the keyboard. The character put back does not need to be the last character read. Only one character may be put back.
The next most commonly used function is the kbhit() function, which has no stream function counterpart. The kbhit() function enables you to poll the keyboard for a keypress. Many business applications have little use for this function. Games, however, run in real time, so they must check for user input without stopping the action. The kbhit() function enables a program to do just that.
Listing 9.6, ARCADE.C, does processing while waiting for keyboard input from the user. By a far stretch of the imagination, you could consider this program a simple arcade game.
Part II • Managing Data in C
Listing 9.6. ARCADE.C.
/* ARCADE, written 1992 by Peter D. Hipson
*This is a simple arcade game that uses console I/O.
*It should be run under DOS on a PC. It also should
*be compiled with Microsoft C or a compiler that
*supports kbhit() and getch(). In addition, ANSI.SYS
*should be loaded before using this program, and the
*screen size is assumed to be 25 by 80.
*/ |
|
|
#include <stdio.h> |
// Make includes first part of file |
#include <conio.h> |
// Console I/O functions |
#include <string.h> |
// For string functions |
#include <stdlib.h> |
// Standard include items |
#include <process.h> |
// For exit(), etc. |
#include <time.h> |
// To seed random numbers |
/* ANSI.SYS screen control #defines follow: */ |
#define BOLD |
“\x1B[1m” |
#define NORMAL |
“\x1B[0m” |
#define RED |
“\x1B[31m” |
#define BLACK |
“\x1B[30m” |
#define GREEN |
“\x1B[32m” |
#define CLEAR_SCREEN |
“\x1B[2J” |
#define CLEAR_EOL |
“\x1B[K” |
#define MOVE_CURSOR |
“\x1B[%d;%df” |
#define UP |
‘\x48’ |
#define DOWN |
‘\x50’ |
#define LEFT |
‘\x4B’ |
#define RIGHT |
‘\x4D’ |
#define MAX_HEIGHT 25 #define MAX_WIDTH 80
#define HALF_SECOND (CLOCKS_PER_SEC / 2)
Part II • Managing Data in C
Listing 9.6. continued
while(1)
{
if (kbhit())
{/* A key has been pressed, so process it as necessary */ chChar = getch();
if (chChar == (char)NULL)
{
chChar = getch();
printf(MOVE_CURSOR, nVertical, nHorizontal);
printf(“ “);
switch(chChar)
{
case DOWN:
if (++nVertical > MAX_HEIGHT)
{
--nVertical;
}
break; case UP:
if (--nVertical < 1)
{
++nVertical;
}
break; case RIGHT:
if (++nHorizontal > MAX_WIDTH)
{
--nHorizontal;
}
break; case LEFT:
if (--nHorizontal < 1)
{
++nHorizontal;
}
break;
Disk Files and Other I/O
default:
break;
}
printf(MOVE_CURSOR, nVertical, nHorizontal);
if (nMoneyHorizontal == nHorizontal && nMoneyVertical == nVertical)
{
printf(“\a”);
}
printf(“?”);
}
else
{
if (chChar == ‘\x1b’)
{/* Exit on Esc keypress */ printf(CLEAR_SCREEN); exit(4);
}
}
}
else
{/* No key has been pressed. Move the money. */ ClockTime = clock() / HALF_SECOND;
if (ClockTime != OldClockTime)
{
OldClockTime = ClockTime;
printf(MOVE_CURSOR, nMoneyVertical, nMoneyHorizontal);
printf(“ “); /* Erase the money */
i = rand();
switch(i % 4) /* Allow four states */
{
case 0:
if (++nMoneyVertical > MAX_HEIGHT)
continues