2.8 Files

Basics and Classification of Files

When reading input from the keyboard and writing output to the monitor you have been using a special case of file I/O (input/output). You already know how to read and write text data, as you have been doing it every time you use scanf() and printf(). All you need to do now is learn how to direct I/O to file other than from your keyboard or to your monitor.

Abstractly, a file is a collection of bytes stored on a secondary storage device, which is generally a disk of some kind. The collection of bytes may be interpreted, for example, as characters, words, lines, paragraphs and pages from a textual document; fields and records belonging to a database; or pixels from a graphical image. The meaning attached to a particular file is determined entirely by the data structures and operations used by a program to process the file. It is conceivable (and it sometimes happens) that a graphics file will be read and displayed by a program designed to process textual data. The result is that no meaningful output occurs (probably) and this is to be expected. A file is simply a machine decipherable storage media where programs and data are stored for machine usage.

Essentially there are two kinds of files that programmers deal with text files and binary files:

  • Text files are any files that contain only ASCII characters. Examples include C source code files, HTML files, and any file that can be viewed using a simple text editor.
  • Binary files are any files that created by writing on it from a C-program, not by an editor (as with text files). Binary files are very similar to arrays of records, except the records are in a disk file rather than in an array in memory. Because the records in a binary file are on disk, you can create very large collections of them (limited only by your available disk space). They are also permanent and always available. The only disadvantage is the slowness that comes from disk access time.

A text file can be a stream of characters that a computer can process sequentially. It is not only processed sequentially but only in forward direction. For this reason a text file is usually opened for only one kind of operation (reading, writing, or appending) at any given time.

Similarly, since text files only process characters, they can only read or write data one character at a time. (In C Programming Language, Functions are provided that deal with lines of text, but these still essentially process data one character at a time). A text stream in C is a special kind of file. Depending on the requirements of the operating system, newline characters may be converted to or from carriage-return/linefeed combinations depending on whether data is being written to, or read from, the file. Other character conversions may also occur to satisfy the storage requirements of the operating system. These translations occur transparently and they occur because the programmer has signaled the intention to process a text file.

A binary file is no different to a text file. It is a collection of bytes. In C Programming Language a byte and a character are equivalent. Hence a binary file is also referred to as a character stream, but there are two essential differences.

  • No special processing of the data occurs and each byte of data is transferred to or from the disk unprocessed.
  • C Programming Language places no constructs on the file, and it may be read from, or written to, in any manner chosen by the programmer.

Binary files can be either processed sequentially or, depending on the needs of the application, they can be processed using random access techniques. In C Programming Language, processing a file using random access techniques involves moving the current file position to an appropriate place in the file before reading or writing data. This indicates a second characteristic of binary files – they a generally processed using read and write operations simultaneously.

For example, a database file will be created and processed as a binary file. A record update operation will involve locating the appropriate record, reading the record into memory, modifying it in some way, and finally writing the record back to disk at its appropriate location in the file. These kinds of operations are common to many binary files, but are rarely found in applications that process text files.

For all file operations you should always follow the 5-step plan as outlined below.

  1. Declare file pointer.
  2. Attach the file pointer to the file (open file).
  3. Check file opened correctly.
  4. Read or Write the data from or to the file.
  5. Close the file.

Operations on Files

Declarations

In C, we usually create variables of type FILE * to point to a file located on the computer.

FILE *file_pointer_name;

Example

FILE * f1, * f2;

Open Files

First things first: we have to open a file to be able to do anything else with it. For this, we use fopen function, like all the I/O functions, is made available by the stdio.h library. The fopen() function prototype is as follows.

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

In the above prototype, there are two arguments:

  • filename is a string containing the name of the file to be opened. So if your file sits in the same directory as your C source file, you can simply enter the filename in here – this is probably the one you’ll use most.
  • mode determines how the file may be accessed.
Mode Meaning
“r” Open a file for read only, starts at beginning of file (default mode).
“w” Write-only, truncates existing file to zero length or create a new file for writing.
“a” Write-only, starts at end of file if file exists,otherwise creates a new file for writing.
“r+” Open a file for read-write, starts at beginning of file. If the file is not exist, it will cause an error.
“w+” Read-write, truncates existing file to zero length or creates a new file for reading and writing.
“a+” Read-write, starts at end of file if file exists, otherwise creates a new file for reading and writing.

