Вы находитесь на странице: 1из 59

Lecture 11, Page 1

BIL 104E

INTRODUCTION TO
SCIENTIFIC AND ENGINEERING

COMPUTING

Compiled by Ergin TARI

Lecture 11, Page 2

INFORMATION TO USERS
THE NOTES IN THE FOLLOWING SLIDES ARE COMPILED FROM FREE VERSIONS OF THE BOOKS Sams Teach Yourself C in 21 Days (Sams Teach Yourself) By Peter Aitken and Sams Teach Yourself C in 24 Hours (Published by Sams) By Tony Zhang.
The electronic versions of these and other books can be found in the web pages including

http://server11.hypermart.net/davidbook901/data/c/c1f8c0d9.htm http://www.informit.com/itlibrary http://www.free-book.co.uk/computers-internet/programming/c/ http://aelinik.free.fr/c/

Compiled by Ergin TARI

Lecture 11, Page 3

BIL104E: Introduction to Scientific and Engineering Computing, Summer 2007 Lecture 11


Outline Introduction Files and streams Opening a file with fopen() Closing a file with fclose() The feof() function .. The fgetc() and fputc() functions The fgets() and fputs() functions The fread() and fwrite() functions Summary
Compiled by Ergin TARI

Lecture 11, Page 4

Introduction

Data files
Can be created, updated, and processed by C
programs Are used for permanent storage of large amounts of

data
Storage of data in variables and arrays is only temporary

Compiled by Ergin TARI

Lecture 11, Page 5

The Data Hierarchy

Data Hierarchy:
Bit smallest data item
Value of 0 or 1

Byte 8 bits
Used to store a character Decimal digits, letters, and special symbols

Field group of characters conveying meaning


Example: your name

Record group of related fields


Represented by a struct

Example: In a payroll system, a record for a particular employee that contained his/her identification number, name, address, etc.
Compiled by Ergin TARI

Lecture 11, Page 6

The Data Hierarchy

Data Hierarchy (continued):


File group of related records
Example: payroll file

Database group of related files


Sally Tom Judy Iris Randy Black Blue Green Orange Red

File

Judy Judy

Green Field
(ASCII character J)

Record

01001010 Byte 1 Bit

Compiled by Ergin TARI

Lecture 11, Page 7

Disk Files
In former weeks, "Reading from and Writing to Standard
I/O," you learned how to read or write characters through

standard input or output. In this lesson you'll learn to read


data from or write data to disk files.

Compiled by Ergin TARI

Lecture 11, Page 8

Files Versus Streams


What Is a File?
In C, a file can refer to a disk file, a terminal, a printer, or a tape drive. In other words, a file represents a concrete device with which you want to exchange information.

What Is a Stream?
The data flow you transfer from your program to a file, or vice versa, is called a stream, which is a series of bytes. Not like a file, a stream is deviceindependent. All streams have the same behavior. To perform I/O operations, you can read from or write to any type of files by simply associating a stream to the file. There are two formats of streams. The first one is called the text stream, which consists of a sequence of characters (that is, ASCII data). Depending on the compilers, each character line in a text stream may be terminated by a newline character. Text streams are used for textual data, which has a consistent appearance from one environment to another, or from one machine to another. The second format of streams is called the binary stream, which is a series of bytes. The content of an .exe file would be one example. Binary streams are primarily used for nontextual data, which is required to keep the exact contents of the file.
Compiled by Ergin TARI

Files and Streams


C views each file as a sequence of bytes

Lecture 11, Page 9

File ends with the end-of-file marker(EOF)


Or, file ends at a specified byte

Stream created when a file is opened

Provide communication channel between files and programs Opening a file returns a pointer to a FILE structure
Example file pointers: stdin - standard input (keyboard) stdout - standard output (screen) stderr - standard error (screen)

Compiled by Ergin TARI

Lecture 11, Page 10

Types of Disk Files


C streams come in two kinds: text and binary. You can associate either type of stream with a file, and it's important that you understand the distinction in order to use the proper mode for your files. Text streams are associated with text-mode files. Text-mode files consist of a sequence of lines. Each line contains zero or more characters and ends with one or more characters that signal end-of-line. The maximum line length is 255 characters. It's important to remember that a "line" isn't a C string; there is no terminating NULL character (\0). When you use a text-mode stream, translation occurs between C's newline character (\n) and whatever character(s) the operating system uses to mark end-of-line on disk files. On DOS systems, it's a carriage-return linefeed (CR-LF) combination. When data is written to a text-mode file, each \n is translated to a CR-LF; when data is read from a disk file, each CR-LF is translated to a \n. On UNIX systems, no translation is done--newline characters remain unchanged. Binary streams are associated with binary-mode files. Any and all data is written and read unchanged, with no separation into lines and no use of end-of-line characters. The NULL and end-of-line characters have no special significance and are treated like any other byte of data. Some file input/output functions are restricted to one file mode, whereas other functions can use either mode.

Compiled by Ergin TARI

Lecture 11, Page 11

Filenames
Every disk file has a name, and you must use filenames when dealing with disk
files. Filenames are stored as strings, just like other text data. The rules as to what is acceptable for filenames and what is not differ from one operating system to another. In DOS and Windows 3.x, a complete filename consists of a name that has from one to eight characters, optionally followed by a period and an extension that has from one to three characters. In contrast, the Windows 95 and Windows NT operating systems, as well as most UNIX systems, permit filenames up to 256 characters long. Operating systems also differ in the characters that are permitted in filenames. In Windows 95, for example, the following characters are not permitted: /\:*?"<>| You must be aware of the filename rules of whichever operating system you're writing for.
Compiled by Ergin TARI

Lecture 11, Page 12

Filenames
A filename in a C program also can contain path information. The path specifies the drive and/or directory (or folder) where the file is located. If you specify a filename without a path, it will be assumed that the file is located at whatever location the operating system currently designates as the default. It's good programming practice to always specify path information as part of your filenames. On PCs, the backslash character is used to separate directory names in a path. For example, to DOS and Windows, the name You are strictly required to do if use windows z:\data\list.txt refers to a file named LIST.TXT in the directory \DATA on drive Z. Remember that the backslash character has a special meaning to C when it's in a string. To represent the backslash character itself, you must precede it with another backslash. Thus, in a C program, you would represent the filename as follows: char *filename = z:\\data\\list.txt"; If you're entering a filename using the keyboard, however, enter only a single backslash.

