ASIC Verification: C File Handling

Thursday, February 28, 2008

C File Handling

C communicates with files using a new data type called a "file pointer". This type is defined within the library stdio.h, and written as FILE *. A file pointer called output_file is declared in a statement like

FILE *fp;

Your program must open a file before it can access it. This is done using the "fopen()" function, which returns the required file pointer. If the file cannot be opened for any reason then the value NULL will be returned. You will usually use "fopen" as follows

if ((fp = fopen("name of the file to open", "w")) == NULL)
fprintf(stderr, "Can't open %s\n", "fp");

fopen takes two arguments, both are strings, the first is the name of the file to be opened, the second is an access character, which is usually one of:

"r" or "w" or "a" or "r+" or "w+" or "a+". we will discuss these modes in detail in a later section.

Now let us take a simple program that will read a file and count how many characters, spaces, tabs and newlines are present in that file.
====================================================================================
main()
{
FILE *fp; /* file pointer fp*/
char ch;
int number_of_lines=0, number_of_tabs=0,
number_of_blanks=0, number_of_char=0;

fp=fopen("ex.c", "r");

while(1)
{
ch = fgetc(fp);
if(ch == EOF) break;
number_of_char++;

if(ch == ' ') number_of_blanks++;
if(ch == '\n') number_of_lines++;
if(ch == '\t') number_of_tabs++;
}
fclose(fp);
printf("Number of characters in the file = %d\n", number_of_char);
}
====================================================================================

Opening a file establishes a link b/w a program and OS about which file we are going to open and how. We provide the OS the name of the file and whether we plan to read or write to it. The link b/w our program and OS is a structure called FILE which has been defined in the header file called stdio.h. When we request the OS to open a file, what we get back is a pointer to the structure FILE. Thats why we make the following declaration before opening the file,

FILE *fp;

The FILE structure contains information about the file being used, such as its current size, its location in memory and a character pointer which points to the character that is about to get read. Now lets understand the following statements:

FILE *fp;
/* fp is a file pointer which contains address of the structure FILE*/
fp=fopen("ex.c", "r");

fopen() performs 3 tasks:

  • It searches on the disk the file to be opened.
  • If the file is presents, it loads the file from the disk into memory.
  • It sets up a character pointer which points to the first character of the chunk of the memory where the file is loaded.
Reading from a file

To read the file contents in the memory, there exists a function called fgetc().

ch = fgetc(fp);

fgetc() reads the character from the current file pointer position, advances the pointer position so that it now points to the next character, and returns the character it read, which we collected in the variable ch. While reading from the file, when fgetc() encounters the special character whose ASCII value is 26, it returns the macro EOF. The EOF macro has been defined in stdio.h.

Closing the file

When we have finished reading from the file, we need to close it. This is done using the function fclose() through the statement,

flose(fp);

File opening modes

Following is a list of all possible modes in which a file can be opened.

"r" - Reading from the file.

"w" - Writing into the file.

"a" - Appending new contents at the end of the file.

"r+" - Reading existing contents, Writing new contents, modifying existing contents.

"w+" - Writing new contents, Reading them back and modifying the existing contents.

"a+" - Reading existing contents, appending the new contents. Can't modify the existing contents.

Standard DOS devices

To perform reading or writing operations on a file, we need to use the function fopen(), which sets up a file pointer to refer to this file. MS-DOS also predefines file pointers for standard files.

stdin - Standard input device (Keyboard);

stdout - Standard output device (VDU);

stderr - Standard error device;

stdaux - Standard auxiliary device;

stdprn - Standard printing device;

Thus the statement ch = fgetc(stdin) would read a character from the keyboard rather than from a file.

fputc(ch, stdprn) writes a character read from the file to the printer.

Text mode versus Binary mode

There are 3 main areas where text and binary mode files are different. These are:

  1. Handling of new lines
  2. Representation of end of file
  3. Storage numbers

I am not going into detail about these things.

Record I/O in files

So far we have seen programs which write characters, strings or numbers to a file. If we desire to write a combination of dissimilar data types, what should we do? We all know, use structures. Following program illustrates the use of structures for writing records of employees.

====================================================================================

/*Writing records to a file using structures*/

#include "stdio.h"
main()
{
FILE *fp;
char another ='Y';
struct emp
{
int age;
char name[25];
float basic_sal;
} e;
fp = fopen("Employees_details.txt", "w");

if(fp == NULL)
{
printf("Can't open the write file\n");
exit();
}

while(another == 'Y')
{
printf("Enter the name of the employee, age and his bs\n");
scanf("%s%d%f", &e.name , &e.age , &e.basic_sal);

fprintf(fp, "%s %d %f\n", e.name , e.age , e.basic_sal);
printf("Add another record(Y/N)\n");
fflush(stdin);
another = getchar();
}
fclose(fp);
}

====================================================================================

The above program has two disadvantages:

Guess....???

  • The numbers would occupy more number of bytes, since the file has been in text mode. This is because when the file is opened in text mode, each number is stored as a character string.
  • If the number of fields in the structure increase, writing structures using fprintf(), or reading them using fscanf(), becomes quite clumsy.
Let us now see more efficient way of reading/writing structures. This makes use of two functions fread() and fwrite().

====================================================================================

main()
{
FILE *fp;
char another ='Y';

struct emp
{
int age;
char name[ 25];
float basic_sal;
} e;

fp = fopen("Employees_details.txt", "w");

if(fp == NULL)
{
printf("Can't open the write file\n");
exit();
}

while(another == 'Y')
{
printf("Enter the name of the employee, age and his bs\n");
scanf("%s%d%f", &e.name, &e.age, &e.basic_sal);
fwrite(&e, sizeof(e), 1,fp);
printf("Add another record(Y/N)");
fflush(stdin);
another = getchar();
}
fclose(fp);
} // End of main()

====================================================================================

Consider the statement:

fwrite (&e, sizeof (e), 1, fp);

Here, first argument is the address of the structure to be written to the disk. The second argument is the size of the structure in bytes. Instead of counting the bytes ourselves, we let the program do it for us by using the sizeof() operator. The third argument is the number of structures that we want to write at one time. In this case, we want to write only one structure at a time. The last argument is the pointer to the file we want to write to.

Reference

"Let us C" by Yashavant Kanethkar.


2 comments:

n00b said...

From Argentina, thank you very much! Great Post, very well explain!

Suresh said...

Thanks Nicolas, Thanks for your great support.