So there are 12 different values that could be used: “rt”, “wt”, “at”, “r+t”, “w+t”, “a+t” and “rb”, “wb”, “ab”, “r+b”, “w+b”, “a+b”.

Character Type
“t” Text File
“b” Binary File
When work with the text file, you also can use only “r”, “w”, “a”, “r”, “w”, “a”, instead of “rt”, “wt”, “at”, “r+t”, “w+t”, “a+t” respectively.

Example

FILE *f1, *f2, *f3, *f4;

To open text file c:\abc.txt for ready only:

f1 = fopen("c:\\abc.txt", "r");

To open text file c:\list.dat for write only:

f2 = fopen("c:\\list.dat", "w");

To open text file c:\abc.txt for read-write:

f3 = fopen("c:\\abc.txt", "r+");

To open binary file c:\liststudent.dat for write only:

f4 = fopen("c:\\liststudent.dat", "wb");

The file pointer will be used with all other functions that operate on the file and it must never be altered or the object it points to.

File checking

if (file_pointer_name == NULL) 
{
printf("Error opening file.");
<Action for error >
}
else
{
<Action for success>
}

Before using an input/output file it is worth checking that the file has been correctly opened first. A call to fopen() may result in an error due to a number of reasons including:

  • A file opened for reading does not exist;
  • A file opened for reading is read protected;
  • A file is being opened for writing in a folder or directory where you do not have write access.

If the operation is successful, fopen() returns an address which can be used as a stream. If a file is not successfully opened, the value NULL is returned. An error opening a file can occur if the file was to be opened for reading and did not exist, or a file opened for writing could not be created due to lack of disk space. It is important to always check that the file has opened correctly before proceeding in the program.

FILE *fp;
if ((fp = fopen("myfile", "r")) ==NULL){
  printf("Error opening file\n");
  exit(1); 
}

Once a file has been opened, depending upon its mode, you may read and/or write bytes to or from it.

Access to Text Files

Write data to text files

When writing data to text files, C provides three functions: fprintf()fputs()fputc().

The fprintf() function prototype is as follows:

int fprintf(FILE *fp, char *format, ...);

This function writes to the file specified by file pointer fp a sequence of data formatted as the format argument specifies. After the format parameter, the function expects at least as many additional arguments as specified in format. Depending on the format string, the function may expect a sequence of additional arguments, each containing one value to be inserted instead of each %-tag specified in the format parameter, if any. There should be the same number of these arguments as the number of %-tags that expect a value.

Return value: On success, the total number of characters written is returned. On failure, a negative number is returned.

#include <stdio.h>

int main ()
{
   FILE * fp;
   int n;
   char name [50];

   fp = fopen ("myfile.txt","w");
   for (n=0 ; n<3 ; n++)
   {
     puts ("Please, enter a name: ");
     gets (name);
     fprintf (fp, "Name %d [%-10.10s]\n",n,name);
   }
   fclose (fp);
   return 0;
}

This example prompts 3 times the user for a name and then writes them to myfile.txt each one in a line with a fixed length (a total of 19 characters + newline). Two format tags are used: %d : signed decimal integer, %-10.10s : left aligned (-), minimum of ten characters (10), maximum of ten characters (.10), String (s).

Assuming that we have entered John, Jean-Francois and Yoko as the 3 names, myfile.txt would contain:

myfile.txt
Name 1 [John ]
Name 2 [Jean-Franc]
Name 3 [Yoko ]

The fputc() function prototype is as follows.

int fputc(int character, FILE *fp);

The fputc() function writes a character to the file associated with fp. The character is written at the current position of the fp as indicated by the internal position indicator, which is then advanced one character

Return value: If there are no errors, the same character that has been written is returned.If an error occurs, EOF is returned and the error indicator is set.

Write the program that creates a file called alphabet.txt and writes ABCDEFGHIJKLMNOPQRSTUVWXYZ to it.