Not all systems use the backslash as the directory separator. For example, UNIX uses the forward slash (/).

Compiled by Ergin TARI

Lecture 11, Page 13

The Basics of Disk File I/O


Pointers of FILE
The FILE structure is the file control structure defined in the header file stdio.h. A pointer of type FILE is called a file pointer, which

references a disk file. A file pointer is used by a stream to conduct the


operation of the I/O functions. For instance, the following defines a file pointer called fptr:

FILE *fptr;

In the FILE structure there is a member, called the file position


indicator, that points to the position in a file where data will be read from or written to.
Compiled by Ergin TARI

Lecture 11, Page 14

Opening a File with fopen() function


The syntax for the fopen() function is

#include <stdio.h>

FILE *fopen(const char *filename, const char *mode);

Here filename is a char pointer that references a string of a filename. The filename is given to the file that is about to be opened by the fopen() function. mode points to another string that specifies the way to open the file. The fopen() function returns a pointer of type FILE. If an error occurs during the procedure to open a file, the fopen() function returns a null pointer.

Compiled by Ergin TARI

Lecture 11, Page 15

Opening a File with fopen() function


The following list shows the possible ways to open a file by various strings of modes:
"r" opens an existing text file for reading. "w" creates a text file for writing. "a" opens an existing text file for appending. "r+" opens an existing text file for reading or writing. "w+" creates a text file for reading or writing. "a+" opens or creates a text file for appending.

"rb" opens an existing binary file for reading.


"wb" creates a binary file for writing. "ab" opens an existing binary file for appending. "r+b" opens an existing binary file for reading or writing.

"w+b" creates a binary file for reading or writing.


"a+b" opens or creates a binary file for appending.

Note that you might see people use the mode "rb+" instead of "r+b". These two strings are equivalent. Similarly, "wb+" is the same as "w+b"; "ab+" is equivalent to "a+b".
Compiled by Ergin TARI

Lecture 11, Page 16

Example: Opening a File with fopen() function


The following statements try to open a file called test.txt:
FILE *fptr; if ( (fptr = fopen("test.txt", "r")) == NULL){

printf("Cannot open test.txt file.\n"); exit(1); }

Here "r" is used to indicate that a text file is about to be opened for
reading only. If an error occurs when the fopen() function tries to open the file, the function returns a null pointer. Then an error message is

printed out by the printf() function and the program is aborted by


calling the exit() function with a nonzero value.

Compiled by Ergin TARI

Lecture 11, Page 17

Closing a File with fclose() function


After a disk file is read, written, or appended with some new data, you have to disassociate the file from a specified stream by calling the fclose() function.

The syntax for the fclose() function is


#include <stdio.h> int fclose(FILE *stream);

Here stream is a file pointer that is associated with a stream to the opened file. If fclose() closes a file successfully, it returns 0. Otherwise, the function returns EOF. Normally, the fclose() function fails only when the disk is removed before the function is called or there is no more space left on the disk.
Compiled by Ergin TARI

Lecture 11, Page 18

Example: opening and closing a text file


1: /* Opening and closing a file */ 2: #include <stdio.h> 4: enum {SUCCESS, FAIL}; 5: 6: main(void) 7: { 8: FILE *fptr; 9: char filename[]= "haiku.txt"; 10: int reval = SUCCESS; 11: 12: if ((fptr = fopen(filename, "r")) == NULL){ 13: printf("Cannot open %s.\n", filename); 14: reval = FAIL; 15: } else { 16: printf("The value of fptr: 0x%p\n", fptr); 17: printf("Ready to close the file."); 18: fclose(fptr); 19: } 20: 21: return reval; 22: }

Output
The value of fptr: 0x013E Ready to close the file.

Compiled by Ergin TARI

Lecture 11, Page 19

ANALYSIS
From the expression in line 12, you can see that the fopen() function tries to open a text file with the name contained by the character array filename for reading. The filename array is defined and initialized with the name haiku.txt in line 9. If an error occurs when you try to open the text file, the fopen() function returns a null pointer. Line 13 then prints a warning message, and line 14 assigns the value represented by the enum name FAIL to the int variable reval. From the declaration of the enum data type in line 4, we know that the value of FAIL is 1. If, however, the fopen() function opens the text file successfully, the statement in line 16 prints the value contained by the file pointer fptr. Line 17 tells the user that the program is about to close the file, and line 18 then closes the file by calling the fclose() file. In line 21, the return statement returns the value of reval that contains 0 if the text file has been opened successfully, or 1 otherwise. From the output shown on my screen, I see that the value held by the file pointer fptr is 0x013E after the text file is opened.
Compiled by Ergin TARI

Lecture 11, Page 20

Detecting the End of a File


Sometimes you know exactly how long a file is, so there's no need to be able to detect the file's end. For example, if you used fwrite() to save a 100-element integer array, you know the file is 200 bytes long (assuming 2-byte integers). At other times, however, you don't know how long the file is, but you still want to read data from the file, starting at the beginning and proceeding to the end. There are two ways to detect end-of-file. When reading from a text-mode file character-by-character, you can look for the end-of-file character. The symbolic constant EOF is defined in STDIO.H as -1, a value never used by a "real" character. When a character input function reads EOF from a text-mode stream, you can be sure that you've reached the end of the file. For example, you could write the following: while ( (c = fgetc( fp )) != EOF ) With a binary-mode stream, you can't detect the end-of-file by looking for -1, because a byte of data from a binary stream could have that value, which would result in premature end of input. Instead, you can use the library function feof(), which can be used for both binary- and text-mode files: int feof(FILE *fp); The argument fp is the FILE pointer returned by fopen() when the file was opened. The function feof() returns 0 if the end of file fp hasn't been reached, or a nonzero value if end-of-file has been reached.
Compiled by Ergin TARI

Lecture 11, Page 21

