Unit 3
Preprocessor and File handling in C
Preprocessor:
The C Preprocessor is not a part of the compiler, but is a separate step in the compilation process. In simple terms, a C Preprocessor is just a text substitution tool and it instructs the compiler to do required pre-processing before the actual compilation. We'll refer to the C Preprocessor as CPP.
All preprocessor commands begin with a hash symbol (#). It must be the first nonblank character, and for readability, a preprocessor directive should begin in the first column. The following section lists down all the important preprocessor directives −
Sr.No. | Directive & Description |
1 | #define Substitutes a preprocessor macro. |
2 | #include Inserts a particular header from another file. |
3 | #undef Undefines a preprocessor macro. |
4 | #ifdef Returns true if this macro is defined. |
5 | #ifndef Returns true if this macro is not defined. |
6 | #if Tests if a compile time condition is true. |
7 | #else The alternative for #if. |
8 | #elif #else and #if in one statement. |
9 | #endif Ends preprocessor conditional. |
10 | #error Prints error message on stderr. |
11 | #pragma Issues special commands to the compiler, using a standardized method. |
Preprocessors Examples
Analyze the following examples to understand various directives.
#define MAX_ARRAY_LENGTH 20
This directive tells the CPP to replace instances of MAX_ARRAY_LENGTH with 20. Use #define for constants to increase readability.
#include <stdio.h>
#include "myheader.h"
These directives tell the CPP to get stdio.h from System Libraries and add the text to the current source file. The next line tells CPP to get myheader.h from the local directory and add the content to the current source file.
#undef FILE_SIZE
#define FILE_SIZE 42
It tells the CPP to undefine existing FILE_SIZE and define it as 42.
#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif
It tells the CPP to define MESSAGE only if MESSAGE isn't already defined.
#ifdef DEBUG
/* Your debugging statements here */
#endif
It tells the CPP to process the statements enclosed if DEBUG is defined. This is useful if you pass the -DDEBUG flag to the gcc compiler at the time of compilation. This will define DEBUG, so you can turn debugging on and off on the fly during compilation.
Predefined Macros
ANSI C defines a number of macros. Although each one is available for use in programming, the predefined macros should not be directly modified.
Sr.No. | Macro & Description |
1 | __DATE__ The current date as a character literal in "MMM DD YYYY" format. |
2 | __TIME__ The current time as a character literal in "HH:MM:SS" format. |
3 | __FILE__ This contains the current filename as a string literal. |
4 | __LINE__ This contains the current line number as a decimal constant. |
5 | __STDC__ Defined as 1 when the compiler complies with the ANSI standard. |
Let's try the following example −
#include <stdio.h>
Int main() {
Printf("File :%s\n", __FILE__ );
Printf("Date :%s\n", __DATE__ );
Printf("Time :%s\n", __TIME__ );
Printf("Line :%d\n", __LINE__ );
Printf("ANSI :%d\n", __STDC__ );
}
When the above code in a file test.c is compiled and executed, it produces the following result −
File :test.c
Date :Jun 2 2012
Time :03:36:24
Line :8
ANSI :1
Preprocessor Operators
The C preprocessor offers the following operators to help create macros −
The Macro Continuation (\) Operator
A macro is normally confined to a single line. The macro continuation operator (\) is used to continue a macro that is too long for a single line. For example −
#define message_for(a, b) \
Printf(#a " and " #b ": We love you!\n")
The Stringize (#) Operator
The stringize or number-sign operator ( '#' ), when used within a macro definition, converts a macro parameter into a string constant. This operator may be used only in a macro having a specified argument or parameter list. For example −
#include <stdio.h>
#define message_for(a, b) \
Printf(#a " and " #b ": We love you!\n")
Int main(void) {
Message_for(Carole, Debra);
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Carole and Debra: We love you!
The Token Pasting (##) Operator
The token-pasting operator (##) within a macro definition combines two arguments. It permits two separate tokens in the macro definition to be joined into a single token. For example −
#include <stdio.h>
#define tokenpaster(n) printf ("token" #n " = %d", token##n)
Int main(void) {
Int token34 = 40;
Tokenpaster(34);
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Token34 = 40
It happened so because this example results in the following actual output from the preprocessor −
Printf ("token34 = %d", token34);
This example shows the concatenation of token##n into token34 and here we have used both stringize and token-pasting.
The Defined() Operator
The preprocessor defined operator is used in constant expressions to determine if an identifier is defined using #define. If the specified identifier is defined, the value is true (non-zero). If the symbol is not defined, the value is false (zero). The defined operator is specified as follows −
#include <stdio.h>
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
Int main(void) {
Printf("Here is the message: %s\n", MESSAGE);
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Here is the message: You wish!
Parameterized Macros
One of the powerful functions of the CPP is the ability to simulate functions using parameterized macros. For example, we might have some code to square a number as follows −
Int square(int x) {
Return x * x;
}
We can rewrite above the code using a macro as follows −
#define square(x) ((x) * (x))
Macros with arguments must be defined using the #define directive before they can be used. The argument list is enclosed in parentheses and must immediately follow the macro name. Spaces are not allowed between the macro name and open parenthesis. For example −
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
Int main(void) {
Printf("Max between 20 and 10 is %d\n", MAX(10, 20));
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Max between 20 and 10 is 20
Files:
Main memory is volatile and the data would be lost once the program is terminated. If we need the same data again, we have to store the data in a file on the disk. A file is sequential stream of bytes ending with an end-of-file marker.
Types of file supported by C:
- Text Files
- Binary Files
Difference between text file and binary file
- Text file is human readable because everything is stored in terms of text. In binary file everything is written in terms of 0 and 1, therefore binary file is not human readable.
- A newline(\n) character is converted into the carriage return-linefeed combination before being written to the disk. In binary file, these conversions will not take place.
- In text file, a special character, whose ASCII value is 26, is inserted after the last character in the file to mark the end of file. There is no such special character present in the binary mode files to mark the end of file.
- In text file, the text and characters are stored one character per byte. For example, the integer value 1245 will occupy 2 bytes in memory but it will occupy 5 bytes in text file. In binary file, the integer value 1245 will occupy 2 bytes in memory as well as in file.
Declaration of File Pointer
To access any file, we need to declare a pointer to FILE structure and then associate it with the particular file. This pointer is referred to as file pointer.
Syntax for declaring file pointer
FILE * fp;
A pointer to FILE structure contains information, such as size, current file pointer position, type of file etc., to perform operation on the file.
Opening a file using fopen() function
The fopen() function takes two arguments, the name of the file and the mode in which the file is to be opened. Mode specify the purpose of opening the file i.e, whether for reading or writing.
Syntax for opening the file in C
Fp = fopen(char *filename,char *mode);
When fopen() function opens a file in memory, it returns a pointer to this particular file. If fopen() function can't open the file then it will return NULL.
Example for opening the file in C
Void main()
{
FILE *fp;
Fp = fopen("file1.txt","r"); //Statement 1
If(fp == NULL)
{
Printf("\nCan't open file or file doesn't exist.");
Exit(0);
}
In the above example, statement 1 will open an existing in text mode and return a pointer to file. If file will not open, an appropriate message will be displayed.
File Opening Modes
Mode | Purpose |
"r" or "rt" | Open a text file for reading, The file must already exist. |
"w" or "wt" | Open a text file for writing. If the file already exists, all the data will lost. If it doesnt exist, it will be created. |
"a" or "at" | Open a text file for appending. Data will be added at the end of the existing file. If file doesnt exist, it will be created. |
Rb | Open a binary file for reading, The file must already exist. |
Wb | Open a binary file for writing. If the file already exists, its contents will be overwritten. If it doesnt exist, it will be created. |
Ab | Open a binary file for appending. Data will be added at the end of the existing file. If file doesnt exist, it will be created. |
"rt+" or "r+t" | Open text file for reading and writing. The file must already exist. |
"wt+" or "w+t" | Open text file for reading and writing. If file doesn't exist, it will be created. |
"at+" or "a+t" | Open text file for reading and appending. If file doesn't exist, it will be created. |
"rb+" or "r+b" | Open binary file for reading and writing. The file must already exist. |
"wb+" or "w+b" | Open binary file for reading and writing. If file doesn't exist, it will be created. |
"ab+" or "a+b" | Open binary file for reading and appending. If file doesn't exist, it will be created. |
Closing a File using fclose() function
When the reading or writing of a file is finished, the file should be closed properly using fclose() function. The fclose() function does the followling tasks:
- Flushes any unwritten data from memory.
- Discards any unread buffered input.
- Frees any automatically allocated buffer
- Finally, close the file.
Syntax for closing the file in C
Int fclose( FILE* );
Example for closing the file in C
Void main()
{
FILE *fp;
Fp = fopen("file1.txt","r");
If(fp == NULL)
{
Printf("\nCan't open file or file doesn't exist.");
Exit(0);
}
- - - - - - - - - -
- - - - - - - - - -
Fclose(fp);
}
Reading and Writing File
We can read data from file and write data to file in many ways.
- Reading or writing characters using fgetc() and fputc() functions.
- Reading or writing string using fgets() and fputs() functions.
- Reading or writing integers using getw() and putw() functions.
- Reading or writing formatted IO using fscanf() and fprintf() functions.
- Reading or writing records using fread() and fwrite() functions.
File Positioning Functions
File positioning functions are used to move the pointer in a file to the desired position, without closing and re-opening the file.
C provides followling three functions to move the pointer in the file:
- The rewind() function is used to move the pointer to the beginning of the file.
- The ftell() function is used to retrieve the current position of pointer in the file.
- The fseek() function is used to move the pointer to the desired position in the file.
Writing
To write a binary file in C++ use write method. It is used to write a given number of bytes on the given stream, starting at the position of the "put" pointer. The file is extended if the put pointer is currently at the end of the file. If this pointer points into the middle of the file, characters in the file are overwritten with the new data.
If any error has occurred during writing in the file, the stream is placed in an error state.
Syntax of write method
Ostream& write(const char*, int);
Reading
To read a binary file in C++ use read method. It extracts a given number of bytes from the given stream and place them into the memory, pointed to by the first parameter. If any error is occurred during reading in the file, the stream is placed in an error state, all future read operation will be failed then.
Gcount() can be used to count the number of characters has already read. Then clear() can be used to reset the stream to a usable state.
Syntax of read method
Ifstream& write(const char*, int);
Algorithm
Begin
Create a structure Student to declare variables.
Open binary file to write.
Check if any error occurs in file opening.
Initialize the variables with data.
If file open successfully, write the binary data using write method.
Close the file for writing.
Open the binary file to read.
Check if any error occurs in file opening.
If file open successfully, read the binary data file using read method.
Close the file for reading.
Check if any error occurs.
Print the data.
End.
Example Code
#include<iostream>
#include<fstream>
Using namespace std;
Struct Student {
Int roll_no;
String name;
};
Int main() {
Ofstream wf("student.dat", ios::out | ios::binary);
If(!wf) {
Cout << "Cannot open file!" << endl;
Return 1;
}
Student wstu[3];
Wstu[0].roll_no = 1;
Wstu[0].name = "Ram";
Wstu[1].roll_no = 2;
Wstu[1].name = "Shyam";
Wstu[2].roll_no = 3;
Wstu[2].name = "Madhu";
For(int i = 0; i < 3; i++)
Wf.write((char *) &wstu[i], sizeof(Student));
Wf.close();
If(!wf.good()) {
Cout << "Error occurred at writing time!" << endl;
Return 1;
}
Ifstream rf("student.dat", ios::out | ios::binary);
If(!rf) {
Cout << "Cannot open file!" << endl;
Return 1;
}
Student rstu[3];
For(int i = 0; i < 3; i++)
Rf.read((char *) &rstu[i], sizeof(Student));
Rf.close();
If(!rf.good()) {
Cout << "Error occurred at reading time!" << endl;
Return 1;
}
Cout<<"Student's Details:"<<endl;
For(int i=0; i < 3; i++) {
Cout << "Roll No: " << wstu[i].roll_no << endl;
Cout << "Name: " << wstu[i].name << endl;
Cout << endl;
}
Return 0;
}
Output
Student’s Details:
Roll No: 1
Name: Ram
Roll No: 2
Name: Shyam
Roll No: 3
Name: Madhu
Let's first see what should be the step-by-step procedure to compare two integers−
START
Step 1 → Take two integer variables, say A & B
Step 2 → Assign values to variables
Step 3 → Compare variables if A is greater than B
Step 4 → If true print A is greater than B
Step 5 → If false print A is not greater than B
STOP
Flow Diagram
We can draw a flow diagram for this program as given below −
Pseudocode
Let's now see the pseudocode of this algorithm −
Procedure compare (A, B)
IF A is greater than B
DISPLAY "A is greater than B"
ELSE
DISPLAY "A is not greater than B"
END IF
End procedure
Implementation
Assuming we have file with name file_append.txt with this content −
This text was already there in the file.
Now, we shall see the actual implementation of the program −
#include <stdio.h>
Int main()
{
FILE *fp;
Char ch;
Char *filename = "file_append.txt";
Char *content = "This text is appeneded later to the file, using C programming.";
/* open for writing */
Fp = fopen(filename, "r");
Printf("\nContents of %s -\n\n", filename);
While ((ch = fgetc(fp) )!= EOF)
{
Printf ("%c", ch);
}
Fclose(fp);
Fp = fopen(filename, "a");
/* Write content to file */
Fprintf(fp, "%s\n", content);
Fclose(fp);
Fp = fopen(filename, "r");
Printf("\nContents of %s -\n", filename);
While ((ch = fgetc(fp) )!= EOF)
{
Printf ("%c", ch);
}
Fclose(fp);
Return 0;
}
Output
Output of this program should be −
Contents of file_append.txt -
This text was already there in the file.
Appending content to file_append.txt...
Content of file_append.txt after 'append' operation is -
This text was already there in the file.
This text is appended later to the file, using C programming.
For writing in file, it is easy to write string or int to file using fprintf and putc, but you might have faced difficulty when writing contents of struct. Fwrite and fread make task easier when you want to write and read blocks of data.
- Fwrite : Following is the declaration of fwrite function
- Size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
- Ptr - This is pointer to array of elements to be written
- Size - This is the size in bytes of each element to be written
- Nmemb - This is the number of elements, each one with a size of size bytes
- Stream - This is the pointer to a FILE object that specifies an output stream
// C program for writing // struct to file #include <stdio.h> #include <stdlib.h> #include <string.h>
// a struct to read and write Struct person { Int id; Char fname[20]; Char lname[20]; };
Int main () { FILE *outfile;
// open file for writing Outfile = fopen ("person.dat", "w"); If (outfile == NULL) { Fprintf(stderr, "\nError opend file\n"); Exit (1); }
Struct person input1 = {1, "rohan", "sharma"}; Struct person input2 = {2, "mahendra", "dhoni"};
// write struct to file Fwrite (&input1, sizeof(struct person), 1, outfile); Fwrite (&input2, sizeof(struct person), 1, outfile);
If(fwrite != 0) Printf("contents to file written successfully !\n"); Else Printf("error writing file !\n");
// close file Fclose (outfile);
Return 0; } |
Output:
Gcc demowrite.c
./a.out
Contents to file written successfully!
7. fread : Following is the declaration of fread function
8. size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
9. ptr - This is the pointer to a block of memory with a minimum size of size*nmemb bytes.
10. size - This is the size in bytes of each element to be read.
11. nmemb - This is the number of elements, each one with a size of size bytes.
Stream - This is the pointer to a FILE object that specifies an input stream.
// C program for reading // struct from a file #include <stdio.h> #include <stdlib.h>
// struct person with 3 fields Struct person { Int id; Char fname[20]; Char lname[20]; };
// Driver program Int main () { FILE *infile; Struct person input;
// Open person.dat for reading Infile = fopen ("person.dat", "r"); If (infile == NULL) { Fprintf(stderr, "\nError opening file\n"); Exit (1); }
// read file contents till end of file While(fread(&input, sizeof(struct person), 1, infile)) Printf ("id = %d name = %s %s\n", input.id, Input.fname, input.lname);
// close file Fclose (infile);
Return 0; } |
Output:
Gcc demoread.c
./a.out
Id = 1 name = rohan sharma
Id = 2 name = mahendra dhoni
Random Access To File
There is no need to read each record sequentially, if we want to access a particular record.C supports these functions for random access file processing.
- Fseek()
- Ftell()
- Rewind()
fseek():
This function is used for seeking the pointer position in the file at the specified byte.
Syntax: fseek( file pointer, displacement, pointer position);
Where
file pointer ---- It is the pointer which points to the file.
displacement ---- It is positive or negative.This is the number of bytes which are skipped backward (if negative) or forward( if positive) from the current position.This is attached with L because this is a long integer.
Pointer position:
This sets the pointer position in the file.
Value pointer position
0 Beginning of file.
1 Current position
2 End of file
Ex:
1) fseek( p,10L,0)
0 means pointer position is on beginning of the file, from this statement pointer position is skipped 10 bytes from the beginning of the file.
2)fseek( p,5L,1)
1 means current position of the pointer position. From this statement pointer position is skipped 5 bytes forward from the current position.
3)fseek(p,-5L,1)
From this statement pointer position is skipped 5 bytes backward from the current position.
ftell()
This function returns the value of the current pointer position in the file. The value is count from the beginning of the file.
Syntax: ftell(fptr);
Where fptr is a file pointer.
rewind()
This function is used to move the file pointer to the beginning of the given file.
Syntax: rewind( fptr);
Where fptr is a file pointer.
Example program for fseek():
Write a program to read last ‘n’ characters of the file using appropriate file functions(Here we need fseek() and fgetc()).
01 | #include<stdio.h> |
02 | #include<conio.h> |
03 | Void main() | |
04 | { | |
05 | FILE *fp; | |
06 | Char ch; | |
07 | Clrscr(); | |
08 | Fp=fopen("file1.c", "r"); | |
09 | If(fp==NULL) | |
10 | Printf("file cannot be opened"); | |
11 | Else | |
12 | { | |
13 | Printf("Enter value of n to read last ‘n’ characters"); | |
14 | Scanf("%d",&n); | |
15 | Fseek(fp,-n,2); | |
16 | While((ch=fgetc(fp))!=EOF) | |
17 | { | |
18 | Printf("%c\t",ch); | |
19 | } | |
20 | } | |
21 | Fclose(fp); | |
22 | Getch(); | |
23 | } |
OUTPUT: It depends on the content in the file.