So far all programs we have created have keep the data in memory (RAM) so far the program is running. After the program have stopped, all data have disappeared. In this section we learn how to store data permanently in computer physical memory.
This chapter covers how C programmers can create, open, close text or binary files for their data storage.
A file represents a sequence of bytes, regardless of it being a text file or a binary file. C programming language provides access on high level functions as well as low level (OS level) calls to handle file on your storage devices. This chapter will take you through the important calls for file management.
Opening Files
You can use the fopen( ) function to create a new file or to open an existing file. This call will initialize an object of the type FILE, which contains all the information necessary to control the stream. The prototype of this function call is as follows:
FILE *fopen( const char * filename, const char * mode );
fopen returns a stream and NULL if the attempt fails. Here, filename is a string literal, which you will use to name your file, and access mode can have one of the following values:
Sr.No. | Mode & Description |
---|---|
1 | r
Opens an existing text file for reading purpose. |
2 | w
Opens a text file for writing. If it does not exist, then a new file is created. Here your program will start writing content from the beginning of the file. |
3 | a
Opens a text file for writing in appending mode. If it does not exist, then a new file is created. Here your program will start appending content in the existing file content. |
4 | r+
Opens a text file for both reading and writing. |
5 | w+
Opens a text file for both reading and writing. It first truncates the file to zero length if it exists, otherwise creates a file if it does not exist. |
6 | a+
Opens a text file for both reading and writing. It creates the file if it does not exist. The reading will start from the beginning but writing can only be appended. |
If you are going to handle binary files, then you will use following access modes instead of the above mentioned ones :
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
Closing a File
To close a file, use the fclose( ) function. The prototype of this function is:
int fclose( FILE *fp );
The fclose(-) function returns zero on success, or EOF if there is an error in closing the file. This function actually flushes any data still pending in the buffer to the file, closes the file, and releases any memory used for the file. The EOF is a constant defined in the header file stdio.h.
There are various functions provided by C standard library to read and write a file, character by character, or in the form of a fixed length string.
Writing a File
Following is the simplest function to write individual characters to a stream:
int fputc( int c, FILE *fp );
The function fputc() writes the character value of the argument c to the output stream referenced by fp. It returns the written character written on success otherwise EOF if there is an error. You can use the following functions to write a null-terminated string to a stream:
int fputs( const char *s, FILE *fp );
The function fputs() writes the string s to the output stream referenced by fp. It returns a non-negative value on success, otherwise EOF is returned in case of any error. You can use int fprintf(FILE *fp,const char *format, …) function as well to write a string into a file. Try the following example.
#include <stdio.h> main() { FILE *fp; fp = fopen("test.txt", "w+"); fprintf(fp, "This is testing for fprintf...\n"); fputs("This is testing for fputs...\n", fp); fclose(fp); }
Go to the directory where you have run the program and open the file and see if there is something.
Reading a File
Given below is the simplest function to read a single character from a file −
int fgetc( FILE * fp );
The fgetc() function reads a character from the input file referenced by fp. The return value is the character read, or in case of any error, it returns EOF. The following function allows to read a string from a stream −
char *fgets( char *buf, int n, FILE *fp );
The functions fgets() reads up to n-1 characters from the input stream referenced by fp. It copies the read string into the buffer buf, appending a null character to terminate the string.
If this function encounters a newline character ‘\n’ or the end of the file EOF before they have read the maximum number of characters, then it returns only the characters read up to that point including the new line character. You can also use int fscanf(FILE *fp, const char *format, …) function to read strings from a file, but it stops reading after encountering the first space character.
#include <stdio.h> main() { FILE *fp; char buff[255]; fp = fopen("test.txt", "r"); fscanf(fp, "%s", buff); printf("1 : %s\n", buff ); fgets(buff, 255, (FILE*)fp); printf("2: %s\n", buff ); fgets(buff, 255, (FILE*)fp); printf("3: %s\n", buff ); fclose(fp); }
When the above code is compiled and executed, it reads the file created in the previous section and produces the following result:
1 : This 2: is testing for fprintf... 3: This is testing for fputs...
Let’s see a little more in detail about what happened here. First, fscanf() read just This because after that, it encountered a space, second call is for fgets() which reads the remaining line till it encountered end of line. Finally, the last call fgets() reads the second line completely.
More useful functions:
getchar() gets a character and getc(fp) also (returns EOF on error)
putchar() prints a character and putc(fp, stdout) also (returns EOF on error)
gets() gets a line (returns EOF on error)
puts() prints a line (returns EOF in error)
ftell(fp) returns the current fileposition for stream, or -1L on error.
fseek(fp, variable_name, x), where x is 0 (beginning), 1(current position), 2(end of file). Sets the file position for stream; a subsequent read or write will access data beginning at the new position. Returns non-zero on error.
feof(fp) returns non-zero if the error indicator for stream is set
scanf(“%d, &variablename) or fscanf(fp, “%d, &variablename) needs a pointer
printf(“%d”, variablename) or fprintf(fp, “%d”, variablename) needs a pointer
strlen() finds the length of the string
strcmp() compares two strings
strcpy() copies a string
strcat() combines two strings
Find more functions here, where you can find C Standard Library Reference Tutorial.
Lets write the program which can create and open the file for reading, and writing.
/* filehandler: a program that reads and writes the file */ #include <stdio.h> void Openandwrite(); void Read(); int main() { int selection; do { printf("\nMenu: ");/* draws a menu for the user */ printf("\n1.Create the new file for writing"); printf("\n2.Open the existing file for reading"); printf("\n0.Exit the program"); printf("\n \nPlease give your choice: \n?"); scanf("%d", &selection); switch(selection) case 1: Openandwrite(); break; case 2: Read(); break; case 0: printf("\nYou choosed the exit, Goodbye!"); break; default: printf("\nPlease,select the right choice (0, 1, 2)"); break; } while (selection != 0); void Openandwrite()/*Creates new file for writing*/ { FILE *fp; fp = fopen("test.txt", "w+"); fprintf(fp, "This is testing for fprintf...\n"); fputs("This is testing for fputs...\n", fp); fclose(fp); } void read();/*Opens file for reading*/ { FILE *fp; char buff[255]; fp = fopen("test.txt", "r"); fscanf(fp, "%s", buff); printf("1 : %s\n", buff ); fgets(buff, 255, (FILE*)fp); printf("2: %s\n", buff ); fgets(buff, 255, (FILE*)fp); printf("3: %s\n", buff ); fclose(fp); }
Error handling when opening the file:
If((fp= fopen("filename", r)) ==0)/* fopen returns 0, if it cannot open the file*/ printf("\nCannot open the file) else
Error handling (when searching the file end)
while(! feof(fp)) { … }
Next program is an example of binary file. It is a binary file, because the age is type of int. User enters the needed information from the keyboard and program writes them in file. Program deals one record in time.
#define RECORD "NAMES.BIN" #define NAME_MAX 20 struct person{ char name[NAME_MAX+1]; int age; }; FILE *f; void main() { struct person h; char answer; char line[81]; f = fopen (RECORD, "wb"); if (f==NULL) { printf ("\nOpening output-file "); printf ("\nDoesn't open!.\n"); } else { do{ printf ("\nEnter the name (max %d characters):", NAME_MAX); gets(h.name); printf ("\nEnter the age: "); gets(line); sscanf(line,"%d",&h.age); fwrite(&h, sizeof h,1,f); printf ("More names (y/n)?"); /*scanf("%s", &answer);*/ answer = getchar(); getchar(); }while (answer=='y'); fclose(f); f = fopen(RECORD, "rb"); if (f==NULL) { printf ("\nOpening input-file "); printf ("\nDoesn't open!.\n"); } else { fread (&h,sizeof h,1,f); while (!feof(f)) { printf ("%s %d\n",h.name,h.age); fread(&h,sizeof h,1,f); } fclose(f); } } }
Next program copies file to an another file and includes also error detection if something goes wrong. If one of the files can’t be accessed for some reason, the diagnostic is printed at the end of the concatenated output. That might be acceptable if the output is going to a screen, but not if it’s going into a file or into another program via pipeline.
#include <stdio.h> /*Cat: write it's error messages on the standard error*/ main(int argc, char *argv[]) { FILE *fp; void filecopy(FILE *, FILE *); char *prog = argv[0];/*program name for errors*/ if(argc==1)/*no args; copy standard input*/ filecopy(stdin, stdout); else while(--argc > 0) if((fp = fopen(*++argv, "r")) == NULL) { fprintf(stderr, "%s: cant open %s\n", prog, *argv); exit(1); } else { filecopy(fp, stdout); fclose(fp); } if(ferror(stdout)) { fprintf(stderr, "%s error wrriting stdou\n", prog); exit(2); } exit(0); } /*filecopy: copy file ifp to file opf*/ void filecopy(FILE *ifp, FILE *ofp) { int c; while((c = getc(ifp)) != EOF) putc(c, ofp); } Usage of this program: type cat test.txt in command prompt(You must have a file test.txt)
Last thing to know with data is how to update or remove existing data or add data into file and how to prevent the program not to overwrite existing data (if that is not what we want).
Function fseek(FILE *stream, long offset, int origin) will help us in that topic. It needs 3 arguments: 1. pointer; 2. for a binary file , the position is set to offset characters from origin, which may be SEEK_SET (beginning), SEEK_CUR ( current position) , or SEEK_END ( end of file), for a text stream, 3. offset must be a zero, or a value returned by ftell (in which case origin must be SEEK_SET).
/*Seek: Usage of fseek to write and print the file*/ #include <stdio.h> main(number, names) int number; char *names[]; { FILE *fp; long offset = 0L; if(number < 2) puts("I need a filename for argument\n"); else { if ((fp = fopen(names[1], "r")) == 0) printf("Cannot open! %s.\n", names[1]); else { while(fseek(fp, offset++,0) == 0) putchar(getc(fp)); fclose(fp); } } } Usage: Seek test.txt. Test what happens if you enter only Seek, after that Seek test.
Next example shows how to move forwards and backwards in file.
/*Fseek: Usage of fseek */ #include main(number,names) int number; char *names[]; { FILE *fp; long offset = 0L; int charac; if(number < 2) puts("I need a filename for argument\n"); else { if ((fp = fopen(names[1], "r")) == 0 ) printf("Cannot open! %s.\n", names[1]); else { while((fseek(fp, offset++,0) == 0) && (charac = getc(fp)) != EOF) { putchar(charac); if(fseek(fp, -offset, 2) ==0) putchar(getc(fp)); } fclose(fp); } } } Usage: type Fseek test.txt in command prompt, write in that text file for example Malvolio and see what happens.
NOTE! This chapter exercises can be found in quiz exercises to chapter 8 This information will be tested in quizzes 7-9! It is very important that the student will make all examples shown in this chapter.