feof() function
In C, a function called feof() can be used to determine when the end of a file is encountered. This function is more useful when you're reading a binary file because the values of some bytes may be equal to the value of EOF. If you determine the end of a binary file by checking the value returned by fread(), you may end up at the wrong position. Using the feof() function helps you to avoid mistakes in determining the end of a file.

The syntax for the feof() function is #include <stdio.h> int feof(FILE *stream);

Here stream is the file pointer that is associated with an opened file. The feof() function returns 0 if the end of the file has not been reached; otherwise, it returns a nonzero integer.
Compiled by Ergin TARI

Lecture 11, Page 22 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: /* Detecting end-of-file. */ #include <stdlib.h> #include <stdio.h> #define BUFSIZE 100 main() { char buf[BUFSIZE]; char filename[60]; FILE *fp;

Enter name of text file to display: hello.c #include <stdio.h> main() { printf("Hello, world."); return(0); }

puts("Enter name of text file to display: "); gets(filename); /* Open the file for reading. */ if ( (fp = fopen(filename, "r")) == NULL) { fprintf(stderr, "Error opening file."); exit(1); } /* If end of file not reached, read a line and display it. */

while ( !feof(fp) ) { fgets(buf, BUFSIZE, fp); printf("%s",buf); }


fclose(fp); return(0); }
Compiled by Ergin TARI

Lecture 11, Page 23

Formatted File Output


Formatted file output is done with the library function fprintf(). The prototype of fprintf() is in the header file STDIO.H, and it reads as follows: int fprintf(FILE *fp, char *fmt, ...); The first argument is a pointer to type FILE. To write data to a particular disk file, you pass the pointer that was returned when you opened the file with fopen(). The second argument is the format string. You learned about format strings in the discussion of printf(). The format string used by fprintf() follows exactly the same rules as printf(). In a function prototype, ellipses represent a variable number of arguments. In other words, in addition to the file pointer and the format string arguments, fprintf() takes zero, one, or more additional arguments. This is just like printf(). These arguments are the names of the variables to be output to the specified stream. Remember, fprintf() works just like printf(), except that it sends its output to the stream specified in the argument list. In fact, if you specify a stream argument of stdout, fprintf() is identical to printf().
Compiled by Ergin TARI

Lecture 11, Page 24

1: /* Demonstrates the fprintf() function. */ 2: #include <stdlib.h> 3: #include <stdio.h> 4: Enter 5 floating-point numerical 5: void clear_kb(void); 6: 3.14159 7: main() 9.99 8: { 9: FILE *fp; 1.50 10: float data[5]; 3. 11: int count; 1000.0001 12: char filename[20]; 13: Enter a name for the file. 14: puts("Enter 5 floating-point numerical values."); numbers.txt 15: 16: for (count = 0; count < 5; count++) 17: scanf("%f", &data[count]); 18: 19: /* Get the filename and open the file. First clear stdin */ 20: /* of any extra characters. */ 21: 22: clear_kb(); 23: 24: puts("Enter a name for the file."); 25: gets(filename); 26: 27: if ( (fp = fopen(filename, "w")) == NULL) 28: { 29: fprintf(stderr, "Error opening file %s.", filename); 30: exit(1); 31: } 32: 33:

values.

Compiled by Ergin TARI

Lecture 11, Page 25

/* Write the numerical data to the file and to stdout. */ 34: 35: for (count = 0; count < 5; count++) 36: { 37: fprintf(fp, "\ndata[%d] = %f", count, data[count]); 38: fprintf(stdout, "\ndata[%d] = %f", count, data[count]); 39: } 40: fclose(fp); 41: printf("\n"); 42: return(0); 43: } Enter 5 floating-point numerical values. 44: 3.14159 45: void clear_kb(void) 46: /* Clears stdin of any waiting characters. */ 9.99 47: { 1.50 48: char junk[80]; 3. 49: gets(junk); 1000.0001 50: }

Enter a name for the file. numbers.txt data[0] = 3.141590 data[1] = 9.990000 data[2] = 1.500000 data[3] = 3.000000 data[4] = 1000.000122

Compiled by Ergin TARI

Lecture 11, Page 26

Formatted File Input


For formatted file input, use the fscanf() library function, which is used like scanf(), except that input comes from a specified stream instead of from stdin. The prototype for fscanf() is int fscanf(FILE *fp, const char *fmt, ...); The argument fp is the pointer to type FILE returned by fopen(), and fmt is a pointer to the format string that specifies how fscanf() is to read the input. The components of the format string are the same as for scanf(). Finally, the ellipses (...) indicate one or more additional arguments, the addresses of the variables where fscanf() is to assign the input. Before getting started with fscanf(), you might want to review the section on scanf(). The function fscanf() works exactly the same as scanf(), except that characters are taken from the specified stream rather than from stdin. To demonstrate fscanf(), you need a text file containing some numbers or strings in a format that can be read by the function. Use your editor to create a file named INPUT.TXT, and enter five floating-point numbers with some space between them (spaces or newlines). For example, your file might look like this: 123.45 87.001 100.02 0.00456 1.0005
Compiled by Ergin TARI

Lecture 11, Page 27 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: /* Reading formatted file data with fscanf(). */ #include <stdlib.h> #include <stdio.h> main() { float f1, f2, f3, f4, f5; FILE *fp;

123.45 87.001 100.02 0.00456 1.0005

if ( (fp = fopen("INPUT.TXT", "r")) == NULL) { fprintf(stderr, "Error opening file.\n"); exit(1); } fscanf(fp, "%f %f %f %f %f", &f1, &f2, &f3, &f4, &f5); printf("The values are %f, %f, %f, %f, and %f\n.", f1, f2, f3, f4, f5); fclose(fp); return(0); }

The values are 123.45, 87.0001, 100.02, 0.00456, and 1.0005.

Compiled by Ergin TARI

Lecture 11, Page 28

Reading and Writing Disk Files


The program in the previous slides does not do anything with the text file, haiku.txt, except open and close it. In fact, there are two pieces of Japanese haiku, written by Sodo and Chora, saved in the haiku.txt file. So how can you

read them from the file?

In C, you can perform I/O operations in the following ways:

Read or write one character at a time. Read or write one line of text (that is, one character line) at a time. Read or write one block of characters at a time.

