File I/O in C Lecture 7 Narrator: Lecture 7: File I/O in C. Winter Quarter
Files in C In C, each file is simply a sequential stream of bytes. C imposes no structure on a file. A file must first be opened properly before it can be accessed for reading or writing. When a file is opened, a stream is associated with the file. Successfully opening a file returns a pointer to (i.e., the address of) a file structure, which contains a file descriptor and a file control block. Instructor: Much like the keyboard and screen, file input and output is completed via streams. The difference is the computer system typically contains many files, so the programmer must tell the computer which file to connect the stream to. Narrator: Much like the keyboard and screen, file input and output is completed via streams. The difference is the computer system typically contains many files, so the programmer must tell the computer which file to connect the stream to by opening the file. Once a file is open, a pointer to the address of that file structure is established. This file structure contains a file descriptor and file control block. Winter Quarter
Files in C The statement: FILE *fptr1, *fptr2 ; declares that fptr1 and fptr2 are pointer variables of type FILE. They will be assigned the address of a file descriptor, that is, an area of memory that will be associated with an input or output stream. Whenever you are to read from or write to the file, you must first open the file and assign the address of its file descriptor (or structure) to the file pointer variable. Instructor: The FILE declaration is included in the same place as int, float, and char declarations. Since FILE variables are pointers the must be preceded by a * so that the computer knows that the variable will actually contain an address (which will later point to the location of the file itself). Declaring the variables themselves does not connect a file to them. Narrator: File pointers, like any other variable, must be declared. The statement shown declares two file pointers. They can now be assigned the address of a file descriptor, or the location in memory that will be associated with the input or output file stream. Whenever you read from or write to a file, you must first open the file and assign the address of its file descriptor to a file pointer variable. The FILE declaration is included in the same area of your program as int, float or char variable declarations. Notice these FILE pointer variables are preceded by a * so that the computer knows that the variable will contain an address. Declaring the variables themselves does not associate a file descriptor with them. Winter Quarter
fptr1 = fopen ( "mydata", "r" ) ; fptr2 = fopen ("results", "w" ) ; Opening Files The statement: fptr1 = fopen ( "mydata", "r" ) ; would open the file mydata for input (reading). fptr2 = fopen ("results", "w" ) ; would open the file results for output (writing). Once the files are open, they stay open until you close them or end the program (which will close all files.) Instructor: Simply put, the fopen command connects the given file and creates either an input or output stream to the file. The file pointer that can then be used as a stream is on the left hand side of the = sign. Narrator: Now that file pointers are declared, a file descriptor can now be assigned by opening a file. This is completed by using the fopen() command. Notice in the commands shown the file pointer is on the left hand side of the assignment and the command fopen() opens a given file by its name. The second argument of fopen() determines how to open the file, in this case either for reading or writing. Once these files have been opened, they stay open until you close them, or when your program terminates, which will automatically close all files. Winter Quarter
Testing for Successful Open If the file was not able to be opened, then the value returned by the fopen routine is NULL. For example, let's assume that the file mydata does not exist. Then: FILE *fptr1 ; fptr1 = fopen ( "mydata", "r") ; if (fptr1 == NULL) { printf ("File 'mydata' did not open.\n") ; } Instructor: A good way to check for fault tolerance is to check for the file pointer to be equal to NULL after attempting to open it. If the file pointer is NULL the program should perform some alternate action besides attempting to use the file pointer (such as displaying an for the user). Narrator: Just because you attempt to open a file doesn’t guarantee it will be opened successfully. Various problems may occur such as the file is in use, your program doesn’t have permission to open the file, or the file simply doesn’t exist or is in another directory. But there is a method of testing for a successful file open. In the given example we will attempt to open a file that doesn’t exist. Notice after we attempt to open the file with fopen we then test the file pointer for a value called NULL. If the file pointer contains NULL then the file in fact didn’t open. Otherwise it can be assumed the file opened correctly and is ready for use. Winter Quarter
Reading From Files In the following segment of C language code: int a, b ; FILE *fptr1, *fptr2 ; fptr1 = fopen ( "mydata", "r" ) ; fscanf ( fptr1, "%d%d", &a, &b) ; the fscanf function would read values from the file "pointed" to by fptr1 and assign those values to a and b. Instructor: In the line: fscanf(fptr1,”%d%d”,&a,&b); operates much like the scanf function except it requires the name of the file pointer it should acquire data from. fscanf – the function called Fptr1 – the file pointer fscanf will acquire data from “%d%d” – the expected format of the data in the file (2 integers in this case) &a,&b – the variables where data from the file will be stored Narrator: Once a file is open for reading, there must be a method of reading from the file. This is completed with the fscanf function. It operates much like the scanf function, except this function requires the file pointer to be included as an argument. In the segment of code shown the file mydata is opened and the file pointer fptr1 is assigned to the open file. Then the fscanf function scans for two integers from this file and assigns the values read to the variables a and b. Winter Quarter
End of File The end-of-file indicator informs the program when there are no more data (no more bytes) to be processed. There are a number of ways to test for the end-of-file condition. One is to use the feof function which returns a true or false condition: fscanf (fptr1, "%d", &var) ; if ( feof (fptr1) ) { printf ("End-of-file encountered.\n) ; } Instructor: Or to complete a section of code while not at the end of a file: Fscanf(…); While(!feof(fptr1)) { //do something with file data fscanf(…); } Narrator: When reading continuously from a file, eventually the program will reach the end of a file. To determine when this occurs another indicator like the NULL indicator is used, called the end-of-file indictor, or EOF. The pointer will return this indicator when no more data is available to be processed. There are multiple ways to test for the end-of-file condition. A simple method is to use the feof function, which will return a true or false condition. Its use is shown in the example in an if statement. Feof() requires the file pointer being tested as an argument. Winter Quarter
End of File There are a number of ways to test for the end-of-file condition. Another way is to use the value returned by the fscanf function: int istatus ; istatus = fscanf (fptr1, "%d", &var) ; if ( istatus == EOF ) { printf ("End-of-file encountered.\n) ; } Narrator: Another way to test for EOF, much like when testing for NULL, is to check the value returned from fscanf for the EOF indicator. Note that when this method is used you must explicitly assign the output of fscanf to a variable where normally the output of fscanf is ignored. Look closely at the fscanf line on this slide and the preceding slide for this subtle difference. Winter Quarter
Writing To Files Likewise in a similar way, in the following segment of C language code: int a = 5, b = 20; FILE *fptr2 ; fptr2 = fopen ( "results", "w" ) ; fprintf ( fptr2, "%d %d\n", a, b ) ; the fprintf functions would write the values stored in a and b to the file "pointed" to by fptr2. Instructor: Point out that the syntax and operation of fprintf is identical to printf, except the first variable into fprintf is the file pointer it will print to. Also the file must be open for writing. Narrator: Now that reading from files has been covered, it is useful to be able to also write to files. The fprintf command, much like printf, allows the programmer to write data to a file. Its format is nearly identical to printf, except the first argument must now be the file pointer of the open file the program should write to. In the given example the fprintf writes to the file assigned to fptr2, which has been opened in write mode. The values of the variables a and b are written to the file, with a new line appended after this data. Winter Quarter
Closing Files The statements: fclose ( fptr1 ) ; fclose ( fptr2 ) ; fclose ( fptr1 ) ; fclose ( fptr2 ) ; will close the files and release the file descriptor space and I/O buffer memory. Instructor: Once a file pointer is closed it can be used to open another file. Narrator: A method exists for closing files besides allowing the program to terminate. The fclose statement properly closes the file pointed to by the pointer included as an argument. This pointer is then freed for use again in another fopen command. This is especially useful in a program that may run for a long time and open a large number of files, so file pointers can be re-used and not fill memory with unnecessary information. Winter Quarter
Reading and Writing Files #include <stdio.h> void main ( ) { FILE *outfile, *infile ; int b = 5, f ; float a = 13.72, c = 6.68, e, g ; outfile = fopen ("testdata", "w") ; fprintf (outfile, "%6.2f%2d%5.2f", a, b, c) ; fclose (outfile) ; Instructor/Narrator: The next two slides contain a full program with file input and output and the output of the program at the end of the next slide. This program also introduces additional formatting of the fprintf string. When formatting data a certain number of spaces on the screen can be reserved and the precision of decimal number displayed can be chosen. This is best described by an example: %6.2f reserves 6 total spaces on the screen (including a decimal point for floats) and displays 2 decimal points of precision. In the case data that doesn’t fit (such as the number 9.1) zeros are appended to fill the number of decimal places and spaces added on the left to fill the number of total characters (so this would appear as “ 9.10”). In the case of integer numbers, the number of characters reserved is the only option available (such as the %2d in the given example). Winter Quarter
Reading and Writing Files infile = fopen ("testdata", "r") ; fscanf (infile,"%f %d %f", &e, &f, &g) ; printf ("%6.2f%2d%5.2f\n", a, b, c) ; printf ("%6.2f,%2d,%5.2f\n", e, f, g) ; } 12345678901234567890 **************************** 13.72 5 6.68 13.72, 5, 6.68 Instructor/Narrator: This program writes some pre-determined values to a file, then reads them from the file and displays them on the screen. Take some time and look over each line of the program and make sure you understand what each is doing. The original values are displayed without commas, and the data read from the file is displayed on the second line with commas as separators. Notice the same spacing of each value occurs, and the commas occur as static characters (which extend the second line further). Winter Quarter
A Little Help for Assignment E6 Assignment E6 requires us to read several lines of some information from a disk file and write it out to the screen and to a file. (Sounds repetitive, doesn't it?) Simple repetitive tasks are easily performed with a "for" loop. We'll talk more about for loops very soon. Today, use something like the following: for ( i = 1 ; i <= n ; i++ ) { /* put statements to be repeated here */ } Instructor: Since loops haven’t been described yet, take some time to explain how a for loop can be used to repeat a set of statements a certain number of times. Narrator: Since we haven’t covered repetition yet, here is some help with assignment E6. This assignment requires us to read several lines of information from a disk file and write it out to the screen. A good method of doing this is use the same command for each line read (instead of writing a line in the program for every line read in). One way of doing repetitive things is with a for loop. Loops will be covered real soon, but for now use the code in red shown here. Also remember to declare the variable “i” used in this loop as an integer. Winter Quarter