#include <stdio.h>
int main ()
{
  FILE * fp;
  char c;

  fp = fopen ("alphabet.txt","w");
  if (fp!=NULL)
  {
    for (c = 'A' ; c <= 'Z' ; c++)
    {
      fputc ((int) c , fp);
    }
    fclose (fp);
  }
  return 0;
}

The fputs() function prototype is as follows.

int fputs(char *str,FILE *fp);

The fputs() function writes the string pointed to by str to the file associated with fp.

Return value: On success, a non-negative value is returned. On error, the function returns EOF. The null that terminates str is not written and it does not automatically append a carriage return/linefeed sequence.

Write the program allows to append a line to a file called myfile.txt each time it is run.

#include <stdio.h>
int main ()
{
   FILE * fp;
   char name [50];

   puts ("Please, enter a name: ");
   gets (name);
   fp = fopen ("myfile.txt","a");
   fputs (name,fp);
   fclose (fp);
   return 0;
}
Read data from text files

When reading data from text files, C provides three functions: fscanf(),fgetc()fgets().

The fscanf() function prototype is as follows.

int fscanf(FILE *fp, char *format, ...);

This function reads data from the file specified by file pointer fp and stores them according to the parameter format into the locations pointed by the additional arguments. The additional arguments should point to already allocated objects of the type specified by their corresponding format tag within the format string.

Return value: On success, the function returns the number of items successfully read. This count can match the expected number of readings or be less -even zero- in the case of a matching failure. In the case of an input failure before any data could be successfully read, EOF is returned.

Read an integer number and a character from file associated with a file pointer fp and stores them to two variables a and c.

fscanf(fp, "%d %c",&a, &c);
#include <stdio.h>
int main ()
{
  char str [80];
  float f;
  FILE * fp;

  fp = fopen ("myfile.txt","w+");
  fprintf (fp, "%f %s", 3.1416, "PI");
  rewind (fp);
  fscanf (fp, "%f", &f);
  fscanf (fp, "%s", str);
  fclose (fp);
  printf ("I have read: %f and %s \n",f,str);
  return 0;
}

This sample code creates a file called myfile.txt and writes a float number and a string to it. Then, the stream is rewinded and both values are read with fscanf. It finally produces an output similar to:

I have read: 3.141600 and PI
feof() function
int feof(FILE *fp);

This function check if End-of-File indicator associated with fp is set

Return value: A non-zero value is returned in the case that the End-of-File indicator associated with the fp is set. Otherwise, a zero value is returned.

Create a text file called fscanf.txt in Notepad with this content:

0	1	2	3	4 
5	6	7	8	9 
10 	11 	12 	13

Remember how scanf stops reading input when it encounters a space, line break or tab character? fscanf is just the same. So if all goes to plan, this example should open the file, read all the numbers and print them out:

#include <stdio.h>
int main() {
  FILE *fp;
  int numbers[30]; 
  /* make sure it is large enough to hold all the data! */
  int i,j;

  fp = fopen("fscanf.txt", "r");

  if(fp==NULL) {
    printf("Error: can't open file.\n");
    return 1;
  }
  else {
    printf("File opened successfully.\n");

    i = 0 ;    

    while(!feof(fp)) { 
      /* loop through and store the numbers into the array */
      fscanf(fp, "%d", &numbers[i]);
      i++;
    }

    printf("Number of numbers read: %d\n\n", i);
    printf("The numbers are:\n");

    for(j=0 ; j<i ; j++) { /* now print them out one by one */
      printf("%d\n", numbers[j]);
    }

    fclose(fp);
    return 0;
  }
}

fflush() function

Same as scanf(), before using fscanf() to read the character or string from the file, we need use fflush().The fflush() function prototype is as follows.

int fflush(FILE *fp)

If the given file that specified by fp was open for writing and the last I/O operation was an output operation, any unwritten data in the output buffer is written to the file. If the file was open for reading, the behavior depends on the specific implementation. In some implementations this causes the input buffer to be cleared. If the argument is a null pointer, all open files are flushed. The files remains open after this call. When a file is closed, either because of a call to fclose or because the program terminates, all the buffers associated with it are automatically flushed.

Return Value: A zero value indicates success. If an error occurs, EOF is returned and the error indicator is set (see feof).

fgetc() function

The fgetc() function prototype is as follows.