Compiled by Ergin TARI

Lecture 11, Page 29

One Character at a Time: fgetc(), fputc() functions


The syntax for the fgetc() function is
#include <stdio.h> int fgetc(FILE *stream);

Here stream is the file pointer that is associated with a stream. The fgetc() function fetches the next character from the stream specified by stream. The function then returns the value of an int that is converted from the character. The syntax for the fputc() function is
#include <stdio.h> int fputc(int c , FILE *stream);

Here c is an int value that represents a character. In fact, the int value is converted to an unsigned char before being outputted. stream is the file pointer that is associated with a stream. The fputc() function returns the character written if the function is successful; otherwise, it returns EOF. After a character is written, the fputc() function advances the associated file pointer.
Compiled by Ergin TARI

Lecture 11, Page 30

Example:One Character at a Time: fgetc(), fputc() functions


1: /* Reading and writing one character at a time */ 2: #include <stdio.h> 4: enum {SUCCESS, FAIL}; 5: 6: void CharReadWrite(FILE *fin, FILE *fout); 7: 8: main(void) 9: { 10: FILE *fptr1, *fptr2; 11: char filename1[]= "outhaiku.txt"; 12: char filename2[]= "haiku.txt"; 13: int reval = SUCCESS; 14: 15: if ((fptr1 = fopen(filename1, "w")) == NULL){ 16: printf("Cannot open %s.\n", filename1); 17: reval = FAIL; 18: } else if ((fptr2 = fopen(filename2, "r")) == NULL){ 19: printf("Cannot open %s.\n", filename2); 20: reval = FAIL; 21: } else { 22: CharReadWrite(fptr2, fptr1); 23: fclose(fptr1); 24: fclose(fptr2); 25: } 27: return reval; 28: } 29: /* function definition */ 30: void CharReadWrite(FILE *fin, FILE *fout) 31: { 32: int c; 33: 34: while ((c=fgetc(fin)) != EOF){ 35: fputc(c, fout); /* write to a file */ 36: putchar(c); /* put the character on the screen */ 37: } 38: }

Leading me along my shadow goes back home from looking at the moon. --- Sodo (1641-1716)
A storm wind blows out from among the grasses the full moon grows. --- Chora (1729-1781)
Compiled by Ergin TARI

Output

Lecture 11, Page 31

Analysis
The purpose of the program is to read one character from a file, write the character to another file, and then display the character on the screen. (You need to copy or create the file, haiku.txt, from the book, and put it in the same directory where you save the executable file. haiku.txt is the text file that is going to be read by the program.) In the program there is a function called CharReadWrite(), which has two file pointers as its arguments. (See the declaration of the CharReadWrite() function in line 6.) The statement in line 10 defines two file pointers, fptr1 and fptr2, which are used later in the program. Lines 11 and 12 define two character arrays, filename1 and filename2, and initialize the two arrays with two strings containing filenames, outhaiku.txt and haiku.txt. In line 15, a text file with the name outhaiku.txt is opened for writing. outhaiku.txt is contained by the filename1 array. The file pointer fptr1 is associated with the file. If the fopen() function returns NULL, which means an error occurs, a warning message is printed out in line 16. Also, in line 17, the reval variable is assigned 1 and is represented by the enum name FAIL. If the file outhaiku.txt is opened successfully, another text file, called haiku.txt, is opened for reading in line 18. The file pointer fptr2 is associated with the opened text file.
Compiled by Ergin TARI

Lecture 11, Page 32

Analysis
If no error occurs, the CharReadWrite() function is invoked in line 22 with two file pointers, fptr1 and fptr2, passed to the function as arguments. From the definition of the CharReadWrite() function in lines 30 and 38, we see that there is a while loop that keeps calling the fgetc() function to read the next character from the haiku.txt text file until the function reaches the end of the file. (See line 34.) Within the while loop, the fputc() function in line 35 writes each character read from the haiku.txt file to another text file, outhaiku.txt, which is pointed to by fout. In addition, putchar() is called in line 36 in order to put the character returned by the fgetc() function on the screen. After the CharReadWrite() function finishes its job, the two opened files, which are associated with fptr1 and fptr2, are closed with a call to the fclose() function respectively in lines 23 and 24. As mentioned earlier, the haiku.txt file contains two pieces of Japanese haiku written by Sodo and Chora. If the program in Listing 21.2 is run successfully, we see the two pieces of haiku shown on the screen, and they are written into the outhaiku.txt file as well. You can view outhaiku.txt in a text editor to confirm that the content of haiku.txt has been correctly copied to outhaiku.txt.

Compiled by Ergin TARI

Lecture 11, Page 33

One Line at a Time: fgets(), fputs() functions


You can also read or write one character line at time. There is a pair of C I/O functions, fgets() and fputs(), that allows you to do so.
The syntax for the fgets() function is #include <stdio.h>

char *fgets(char *s, int n, FILE *stream);

Here s references a character array that is used to store characters read from the opened file pointed to by the file pointer stream. n specifies the maximum number of array elements. If it is successful, the fgets() function returns the char pointer s. If EOF is encountered, the fgets() function returns a null pointer and leaves the array untouched. If an error occurs, the function returns a null pointer, and the contents of the array are unknown. The fgets() function can read up to n-1 characters, and can append a null character after the last character fetched, until a newline or an EOF is encountered. Note that if a newline is encountered during the reading, the fgets() function includes the newline in the array. This is different from what the gets() function does. The gets() function just replaces the newline character with a null character.
Compiled by Ergin TARI

Lecture 11, Page 34

One Line at a Time: fgets(), fputs() functions


The syntax for the fputs() function is
#include <stdio.h> int fputs(const char *s, FILE *stream);

Here s points to the array that contains the characters to be written to a file associated with the file pointer stream. The const modifier indicates that the content of the array pointed to by s cannot be changed. If it fails, the fputs() function returns a nonzero value; otherwise, it returns zero. Note that the character array must include a null character at the end as the terminator to the fputs() function. Also, unlike the puts() function, the fputs() function does not insert a newline character to the string written to a file. We can modify the previous program to read or write one character line at a time by calling the fgets() and fputs() functions. The modified version is shown in te next slide.
Compiled by Ergin TARI

