Low-Level Reading and Writing

The low-level file functions are unbuffered. When you call write, the bytes are written directly to the disk file. On the other hand, the standard file function fwrite doesn't write data to disk; it writes to a buffer. The buffer is transferred to disk when the buffer fills up, when the fflush function is called, or when the file is closed.

The program RWFILE.C illustrates some of the low-level, file-handling commands. It creates a file, writes to it, and closes it. Then the file is opened for reading and the contents of the file are displayed on the screen.

/* RWFILE.C: Read and write a file. */

#include <stdio.h>

#include <string.h>

#include <fcntl.h>

#include <sys\types.h>

#include <sys\stat.h>

#include <io.h>

#define BUFF 512

main()

{

char inbuffer[BUFF];

char outbuffer[BUFF];

int infile, outfile, length, num;

strcpy( outbuffer, "Happy Birthday." );

length = strlen( outbuffer );

length++;

if( (outfile = open( "testfile.bin",

O_CREAT | O_WRONLY | O_BINARY, S_IWRITE )) != -1 )

{

if( (num = write( outfile, outbuffer, length )) == -1 )

perror( "Error in writing" );

printf( "\nBytes written to file: %d\n", num );

close( outfile );

}

else

perror( "Error opening outfile" );

if( (infile = open( "testfile.bin", O_RDONLY | O_BINARY )) != -1 )

{

while( length = read( infile, inbuffer, BUFF ) )

printf( "%d bytes received so far.\n", length );

close( infile );

printf( "%s\n", inbuffer );

}

else

perror( "Error opening infile" );

}

Several header files must be included:

#include <stdio.h>

#include <fcntl.h>

#include <sys\types.h>

#include <sys\stat.h>

#include <io.h>

The symbolic constant BUFF is defined as 512. This value is used immediately in the declaration of two buffers:

char inbuffer[BUFF];

char outbuffer[BUFF];

Note that we don't need FILE structures anywhere in the program. The standard I/O routines automatically allocated space for a buffer. Since we're operating closer to the DOS level, we must allocate our own buffers, instead of relying on the I/O routines. If you set the buffer size to a sufficiently large value, QuickC will run out of stack space. When this happens, you may either make the buffers global variables or use the malloc function to allocate an additional chunk of memory.

The open function takes three parameters:

if( (outfile = open( "testfile.bin",

O_CREAT | O_WRONLY | O_BINARY, S_IWRITE )) != -1 )

The first parameter is the filename. The second is a sequence of “oflags” that are combined with the OR operator. The oflags determine which type of file will be opened: it will be created (O_CREAT), it will be write-only (O_WRONLY), and it will be a binary—not a text—file (O_BINARY). When you create a new file, you must include the third parameter: S_IWRITE.

The open function returns a file handle, which is assigned to the integer variable outfile. Note that this is an integer, not a pointer to a FILE structure. If anything goes wrong, a value of –1 is returned by open, and we should test for this.

Table 11.4 summarizes the differences between fopen and open.

Table 11.4 Standard vs. Low-Level I/O

Function Error Parameters Returns Condition

fopen File name, type (r, w, a), and mode (t, b) Pointer to FILE NULL
open File name, oflags File handle (integer) –1

Low-Level Writing

The write function takes three parameters:

1.The file handle returned by open

2.The address of the buffer

3.The number of bytes to write

You, the programmer, are responsible for filling up the buffer. The write function returns the number of bytes actually written to the file.

if( (num = write( outfile, outbuffer, length )) == -1 )

perror( "Error in writing" );

printf( "\nBytes written to file: %d\n", num );

close( outfile );

Low-Level Reading

Next, we open the file for reading. Again, the oflags are required:

if( (infile = open( "testfile.bin", O_RDONLY | O_BINARY )) != -1 )

{

while( length = read( infile, inbuffer, BUFF ) )

printf( "%d bytes received so far.\n", length );

close( infile );

printf( "%s\n", inbuffer );

}

The read function takes three parameters:

1.The file handle

2.The address of the buffer

3.The size of the buffer

The value returned is the number of bytes read. The while loop continues as long as there are characters in the file. In a real application, you'll have to handle the bytes stored in the buffer.

As a general rule, you should not mix buffered and unbuffered routines. Use the standard routines or the low-level routines, but not both.