int fgetc(FILE *fp);

This function returns the character currently pointed by the internal file position indicator of the specified fp. The internal file position indicator is then advanced by one character to point to the next character.

Return value: The character read is returned as an int value. If the EOF is reached or a reading error happens, the function returns EOF and the corresponding error or eof indicator is set. You can use either ferror or feof to determine whether an error happened or the EOF was reached.

Write the program reads an existing file called myfile.txt character by character and uses the n variable to count how many dollar characters ($) does the file contain.

#include <stdio.h>
int main ()
{
  FILE * fp;
  int c;
  int n = 0;
  fp=fopen ("myfile.txt","r");
  if (fp==NULL) printf("Error opening file");
  else
  {
    do {
      c = fgetc (fp);
      if (c == '$') n++;
    } while (c != EOF);
    fclose (fp);
    printf ("File contains %d$.\n",n);
  }
  return 0;
}

Write the program opens the file called myfile.txt, and counts the number of characters that it contains by reading all of them one by one. Finally the total amount of bytes is printed out.

#include <stdio.h>
int main ()
{
  FILE * fp;
  long n = 0;
  fp = fopen ("myfile.txt","rb");
  if (fp==NULL) printf ("Error opening file");
  else
  {
    while (!feof(fp)) {
      fgetc (fp);
      n++;
      }
    fclose (fp);
    printf ("Total number of bytes: %d\n",n);
  }
  return 0;
}

Opens a file called input.txt which has some random text (less than 200 characters), stores each character in an array, then spits them back out into another file called “output.txt” in reverse order:

#include <stdio.h>
int main() {
  char c;         /* declare a char variable */
  char name[200]; /* Initialize array of total 
                     200 for characters */
  FILE *f_input, *f_output; /* declare FILE pointers  */
  int counter = 0; /* Initialize variable for counter to zero */

  f_input = fopen("input.txt", "r"); 
  /* open a text file for reading */

  if(f_input==NULL) {
    printf("Error: can't open file.\n");
    return 1;
  }
  else {
    while(1) { /* loop continuously */
      c = fgetc(f_input); /* fetch the next character */
      if(c==EOF) { 
        /* if end of file reached, break out of loop */
        break;
      }
      else if (counter<200) { /* else put character into array */
        name[counter] = c;
        counter++; /* increment the counter */
      }
      else {
        break;
      }
    }

    fclose(f_input); /* close input file */

    f_output = fopen("output.txt", "w"); 
    /* create a text file for writing */

    if(f_output==NULL) {
      printf("Error: can't create file.\n");
      return 1;
    }
    else {
      counter--; /* we went one too step far */
      while(counter >= 0) { /* loop while counter's above zero */
        fputc(name[counter], f_output); 
        /* write character into output file */
        counter--; /* decrease counter */
      }

      fclose(f_output); /* close output file */
      printf("All done!\n");
      return 0;
    }
  }
}

Reading one character at a time can be a little inefficient, so we can use fgets to read one line at a time. The fgets() function prototype is as follows.

char *fgets(char *str, int num, FILE *fp);

The fgets() function reads characters from the file associated with fp into a string pointed to by str until num-1 characters have been read, a newline character is encountered, or the end of the file is reached. The string is null-terminated and the newline character is retained.

Return value: the function returns str if successful and a null pointer if an error occurs.

You can’t use an !=EOF check here, as we’re not reading one character at a time (but you can use feof).

Create a file called myfile.txt in Notepad, include 3 lines and put tabs in the last line.

111 222 333
444 555 666
777	888	999 
#include <stdio.h>
int main() 
{
  char c[10];  /* declare a char array */
  FILE *file;  /* declare a FILE pointer  */

  file = fopen("myfile.txt", "r"); 
  /* open a text file for reading */

  if(file==NULL) 
  {
    printf("Error: can't open file.\n");
    /* fclose(file); DON'T PASS A NULL POINTER TO fclose !! */
    return 1;
  }
  else 
  {
    printf("File opened successfully. Contents:\n\n");
    
    while(fgets(c, 10, file)!=NULL) { 
      /* keep looping until NULL pointer... */
      printf("String: %s", c);        
      /* print the file one line at a time  */
    }

    printf("\n\nNow closing file...\n");
    fclose(file);
    return 0;
  }
}