Example: One Line at a Time: fgets(), fputs() functions

Lecture 11, Page 35

1: /* Reading and writing one line at a time */ 29: /* function definition */ 2: #include <stdio.h> 30: void LineReadWrite(FILE *fin, FILE *fout) 4: enum {SUCCESS, FAIL, MAX_LEN = 81}; 31: { 5: 32: char buff[MAX_LEN]; 6: void LineReadWrite(FILE *fin, FILE *fout); 33: 7: 34: while (fgets(buff, MAX_LEN, fin) != NULL){ 8: main(void) 35: fputs(buff, fout); 9: { 36: printf("%s", buff); 10: FILE *fptr1, *fptr2; 37: } 11: char filename1[]= "outhaiku.txt"; 38: } 12: char filename2[]= "haiku.txt"; 13: int reval = SUCCESS; Leading me along 14: my shadow goes back home 15: if ((fptr1 = fopen(filename1, "w")) == NULL){ 16: printf("Cannot open %s for writing.\n", filename1); from looking at the moon. 17: reval = FAIL; --- Sodo 18: } else if ((fptr2 = fopen(filename2, "r")) == NULL){ (1641-1716) 19: printf("Cannot open %s for reading.\n", filename2); 20: reval = FAIL; A storm wind blows 21: } else { out from among the grasses 22: LineReadWrite(fptr2, fptr1); 23: fclose(fptr1); the full moon grows. 24: fclose(fptr2); --- Chora 25: } (1729-1781) Compiled by Ergin TARI 27: return reval;}

Output

Lecture 11, Page 36

Analysis
From the program, you can see that a function called LineReadWrite() has replaced the CharReadWrite() function. The definition of the LineReadWrite() function is shown in lines 30_38. The fgets() function is called repeatedly in a while loop to read one character line at a time from the haiku.txt text file, until it reaches the end of the text file. In line 34, the array name buff and the maximum number of the array elements MAX_LEN are passed to the fgets() function, along with the file pointer fin that is associated with the opened haiku.txt file. Meanwhile, each line read by the fgets() function is written to another opened text file called outhaiku.txt that is associated with the file pointer fout. This is done by invoking the fputs() function in line 35. The statement in line 36 prints the contents of each string on the screen so that you see the two pieces of Japanese verse after running the program. Also, you can view the outhaiku.txt file in a text editor to make sure that the contents of the haiku.txt file have been copied to the outhaiku.txt file.
Compiled by Ergin TARI

Lecture 11, Page 37

One Block at a Time: fread(), fwrite() functions


The fread() and fwrite() functions are mirror images of each other.
The syntax for the fread() function is #include <stdio.h> size_t fread(void *ptr, size_t size, size_t n, FILE *stream); Here ptr is a pointer to an array in which the data is stored. size indicates the size of each array element. n specifies the number of elements to read. stream is a file pointer that is associated with the opened file for reading. size_t is an integral type defined in the header file stdio.h. The fread() function returns the number of elements actually read.

The number of elements read by the fread() function should be equal to the value specified by the third argument to the function, unless an error occurs or an EOF (endof-file) is encountered. The fread() function returns the number of elements that are actually read, if an error occurs or an EOF is encountered.
Compiled by Ergin TARI

Lecture 11, Page 38

One Block at a Time: fread(), fwrite() functions


The syntax for the fwrite() function is #include <stdio.h>

size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream);


Here ptr references the array that contains the data to be written to an opened file pointed to by the file pointer stream. size indicates the size of each element in the array. n specifies the number of elements to be written. The fwrite() function returns the number of elements actually written. If there is no error occurring, the number returned by fwrite() should be the same as the third argument in the function. The return value may be less than the specified value if an error occurs. Note that it's the programmer's responsibility to ensure that the array is large enough to hold data for either the fread() function or the fwrite() function.
Compiled by Ergin TARI

Lecture 11, Page 39

Example:One Block at a Time: fread(), fwrite() functions


1: /* Reading and writing one block at a time */ 2: #include <stdio.h> 28: /* function definition */ 4: enum {SUCCESS, FAIL, MAX_LEN = 80}; 29: void BlockReadWrite(FILE *fin, FILE *fout) 5: 30: { 6: void BlockReadWrite(FILE *fin, FILE *fout); 31: int num; 7: int ErrorMsg(char *str); 32: char buff[MAX_LEN + 1]; 8: 33: 9: main(void) 34: while (!feof(fin)){ 10: { 35: num = fread(buff, sizeof(char), MAX_LEN, fin); 11: FILE *fptr1, *fptr2; 36: buff[num * sizeof(char)] = `\0'; /* append null character */ 12: char filename1[]= "outhaiku.txt"; 37: printf("%s", buff); 13: char filename2[]= "haiku.txt"; 38: fwrite(buff, sizeof(char), num, fout); 14: int reval = SUCCESS; 39: } 15: 40: } 16: if ((fptr1 = fopen(filename1, "w")) == NULL){ 17: reval = ErrorMsg(filename1); 18: } else if ((fptr2 = fopen(filename2, "r")) == NULL){ 19: reval = ErrorMsg(filename2); 41: /* function definition */ 20: } else { 42: int ErrorMsg(char *str) 21: BlockReadWrite(fptr2, fptr1); 43: { 22: fclose(fptr1); 44: printf("Cannot open %s.\n", str); 23: fclose(fptr2); 45: return FAIL; 24: } 46: } 26: return reval; 27: } Compiled by Ergin TARI

1: /* Direct file I/O with fwrite() and fread(). */ 2: #include <stdlib.h> 3: #include <stdio.h> 5: #define SIZE 20 6: 7: main() 8: { 9: int count, array1[SIZE], array2[SIZE]; 10: FILE *fp; 11: 12: /* Initialize array1[]. */ 13: 14: for (count = 0; count < SIZE; count++) 15: array1[count] = 2 * count; 16: 17: /* Open a binary mode file. */ 18: 19: if ( (fp = fopen("direct.txt", "wb")) == NULL) 20: { 21: fprintf(stderr, "Error opening file."); 22: exit(1); 23: } 24: /* Save array1[] to the file. */ 25: 26: if (fwrite(array1, sizeof(int), SIZE, fp) != SIZE) 27: { 28: fprintf(stderr, "Error writing to file."); 29: exit(1); 30: } 32: fclose(fp); 33:

