C provides buffered file streams for file access. Some C
platforms, such as Unix and DOS provide unbuffered file handles as well.
Buffered streams are accessed through a variable of type 'file
pointer'. The data type FILE
is defined in the header file stdio.h. Thus to declare a file pointer;
#include <stdio.h>
FILE
*ptr;
To open a stream C provides the function fopen(), which
accepts two parameters, the name of the file to be opened, and the access mode
for the file to be opened with. The access mode may be any one of;
Mode | Description |
r | Open for reading |
w | Create for writing, destroying any existing file |
a | Open for append, creating a new file if it doesn't |
exist | |
r+ | Open an existing file for reading and writing |
w+ | Create for reading and writing, destroying any |
existing file | |
a+ | Open for append, creating a new file if it doesn't exist. |
Optionaly either 'b' or 't' may be appended for binary or text mode. If
neither is appended, then the file stream will be opened in the mode described
by the global variable _fmode. Data read or written from file streams opened in
text mode undergoes conversion, that is the characters CR and LF are converted
to CR LF pairs on writing, and the CR LF pair is converted to a single LF on
reading. File streams opened in binary mode do not undergo conversion.
If fopen() fails to open the file, it returns a value of NULL (defined in
stdio.h) to the file pointer.
Thus, the following program will create a new file called "data.txt"
and open it for reading and writing;
#include <stdio.h>
void main()
{
FILE
*fp;
fp = fopen("data.txt","w+");
}
To close
a stream C provides the function fclose
(), which accepts the stream's file pointer as a parameter;
fclose
(fp);
If an error occurs in closing the file stream, fclose
() returns non zero.
There are four basic functions for receiving and sending data to and from
streams; fgetc(), fputc(), fgets() and fputs().
fgetc() simply reads a single character from the specified input stream;
char fgetc(FILE
*fp);
Its opposite number is fputc(), which simply writes a single character to the
specified input stream;
char fputc(char c, FILE
*fp);
fgets() reads a string from the input stream;
char *fgets(char s, int numbytes, FILE
*fp);
It stops reading when either numbytes - 1 bytes have been read, or a newline
character is read in. A null terminating byte is appended to the read string, s.
If an error occurs, fgets() returns NULL.
fputs() writes a null terminated string to a stream;
int fputs(char *s, FILE
*fp);
Excepting fgets(), which returns a NULL pointer if an error occurs, all the
other functions described above return EOF (defined in stdio.h) if an error
occurs during the operation.
The following program creates a copy of the file "data.dat" as
"data.old" and illustrates the use of fopen(), fgetc(), fputc() and
fclose
();
#include <stdio.h>
int main()
{
FILE *in;
FILE
*out;
in = fopen("data.dat","r");
if (in == NULL)
{
puts("\nUnable to open file data.dat for reading");
return(0);
}
out = fopen("data.old","w+");
if (out == NULL)
{
puts("\nUnable to create file data.old");
return(0);
}
/* Loop reading and writing one byte at a time until end-of-file */
while(!feof(in))
fputc(fgetc(in),out);
/* Close the file streams */
fclose (in);
fclose
(out);
return(0);
}
Example program using fputs() to copy text from stream stdin
(usually typed in at the keyboard) to a new file called "data.txt".
#include <stdio.h>
int main()
{
FILE *fp;
char text[100];
fp = fopen("data.txt","w+");
do
{
gets(text);
fputs(text,fp);
}
while(*text);
fclose (fp);
}
Random file access for streams is provided for by the fseek()
function that has the following prototype;
int fseek(FILE
*fp, long numbytes, int fromwhere);
fseek() repositions a file pointer associated with a stream
previously opened by a call to fopen(). The file pointer is positioned 'numbytes'
from the location 'fromwhere', which may be the file beginning, the current file
pointer position, or the end of the file, symbolised by the constants SEEK_SET,
SEEK_CUR and SEEK_END respectively. If a call to fseek() succeeds, a value of
zero is returned.
Associated with fseek() is ftell(), which reports the current
file pointer position of a stream, and has the following function prototype;
long int ftell(FILE
*fp);
ftell() returns either the position of the file pointer,
measured in bytes from the start of the file, or -1 upon an error occurring.
File handles are opened with the open() function that has the
prototype;
int open(char *filename,int access[,unsigned mode]);
If open() is successful, the number of the file handle is
returned. Otherwise open() returns -1.
The access integer is comprised from bitwise oring together of
the symbolic constants declared in fcntl.h. These vary from compiler to compiler
but may be;
O_APPEND If set, the file pointer will be set to the end of the
file prior to each write.
O_CREAT If the file does not exist it is created.
O_TRUNC Truncates the existing file to a length of zero bytes.
O_EXCL Used with O_CREAT
O_BINARY Opens the file in binary mode
O_TEXT Opens file in text mode
The optional mode parameter is comprised by bitwise oring of
the symbolic constants defined in stat.h. These vary from C compiler to C
compiler but may be;
S_IWRITE Permission to write
S_IREAD Permission to read
Once a file handle has been assigned with open(), the file may
be accessed with read() and write().
Read
() has the function prototype;
int read(int handle, void *buf, unsigned num_bytes);
It attempts to read 'num_bytes' and returns the number of
bytes actually read from the file handle 'handle', and stores these bytes in the
memory block pointed to by 'buf'.
Write() is very similar to read() and has the same function
prototype and return values, but writes 'num_bytes' from the memory block
pointed to by 'buf'.
Files opened with open() are closed using close
() that has the function prototype;
int close
(int handle);
close
() returns zero on success, and -1 if an error occurs trying to close the
handle.
Random access is provided by lseek(), which is very similar to
fseek(), except that it accepts an integer file handle as the first parameter
rather than a stream FILE
pointer.
This example uses file handles to read data from stdin
(usually the keyboard) and copy the text to a new file called "data.txt".
#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>
int main()
{
int handle;
char text[100];
handle =
open("data.txt",O_RDWR|O_CREAT|O_TRUNC,S_IWRITE);
do
{
gets(text);
write(handle,&text,strlen(text));
}
while(*text);
close (handle);
}
The ANSI standard on C defines file IO as by way of file
streams, and defines various functions for file access;
fopen() has the prototype;
FILE
*fopen(const char *name,const char *mode);
fopen() attempts to open a stream to a file name in a
specified mode. If successful a FILE
type pointer is returned to the file stream, if the call fails NULL is
returned. The mode string can be one of the following;
Mode Description
r Open for reading only
w Create for writing, overwriting any existing file with the same
name.
a Open for append (writing at end of file) or create the file if it
does not exist.
r+ Open an existing file for reading and writing.
w+ Create a new file for reading and writing.
a+ Open for append with read and write access.
fclose
() is used to close
a file stream previously opened by a call to fopen(). It has the prototype;
int fclose
(FILE
*fp);
When a call to fclose
() is successful, all buffers to the stream are flushed and a value of zero is
returned. If the call fails fclose() returns EOF.
Many host computers, and the IBM PC is no exception, use
buffered file access, that is when writing to a file stream the data is stored
in memory and only written to the stream when it exceeds a predefined number of
bytes. A power failure occurring before the data has been written to the stream
will result in the data never being written, so the function fflush() can be
called to force all pending data to be written. fflush() has the prototype;
int fflush(FILE
*fp);
When a call to fflush() is successful, the buffers connected with the stream are flushed and a value of zero is returned. On failure fflush() returns EOF.
The location of the file pointer connected with a stream can
be determined with the function ftell(). ftell() has the prototype;
long int ftell(FILE
*fp);
and returns the offset of the file pointer in bytes from the
start of the file, or -1L if the call fails.
Similarly, you can move the file pointer to a new position
with fseek(). fseek() has the prototype;
int fseek(FILE
*fp, long offset, int from_what_place);
fseek() attempts to move the file pointer, 'fp' 'offset' bytes
from the position 'from_what_place'. 'from_what_place' is predefined as one of;
SEEK_SET The file's beginning
SEEK_CUR The file pointer's current position
SEEK_END End of file
The offset may be a positive value to move the file pointer on
through the file, or negative to move backwards.
To move a file pointer quickly back to the start of a file,
and clear any references to errors that have occurred C provides the function
rewind() that has the prototype;
void rewind(FILE
*fp);
rewind(fp) is similar to fseek(fp,0L,SEEK_SET) in that they both set the file pointer to the start of the file, but whereas fseek() clears the EOF error marker, rewind() clears all error indicators.
Errors occurring with file functions can be checked with the function ferror() that has the prototype;
int ferror(FILE
*fp);
ferror() returns a nonzero value if an error has occurred on the specified stream. After checking ferror() and reporting any errors you should clear the error indicators, and this can be done by a call to clearerr() that has the prototype;
void clearerr(FILE
*fp);
The condition of reaching end of file (EOF) can be tested for with the predefined macro feof() that has the prototype;
int feof(FILE
*fp);
feof() returns a nonzero value if an end of file error indicator was detected on the specified file stream, and zero if the end of file has not yet been reached.
Reading data from a file stream can be achieved using several
functions; A single character can be read with fgetc() that has the prototype;
int fgetc(FILE
*fp);
fgetc() returns either the character read converted to an integer, or EOF if an error occurred.
Reading a string of data is achieved with fgets(). fgets()
attempts to read a string terminated by a newline character and of no more than
a specified number of bytes from the file stream. It has the prototype;
char *fgets(char s, int n, FILE
*fp);
A successful call to fgets() results in a string being stored
in 's' that is either terminated by a newline character, or that is 'n' - 1
characters long, which ever came first. The newline character is retained by
fgets(), and a null bytes is appended to the string. If the call fails a NULL
pointer is returned.
Strings may be written to a stream using fputs() that has the
prototype;
int fputs(const char *s,FILE
*fp);
fputs() writes all the characters in the string 's' to the stream 'fp' except the null terminating byte. On success, fputs() returns the last character written, on failure it returns EOF.
To write a single character to a stream use fputc() that has the prototype;
int fputc(int c,FILE
*fp);
If successful, fputc() returns the character written,
otherwise it returns EOF.
To read a large block of data, or a record from a stream you
can use fread() that has the prototype;
size_t fread(void *ptr,size_t size, size_t n, FILE
*fp);
fread() attempts to read 'n' items, each of length 'size' from
the file stream 'fp' into the block of memory pointed to by 'ptr'. To check the
success or failure status of fread() use ferror().
The sister function to fread() is fwrite() that has the
prototype;
size_t fwrite(const void *ptr,size_t size, size_t n,FILE
*fp);
that writes 'n' items each of length 'size' from the memory
area pointed to by 'ptr' to the specified stream 'fp'.
Formatted input from a stream is achieved with fscanf() that
has the prototype;
int fscanf(FILE
*fp, const char *format[,address ...]);
fscanf() returns the number of fields successfully stored, and
EOF on end of file. This short example shows how fscanf() is quite useful for
reading numbers from a stream, but hopeless when it comes to strings!
#include <stdio.h>
void main()
{
FILE *fp;
int a;
int b;
int c;
int d;
int e;
char text[100];
fp = fopen("data.txt","w+");
if(!fp)
{
perror("Unable to create file");
exit(0);
}
fprintf(fp,"1 2 3 4 5 \"A line of
numbers\"");
fflush(fp);
if (ferror(fp))
{
fputs("Error flushing stream",stderr );
exit(1);
}
rewind(fp);
if (ferror(fp))
{
fputs("Error rewind stream",stderr );
exit(1);
}
fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);
if (ferror(fp))
{
fputs("Error reading from stream",stderr );
exit(1);
}
printf ("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);
}
As you can see from the example, fprintf() can be used to write formatted data to a stream.
If you wish to store the position of a file pointer on a stream, and then later restore it to the same position you can use the functions fgetpos() and fsetpos(). fgetpos() reads the current location of the file pointer and has the prototype;
int fgetpos(FILE
*fp, fpos_t *pos);
fsetpos() repositions the file pointer and has the prototype;
int fsetpos(FILE
*fp, const fpos_t *fpos);
fpos_t is defined in stdio.h.
These functions are more convenient than doing an ftell()
followed by an fseek().
An open stream can have a new file associated with it in place
of the existing file by using the function freopen() that has the prototype;
FILE
*freopen(const char *name,const char *mode,FILE *fp);
freopen() closes the existing stream and then attempts to reopens it with the specified file name. This is useful for redirecting the predefined streams stdin , stdout and stderr to a file or device.
For example; if you wish to redirect all output intended to
stdout
(usually the host computer's display device) to a printer you might use;
freopen("LPT1","w",stdout
);
where LPT1 is the name of the printer device (on a PC host,
LPT1 is the name of the parallel port).
There are three predefined I/O streams; stdin , stdout , and stderr . The streams stdin and stdout default to the keyboard and display respectively, but can be redirected on some hardware platforms, such as the PC and under UNIX. The stream stderr defaults to the display and is not usually redirected by the operator. Thus it can be used for the display of error messages even when program output has been redirected, such as with;
fputs("Error message",stderr
);
The functions printf () and puts(), output data to the stream stdout , and can therefore be redirected by the operator of the program. scanf() and gets() accept input from the stream stdin .
As an example of file i/o with the PC consider the following short program that does a hex dump of a specified file to the predefined stream stdout , which may be redirected to a file using;
dump filename.ext > target.ext
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <string.h>
main(int argc, char *argv[])
{
unsigned counter;
unsigned char v1[20];
int f1;
int x;
int n;
if (argc != 2)
{
fputs("\nERROR: Syntax is dump f1\n",stderr );
return(1);
}
f1 = open(argv[1],O_RDONLY);
if (f1 == -1)
{
fprintf(stderr ,"\nERROR: Unable to open %s\n",argv[1]);
return(1);
}
fprintf(stdout
,"\nDUMP OF FILE
%s\n\n",strupr(argv[1]));
counter = 0;
while(1)
{
/* Set buffer to zero bytes */
memset(v1,0,20);
/* Read buffer from file */
x = _read(f1,&v1,16);
/* x will be 0 on EOF or -1 on error */
if (x < 1)
break
;
/* Print file offset to stdout */
fprintf(stdout
,"%06d(%05x) ",counter,counter);
counter += 16;
/* print hex values of buffer to stdout */
for(n = 0; n < 16; n++)
fprintf(stdout
,"%02x ",v1[n]);
/* Print ascii values of buffer to stdout */
for(n = 0; n < 16; n++)
{
if ((v1[n] > 31) && (v1[n] < 128))
fprintf(stdout ,"%c",v1[n]);
else
fputs(".",stdout );
}
/* Finish the line with a new line */
fputs("\n",stdout );
}
/* successful termination */
return(0);
}
Links: Home C Programming Guide C++ programming Guide