Output:

File opened successfully. Contents: 

String: 111 222 3String: 33 
String: 444 555 6String: 66 
String: 777     888     9String: 99 

Now closing file... 

The main area of focus is the while loop – notice how I performed the check for the return of a NULL pointer. Remember that passing in char * variable, c as the first argument assigns the line read into c, which is printed off by printf. We specified a maximum number of characters to be 10 – we knew the number of characters per line in our text file is more than this, but we wanted to show that fgets reads 10 characters at a time in this case.

Notice how fgets returns when the newline character is reached – this would explain why 444 and 777 follow the word “String”. Also, the tab character, \t, is treated as one character.

Other function:
fseek() function
int fseek (FILE *fp, long int offset, int origin);

In the above prototype, there are two arguments:

  • fp: Pointer to a FILE object that identifies the stream.
  • offset: Number of bytes to offset from origin.
  • If offset >= 0: set the position indicator toward to the end of file,
  • If offset < 0: set the position indicator toward to the beginning of file.
  • origin: Position from where offset is added. It is specified by one of the following constants defined in <cstdio>:
Constant Value Meaning
SEEK_SET 0 Beginning of file
SEEK_CUR 1 Current position of the file pointer
SEEK_END 2 End of file

This function sets the position indicator associated with the fp to a new position defined by adding offset to a reference position specified by origin. The End-of-File internal indicator of the file is cleared after a call to this function.

Return Value: If successful, the function returns a zero value. Otherwise, it returns nonzero value.

#include <stdio.h>
int main ()
{
  FILE * fp;
  fp = fopen ( "myfile.txt" , "w" );
  fputs ( "This is an apple." , fp );
  fseek ( fp , -8 , SEEK_END );
  fputs ( " sam" , fp );
  fclose ( fp );
  return 0;
}

After this code is successfully executed, the file myfile.txt contains:

This is a sample.
#include <stdio.h>
int main ()
{
  FILE * fp;
  fp = fopen ( "myfile.txt" , "w" );
  fputs ( "This is an apple." , fp );
  fseek ( fp , 9 , SEEK_SET );
  fputs ( " sam" , fp );
  fclose ( fp );
  return 0;
}

After this code is successfully executed, the file myfile.txt contains:

This is a sample.
rewind() function
void rewind (FILE *fp);

This function sets the current position indicator associated with fp to the beginning of the file. A call to rewind is equivalent to:

fseek (fp, 0, SEEK_SET);

except that, unlike fseek, rewind clears the error indicator.

On streams open for update (read+write), a call to rewind allows to switch between reading and writing.

#include <stdio.h>
#include <conio.h>
int main ()
{
  char str [80];
  int n;
  FILE * fp;
  fp = fopen ("myfile.txt","w+");
  for ( n='A' ; n<='Z' ; n++)
    fputc ( n, fp);
  rewind (fp);
  n=0;
  while (!feof(fp))
  {
     str[n]= fgetc(fp);
     n++;
  }
  fclose (fp);
  printf ("I have read: %s \n",str);
  getch();
  return 0;
}

A file called myfile.txt is created for reading and writing and filled with the alphabet. The file is then rewinded, read and its content is stored in a buffer, that then is written to the standard output:

ABCDEFGHIJKLMNOPQRSTUVWXYZ
#include <stdio.h>
int main() 
{
  FILE *file;
  char sentence[50];
  int i;

  file = fopen("sentence.txt", "w+");
  /* we create a file for reading and writing */

  if(file==NULL) {
    printf("Error: can't create file.\n");
    return 1;
  }
  else {
    printf("File created successfully.\n");

    printf("Enter a sentence less than 50 characters: ");
    gets(sentence);

    for(i=0 ; sentence[i] ; i++) {
      fputc(sentence[i], file);
    }

    rewind(file); /* reset the file pointer's position */

    printf("Contents of the file: \n\n");

    while(!feof(file)) {
      printf("%c", fgetc(file));
    }

    printf("\n");
    fclose(file);
    return 0;
  }
}