Lecture 11, Page 40

34: /* Now open the same file for reading in binary mode. */ 35: 36: if ( (fp = fopen("direct.txt", "rb")) == NULL) 37: { 38: fprintf(stderr, "Error opening file."); 39: exit(1); 40: } 41: 42: /* Read the data into array2[]. */ 43: 44: if (fread(array2, sizeof(int), SIZE, fp) != SIZE) 45: { 46: fprintf(stderr, "Error reading file."); 47: exit(1); 48: } 49: 50: fclose(fp); 51: 52: /* Now display both arrays to show they're the same. */ 53: 54: for (count = 0; count < SIZE; count++) 55: printf("%d\t%d\n", array1[count], array2[count]); 56: return(0); 57: }

0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38

0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38

Compiled by Ergin TARI

Lecture 11, Page 41

Analysis
The purpose of the program is to show you how to invoke the fread() and fwrite() functions in your program to perform block I/O operations. In the program, the haiku.txt file is read by the fread() function, and then the fwrite() function is used to write the contents read from haiku.txt to another file called outhaiku.txt. We call the two C I/O functions from our own function, BlockReadWrite(). From the definition of the BlockReadWrite() function in lines 29_40, you can see that a character array called buff is defined with the number of elements of MAX_LEN + 1 in line 32, although we only read MAX_LEN number of characters by calling the fread() function in line 35. The reason is that we append a null character in line 36 after the last character read so that we ensure the block of characters saved in buff is treated as a string and can be printed out on the screen properly by the printf() function in line 37. The while loop, shown in lines 34_39, keeps calling the fread() function to read a character block with MAX_LEN elements, until the feof() function in line 34 returns 0, which means that the end of the text file has been reached. As shown in lines 35 and 38, we use the sizof operator to measure the size of the char data type because the elements in the buff array are all characters.
Compiled by Ergin TARI

Lecture 11, Page 42

Beyond the basics

Compiled by Ergin TARI

Lecture 11, Page 43

File Buffering: Closing and Flushing Files


When you're done using a file, you should close it using the fclose() function. Its prototype is int fclose(FILE *fp); The argument fp is the FILE pointer associated with the stream; fclose() returns 0 on success or -1 on error. When you close a file, the file's buffer is flushed (written to the file). You can also close all open streams except the standard ones (stdin, stdout, stdprn, stderr, and stdaux) by using the fcloseall() function. Its prototype is

int fcloseall(void);
This function also flushes any stream buffers and returns the number of streams closed. When a program terminates (either by reaching the end of main() or by executing the exit() function), all streams are automatically flushed and closed. However, it's a good idea to close streams explicitly--particularly those linked to disk files--as soon as you're finished with them. The reason has to do with stream buffers. When you create a stream linked to a disk file, a buffer is automatically created and associated with the stream. A buffer is a block of memory used for temporary storage of data being written to and read from the file. Buffers are needed because disk drives are block-oriented devices, which means that they operate most efficiently when data is read and written in blocks of a certain size. The size of the ideal block differs, depending on the specific hardware in use. It's typically on the order of a few hundred to a thousand bytes. You don't need to be concerned about the exact block size, however.
Compiled by Ergin TARI

Lecture 11, Page 44

File Buffering: Closing and Flushing Files


The buffer associated with a file stream serves as an interface between the stream (which is character-oriented) and the disk hardware (which is block-oriented). As your program writes data to the stream, the data is saved in the buffer until the buffer is full, and then the entire contents of the buffer are written, as a block, to the disk. An analogous process occurs when reading data from a disk file. The creation and operation of the buffer are handled by the operating system and are entirely automatic; you don't have to be concerned with them. (C does offer some functions for buffer manipulation) In practical terms, this buffer operation means that, during program execution, data that your program wrote to the disk might still be in the buffer, not on the disk. If your program hangs up, if there's a power failure, or if some other problem occurs, the data that's still in the buffer might be lost, and you won't know what's contained in the disk file. You can flush a stream's buffers without closing it by using the fflush() or flushall() library functions. Use fflush() when you want a file's buffer to be written to disk while still using the file. Use flushall() to flush the buffers of all open streams. The prototypes of these two functions are as follows: int fflush(FILE *fp); int flushall(void); The argument fp is the FILE pointer returned by fopen() when the file was opened. If a file was opened for writing, fflush() writes its buffer to disk. If the file was opened for reading, the buffer is cleared. The function fflush() returns 0 on success or EOF if an error occurred. The function flushall() returns the number of open streams.

Compiled by Ergin TARI

Lecture 11, Page 45

Sequential Versus Random File Access


Every open file has a file position indicator associated with it. The position indicator specifies where read and write operations take place in the file. The position is always given in terms of bytes from the beginning of the file. When a new file is opened, the position indicator is always at the beginning of the file, position 0. (Because the file is new and has a length of 0, there's no other location to indicate.) When an existing file is opened, the position indicator is at the end of the file if the file was opened in append mode, or at the

beginning of the file if the file was opened in any other mode.
The file input/output functions covered earlier make use of the position indicator, although the manipulations go on behind the scenes. Writing and reading operations occur at the location of the position indicator and update the position indicator as well. For example, if you open a file for reading, and 10 bytes are read, you input the first 10 bytes in the file (the bytes at positions 0 through 9). After the read operation, the position indicator is at position 10, and the next read operation begins there. Thus, if you want to read all the data in a file sequentially or write data to a file sequentially, you don't need to be concerned about the position indicator, because the stream I/O functions take care of it automatically. When you need more control, use the C library functions that let you determine and change the value of the file position indicator. By controlling the position indicator, you can perform random file access. Here, random means that you can read data from, or write data to, any position in a file without reading or writing all the preceding data.

Compiled by Ergin TARI

Lecture 11, Page 46

The ftell() and rewind() Functions


To set the position indicator to the beginning of the file, use the library function rewind(). Its
prototype, in STDIO.H, is void rewind(FILE *fp); The argument fp is the FILE pointer associated with the stream. After rewind() is called, the file's position indicator is set to the beginning of the file (byte 0). Use rewind() if you've read some data from a file and you want to start reading from the beginning of the file again without closing and reopening the file.

To determine the value of a file's position indicator, use ftell(). This function's prototype,
located in STDIO.H, reads long ftell(FILE *fp); The argument fp is the FILE pointer returned by fopen() when the file was opened. The function ftell() returns a type long that gives the current file position in bytes from the start of the file (the first byte is at position 0). If an error occurs, ftell() returns -1L (a type long -1).

Compiled by Ergin TARI

1: /* Demonstrates ftell() and rewind(). */ 2: #include <stdlib.h> 3: #include <stdio.h> 4: 5: #define BUFLEN 6 6: 7: char msg[] = "abcdefghijklmnopqrstuvwxyz"; 8: 9: main() 10: { 11: FILE *fp; 12: char buf[BUFLEN]; 13: 14: if ( (fp = fopen("TEXT.TXT", "w")) == NULL) 15: { 16: fprintf(stderr, "Error opening file."); 17: exit(1); 18: } 19: 20: if (fputs(msg, fp) == EOF) 21: { 22: fprintf(stderr, "Error writing to file."); 23: exit(1); 24: } 25: 26: fclose(fp); 27:
Immediately after opening, position = 0 After reading in abcde, position = 5 The next 5 characters are fghij, and position now = 10 After rewinding, the position is back at 0 and reading starts at the beginning again: abcde

Lecture 11, Page 47 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: } /* Now open the file for reading. */

if ( (fp = fopen("TEXT.TXT", "r")) == NULL) { fprintf(stderr, "Error opening file."); exit(1); } printf("\nImmediately after opening, position = %ld", ftell(fp)); /* Read in 5 characters. */
fgets(buf, BUFLEN, fp); printf("\nAfter reading in %s, position = %ld", buf, ftell(fp)); /* Read in the next 5 characters. */

fgets(buf, BUFLEN, fp); printf("\n\nThe next 5 characters are %s, and position now = %ld", buf, ftell(fp));
/* Rewind the stream. */ rewind(fp); printf("\n\nAfter rewinding, the position is back at %ld", ftell(fp)); /* Read in 5 characters. */ fgets(buf, BUFLEN, fp); printf("\nand reading starts at the beginning again: %s\n", buf); fclose(fp); return(0);
Compiled by Ergin TARI

Lecture 11, Page 48

The fseek() Function


More precise control over a stream's position indicator is possible with the fseek() library function. By using fseek(), you can set the position indicator anywhere in the file. The function prototype, in STDIO.H, is int fseek(FILE *fp, long offset, int origin);
The argument fp is the FILE pointer associated with the file. The distance that the position indicator is to be moved is given by offset in bytes. The argument origin specifies the move's relative starting point. There can be three values for origin, with symbolic constants defined in IO.H, as shown in the following table.

Constant SEEK_SET

Value 0

Description Moves the indicator offset bytes from the beginning of the file.

SEEK_CUR
SEEK_END

1
2

Moves the indicator offset bytes from its current position.


Moves the indicator offset bytes from the end of the file.

The function fseek() returns 0 if the indicator was successfully moved or nonzero if an error occurred.
Compiled by Ergin TARI

Lecture 11, Page 49

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:

/* Random access with fseek(). */


#include <stdlib.h> #include <stdio.h> #define MAX 50

main() { FILE *fp; int data, count, array[MAX]; long offset;


/* Initialize the array. */ for (count = 0; count < MAX; count++) array[count] = count * 10; /* Open a binary file for writing. */ if ( (fp = fopen("RANDOM.DAT", "wb")) == NULL) { fprintf(stderr, "\nError opening file."); exit(1); } /* Write the array to the file, then close it. */ if ( (fwrite(array, sizeof(int), MAX, fp)) != MAX) { fprintf(stderr, "\nError writing data to file."); exit(1); }
Compiled by Ergin TARI

35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: }

fclose(fp); /* Open the file for reading. */ if ( (fp = fopen("RANDOM.DAT", "rb")) == NULL) { fprintf(stderr, "\nError opening file."); exit(1); }
/* Ask user which element to read. Input the element */ /* and display it, quitting when -1 is entered. */

while (1) { printf("\nEnter element to read, 0-%d, -1 to quit: ",MAX-1); scanf("%ld", &offset); if (offset < 0) break; else if (offset > MAX-1) continue; /* Move the position indicator to the specified element. */ if ( (fseek(fp, (offset*sizeof(int)), SEEK_SET)) != 0) { fprintf(stderr, "\nError using fseek()."); exit(1); } /* Read in a single integer. */ fread(&data, sizeof(int), 1, fp); } printf("\nElement %ld has value %d.", offset, data);

Lecture 11, Enter element to read, 0-49, -1 to quit: 5 Page 50 Element 5 has value 50. Enter element to read, 0-49, -1 to quit: 6 Element 6 has value 60. Enter element to read, 0-49, -1 to quit: 49 Element 49 has value 490. Enter element to read, 0-49, -1 to quit: 1 Element 1 has value 10. Enter element to read, 0-49, -1 to quit: 0 Element 0 has value 0. Enter element to read, 0-49, -1 to quit: -1

fclose(fp); return(0);
Compiled by Ergin TARI

Lecture 11, Page 51

Deleting a File
To delete a file, you use the library function remove(). Its prototype is in STDIO.H,

as follows:
int remove( const char *filename ); The variable *filename is a pointer to the name of the file to be deleted. (See the

section on filenames earlier in this week.) The specified file must not be open. If the
file exists, it is deleted (just as if you used the DEL command from the DOS prompt or the rm command in UNIX), and remove() returns 0. If the file doesn't exist, if it's read-only, if you don't have sufficient access rights, or if some other error occurs, remove() returns -1.

Compiled by Ergin TARI

Lecture 11, Page 52

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:

/* Demonstrates the remove() function. */ #include <stdio.h> main() { char filename[80]; printf("Enter the filename to delete: "); gets(filename); if ( remove(filename) == 0) printf("The file %s has been deleted.\n", filename); else fprintf(stderr, "Error deleting the file %s.\n", filename); return(0); }