Output depends on what you entered. First of all, we stored the inputted sentence in a char array, since we’re writing to a file one character at a time it’d be useful to detect for the null character. Recall that the null character, \0, returns 0, so putting sentence[i] in the condition part of the for loop iterates until the null character is met.

Then we call rewind, which takes the file pointer to the beginning of the file, so we can read from it. In the while loop we print the contents a character at a time, until we reach the end of the file – determined by using the feof function.

Note that it is essential to have the include file stdio.h referenced at the top of your program in order to use any of these functions: fscanf(), fgets(), fgetc(), fflush(), fprintf(), fputs(), fputc(), feof(), fseek() và rewind().

EOF and errors

When a function returns EOF (or, occasionally, 0 or NULL, as in the case of fread and fgets respectively), we commonly say that we have reached “end of file” but it turns out that it’s also possible that there’s been some kind of I/O error. When you want to distinguish between end-of-file and error, you can do so with the feof and ferror functions. feof(fp) returns nonzero (that is, “true”) if end-of-file has been reached on the file pointer fp, and ferror(fp) returns nonzero if there has been an error.

Notice feof returns nonzero if end-of-file has been reached. It does not tell you that the next attempt to read from the stream will reach end-of-file, but rather that the previous attempt (by some other function) already did. (If you know Pascal, you may notice that the end-of-file detection situation in C is therefore quite different from Pascal.) Therefore, you would never write a loop like

while(!feof(fp))
   fgets(line, max, fp);

Instead, check the return value of the input function directly:

while(fgets(line, max, fp) != NULL)

With a very few possible exceptions, you don’t use feof to detect end-of-file; you use feof or ferror to distinguish between end-of-file and error. (You can also use ferror to diagnose error conditions on output files.)

Since the end-of-file and error conditions tend to persist on a stream, it’s sometimes necessary to clear (reset) them, which you can do with clearerr(FILE *fp).

What should your program do if it detects an I/O error? Certainly, it cannot continue as usual; usually, it will print an error message. The simplest error messages are of the form

fp = fopen(filename, "r");
	if(fp == NULL)
	{
		fprintf(stderr, "can't open file\n");
		return;
	}

or

while(fgets(line, max, fp) != NULL)
	{
	  ... process input ...
	}

	if(ferror(fp))
	   fprintf(stderr, "error reading input\n");

or

fprintf(fp, "%d %d %d\n", a, b, c);
 if(ferror(fp))
      fprintf(stderr, "output write error\n");

Error messages are much more useful, however, if they include a bit more information, such as the name of the file for which the operation is failing, and if possible why it is failing. For example, here is a more polite way to report that a file could not be opened:

        #include <stdio.h>	/* for fopen */
	#include <errno.h>	/* for errno */
	#include <string.h>	/* for strerror */

	fp = fopen(filename, "r");
	if(fp == NULL)
	{
		fprintf(stderr, "can't open %s for reading: %s\n",
					filename, strerror(errno));
		return;
	}

errno is a global variable, declared in <errno.h>, which may contain a numeric code indicating the reason for a recent system-related error such as inability to open a file. The strerror function takes an errno code and returns a human-readable string such as “No such file” or “Permission denied”.

An even more useful error message, especially for a “toolkit” program intended to be used in conjunction with other programs, would include in the message text the name of the program reporting the error.

Access to Binary Files

Write data to binary files
size_t fwrite(void *buf, size_t sz, size_t n, FILE *fp)

This function writes to file associated with fp, num number of objects, each object size bytes long, from the buffer pointed to by buffer.

Return value: It returns the number of objects written. This value will be less than num only if an output error as occurred.

The void pointer is a pointer that can point to any type of data without the use of a TYPE cast (known as a generic pointer). The type size_t is a variable that is able to hold a value equal to the size of the largest object surported by the compiler.

As a simple example, this program write an integer value to a file called MYFILE using its internal, binary representation.

#include <stdio.h>  /* header file  */
#include <stdlib.h>
void main(void)
{
 FILE *fp;   /* file pointer */
 int i;

 /* open file for output */
 if ((fp = fopen("myfile", "w"))==NULL){
  printf("Cannot open file \n");
  exit(1);
 }
 i=100;

 if (fwrite(&i, 2, 1, fp) !=1){
  printf("Write error occurred");
  exit(1);
 }
 fclose(fp);
}