Enter the filename to delete: *.bak Error deleting the file *.bak. Enter the filename to delete: list1414.bak The file list1414.bak has been deleted.

Compiled by Ergin TARI

Lecture 11, Page 53

Renaming a File
The rename() function changes the name of an existing disk file. The function prototype, in STDIO.H, is as follows: int rename( const char *oldname, const char *newname );

The filenames pointed to by oldname and newname follow the rules given earlier
in this chapter. The only restriction is that both names must refer to the same disk drive; you can't rename a file to a different disk drive. The function rename() returns 0 on success, or -1 if an error occurs. Errors can be caused by the following conditions (among others): The file oldname does not exist. A file with the name newname already exists. You try to rename to another disk.

Compiled by Ergin TARI

Lecture 11, Page 54

: /* Using rename() to change a filename. */ 2: 3: #include <stdio.h> 4: 5: main() 6: { 7: char oldname[80], newname[80]; Enter current filename: list1609.c 8: Enter new name for file: rename.c 9: printf("Enter current filename: "); list1609.c has been renamed rename.c. 10: gets(oldname); 11: printf("Enter new name for file: "); 12: gets(newname); 13: 14: if ( rename( oldname, newname ) == 0 ) 15: printf("%s has been renamed %s.\n", oldname, newname); 16: else 17: fprintf(stderr, "An error has occurred renaming %s.\n", oldname); 18: return(0); 19: }

Compiled by Ergin TARI

Lecture 11, Page 55

Copying a File
It's frequently necessary to make a copy of a file--an exact duplicate with a different name (or with the same name but in a different drive or directory). In DOS, you do this with the COPY command, and other operating systems have equivalents. How do you copy a file in C? There's no library function, so you need to write your own. This might sound a bit complicated, but it's really quite simple thanks to C's use of streams for input and output. Here are the steps you follow:
1. Open the source file for reading in binary mode. (Using binary mode ensures that the function can copy all sorts of files, not just text files.)

2. Open the destination file for writing in binary mode.


3. Read a character from the source file. Remember, when a file is first opened, the pointer is at the start of the file, so there's no need to position the file pointer explicitly.

4. If the function feof() indicates that you've reached the end of the source file, you're done and can close both files and return to the calling program.
5. If you haven't reached end-of-file, write the character to the destination file, and then loop back to step 3.

Compiled by Ergin TARI

Lecture 11, Page 56

1: /* Copying a file. */ 2: 3: #include <stdio.h> 4: 5: int file_copy( char *oldname, char *newname ); 6: 7: main() 8: { 9: char source[80], destination[80]; 10: 11: /* Get the source and destination names. */ 12: 13: printf("\nEnter source file: "); 14: gets(source); 15: printf("\nEnter destination file: "); 16: gets(destination); 17: 18: if ( file_copy( source, destination ) == 0 ) 19: puts("Copy operation successful"); 20: else 21: fprintf(stderr, "Error during copy operation"); 22: return(0); 23: } 24: int file_copy( char *oldname, char *newname ) 25: { 26: FILE *fold, *fnew; 27: int c; 28: 29: /* Open the source file for reading in binary mode. */ 30: 31: if ( ( fold = fopen( oldname, "rb" ) ) == NULL ) 32: return -1; 33:

34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: }

/* Open the destination file for writing in binary mode. */

if ( ( fnew = fopen( newname, "wb" ) ) == NULL ) { fclose ( fold ); return -1; } /* Read one byte at a time from the source; if end of file */ /* has not been reached, write the byte to the */ /* destination. */
while (1) { c = fgetc( fold ); if ( !feof( fold ) ) fputc( c, fnew ); else break; } fclose ( fnew ); fclose ( fold ); return 0;

Enter source file: list1610.c Enter destination file: tmpfile.c Copy operation successful
Compiled by Ergin TARI

Lecture 11, Page 57

Using Temporary Files


Some programs make use of one or more temporary files during execution. A

temporary file is a file that is created by the program, used for some purpose during
program execution, and then deleted before the program terminates. When you create a temporary file, you don't really care what its name is, because it gets

deleted. All that is necessary is that you use a name that isn't already in use for
another file. The C standard library includes a function tmpnam() that creates a valid filename that doesn't conflict with any existing file. Its prototype in STDIO.H is as follows: char *tmpnam(char *s); The arguments must be a pointer to a buffer large enough to hold the filename. You can also pass a null pointer (NULL), in which case the temporary name is stored in a buffer internal to tmpnam(), and the function returns a pointer to that buffer.

Compiled by Ergin TARI

Lecture 11, Page 58

1: /* Demonstration of temporary filenames. */ 2: 3: #include <stdio.h> 4: 5: main() 6: { 7: char buffer[10], *c; 8: 9: /* Get a temporary name in the defined buffer. */ 10: 11: tmpnam(buffer); 12: 13: /* Get another name, this time in the function's */ 14: /* internal buffer. */ 15: 16: c = tmpnam(NULL); 17: 18: /* Display the names. */ 19: 20: printf("Temporary name 1: %s", buffer); 21: printf("\nTemporary name 2: %s\n", c); 22: }

Temporary name 1: TMP1.$$$ Temporary name 2: TMP2.$$$

Compiled by Ergin TARI

Summary

Lecture 11, Page 59

In C, a file can refer to a disk file, a terminal, a printer, or a tape drive. The data flow you transfer from your program to a file, or vice versa, is called a stream. A stream is a series of ordered bytes. Not like a file, a stream is device-independent. There are two stream formats: text stream and binary stream. The file position indicator in the FILE structure points to the position in a file where data will be read from or written to. The fopen() function is used to open a file and associate a stream to the opened file. You can specify different modes for opening a file. The fclose() function is responsible for closing an opened file and disassociating a stream with the file. The fgetc() and fputc() functions read or write one character at a time. The fgets() and fputs() functions read or write one line at a time. The fread() and fwrite() functions read or write one block of data at a time. The feof() function can determine when the end of a file has been reached. In a binary file, the feof() function should be used to detect EOF.

Compiled by Ergin TARI

Вам также может понравиться