Read data from binary files
size_t fread(void *buf, size_t sz, size_t n, FILE *fp)

fread reads up to n objects, each of size sz, from the file specified by fp, and copies them to the buffer pointed to by buf. It reads them as a stream of bytes, without doing any particular formatting or other interpretation. (However, the default underlying stdio machinery may still translate newline characters unless the stream is open in binary or “b” mode).

Return value: returns the number of items read. It returns 0 (not EOF) at end-of-file.

#include <stdio.h>
int main() {
  FILE *file;
  char c[30]; /* make sure it is large enough to hold all the data! */
  char *d;
  int n;

  file = fopen("numbers.txt", "r");

  if(file==NULL) {
    printf("Error: can't open file.\n");
    return 1;
  }
  else {
    printf("File opened successfully.\n");
    
    n = fread(c, 1, 10, file); /* passing a char array, 
                                  reading 10 characters */
    c[n] = '\0';               /* a char array is only a 
                                  string if it has the
                                  null character at the end */
    printf("%s\n", c);         /* print out the string      */
    printf("Characters read: %d\n\n", n);

    fclose(file);          /* to read the file from the beginning, */
                           /* we need to close and reopen the file */
    file = fopen("numbers.txt", "r");

    n = fread(d, 1, 10, file);
            /* passing a char pointer this time - 10 is irrelevant */
    printf("%s\n", d);
    printf("Characters read: %d\n\n", n);

    fclose(file);
    return 0;
  }
}

Output:

File opened successfully. 
111 
222 
33 
Characters read: 10 

111 
222 
333 

444 
5ive 
Characters read: 10 

The above code: passing a char pointer reads in the entire text file, as demonstrated. Note that the number fread returns in the char pointer case is clearly incorrect. This is because the char pointer (d in the example) must be initialized to point to something first.

An important line is: c[n] = ‘\0’; Previously, we put 10 instead of n (n is the number of characters read). The problem with this was if the text file contained less than 10 characters, the program would put the null character at a point past the end of the file.

There are several things you could try with this program:

  • After reading the memory allocation section, try allocating memory for d using malloc() and freeing it later with free().
  • Read 25 characters instead of 10: n = fread(c, 1, 25, file);
  • Not bother adding a null character by removing: c[n] = ‘\0’;
  • Not bother closing and reopening the file by removing the fclose and fopen after printing the char array.

Binary files have two features that distinguish them from text files: You can jump instantly to any record in the file, which provides random access as in an array; and you can change the contents of a record anywhere in the file at any time. Binary files also usually have faster read and write times than text files, because a binary image of the record is stored directly from memory to disk (or vice versa). In a text file, everything has to be converted back and forth to text, and this takes time.

Besides reading and writing “blocks” of characters, you can use fread and fwrite to do “binary” I/O. For example, if you have an array of int values:

int array[N];

you could write them all out at once by calling

fwrite(array, sizeof(int), N, fp);

This would write them all out in a byte-for-byte way, i.e. as a block copy of bytes from memory to the output stream, i.e. not as strings of digits as printf %d would. Since some of the bytes within the array of int might have the same value as the \n character, you would want to make sure that you had opened the stream in binary or “wb” mode when calling fopen.

Later, you could try to read the integers in by calling

fread(array, sizeof(int), N, fp);

Similarly, if you had a variable of some structure type:

struct somestruct x;

you could write it out all at once by calling

fwrite(&x, sizeof(struct somestruct), 1, fp);

and read it in by calling

fread(&x, sizeof(struct somestruct), 1, fp);

Close Files

The funtion for closing a file :

int fclose(FILE* fp);

This function closes the file associated with the fp and disassociates it. All internal buffers associated with the file are flushed: the content of any unwritten buffer is written and the content of any unread buffer is discarded. Even if the call fails, the fp passed as parameter will no longer be associated with the file.

Return value: If the file is successfully closed, a zero value is returned.

#include <stdio.h>
int main ()
{
  FILE * fp;
  fp = fopen ("myfile.txt","wt");
  fprintf (fp, "fclose example");
  fclose (fp);
  return 0;
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s