Friday, July 20, 2012

Reading and Writing Structures from/to Text Files in ANSI C

The only way to keep a program's data after exiting the program is to write the data in a file. By writing the program's data into a file you will be able to use it next time you start the program.

Let us suppose that we have a structure Person with the following attributes:
-Name (string) (the string can contain spaces)
-Age (integer)
-Salary (real)
-Email (string) (cannot contain spaces)

We shall consider a simple program that writes into a text file a struct vector of the Person type and then loads from the same text file the data into a new struct vector of the Person type.
#include<stdio.h>
#include<string.h>

/*The size of the buffer used for reading from the text file*/
#define BUFFER_SIZE   250
/*The maximum size of the name field in the Person struct*/
#define MAX_NAME_SIZE    80
/*The maximum size of the email field in the Person struct*/
#define MAX_EMAIL_SIZE   60
/*The maximum number of persons*/
#define MAX_PERSONS      10
/*The relative path to the file where you can find the text file*/
#define PATH_TO_TXT_FILE "persons.txt"
/*The character who will be used to replace the space character*/
#define SPACE_REPLACEMENT   '$'

typedef struct
{
   char name[MAX_NAME_SIZE];
   int age;
   double salary;
   char email[MAX_EMAIL_SIZE];
}Person;

/*
 * Description:
 *  Replaces all occurrences of @replacedChar with @replacementChar
 * Parameters:
 *  string - the string where the replacement process will take place
 *  replacedChar - the character who will be replaced
 *  replacementChar - the character who will replace the @replacedChar
 * Returns:
 *  Nothing
 */
void ReplaceChar(char* string, char replacedChar, char replacementChar)
{
   int size = strlen(string);
   int i;
   for(i=0; i<size;i++)
   {
      if(string[i]==replacedChar)
      {
         string[i] = replacementChar;
      }
   }
}
/*
 * Description:
 *  Outputs to stdout the content of the Person vector
 * Parameters:
 *  p - a pointer to the the Person vector
 *  numberOfPersons - how many elements the vector has
 * Returns:
 *  Nothing
 */
void Persons_Print(Person *p, int numberOfPersons)
{
   int i;
   for(i = 0; i<numberOfPersons; i++)
   {
      printf("%s %d %f %s\n",p[i].name,p[i].age,p[i].salary,p[i].email);
   }
}
/*
 * Description:
 *  Saves in the text file represented by @stream the content of the
 *  person vector.
 * Parameters:
 *  p - a pointer to the Person vector
 *  numberOfPersons - how many elements the vector has
 *  stream - a pointer to the text file
 */
void Persons_Save(Person *p, int numberOfPersons, FILE* stream)
{
   int i;
   for(i = 0; i<numberOfPersons; i++)
   {
      /*Replaces all spaces in the name string in order to be
        able to load a person's name */
      ReplaceChar(p[i].name,' ',SPACE_REPLACEMENT);

      /*Writes the structure into the text file*/
      fprintf(stream, "%s %d %f %s\n",p[i].name,p[i].age,
                                      p[i].salary,p[i].email);

      /*Brings the name string back to normal*/
      ReplaceChar(p[i].name,SPACE_REPLACEMENT,' ');
   }
}
/*
 * Description:
 *  Loads in the Person vector the contents of the text file
 * Parameters:
 *  p - a pointer to the Person vector
 *  numberOfPersons - how many elements the vector has
 *  stream - a pointer to the text file
 * Returns:
 *  Nothing
 */
void Persons_Load(Person *p, int *numberOfPersons, FILE* stream)
{
   char buffer[BUFFER_SIZE];
   char *result = NULL;
   while(feof(stream)==0)
   {
      /*Reads a line of the file*/
      result = fgets(buffer,BUFFER_SIZE,stream);
      /*Checks if the line reading was successful*/
      if(result!=NULL)
      {
         /*Interprets the line of the text and adds a new entry in the struct
           vector*/
         sscanf(buffer,"%s %d %f %s",p[(*numberOfPersons)].name,
                                     &p[(*numberOfPersons)].age,
                                     &p[(*numberOfPersons)].salary,
                                     p[(*numberOfPersons)].email);
         /*Replaces the SPACE_REPLACEMENT character with space*/
         ReplaceChar(p[(*numberOfPersons)].name,SPACE_REPLACEMENT,' ');
         /*Increments the Person counter*/
         (*numberOfPersons)++;
      }
    }
}

int main(void)
{
   Person persons[MAX_PERSONS] =
   {
      {"Clark Kent", 30, 3250.5, "superman@krypton.com"},
      {"Peter Griffin", 40, 1250.0, "birdmaster@familyguy.com"},
      {"Stewie Griffin", 3, 50000.0, "world_domination@familyguy.com"},
      {"Eric Cartman", 12, 50000.0, "autoritah@southpark.com"},
      {"Zapp Brannigan", 30, 200.5, "stargeneral@futurama.com"},
   };
   Person personsCopy[MAX_PERSONS];
   int numberOfPersons = 0;
   FILE* stream = NULL;

   /*Opens the stream into <write text> mode*/
   stream = fopen(PATH_TO_TXT_FILE,"wt");
   if(stream!=NULL)
   {
      /*Save the struct vector into the text file*/
      Persons_Save(&persons,5,stream);
      /*Closes the stream*/
      fclose(stream);
      /*Reopens the stream into <read text> mode*/
      stream = fopen(PATH_TO_TXT_FILE,"rt");
      if(stream!=NULL)
      {
         /*Loads the text file into another struct vector*/
         Persons_Load(&personsCopy, &numberOfPersons, stream);
         /*Closes the stream*/
         fclose(stream);
         /*Prints the results*/
         Persons_Print(&personsCopy,numberOfPersons);
      }
   }
   return 0;
}
A problem arises when you want to read from the text file a string that contains spaces because int sscanf ( const char * str, const char * format, ...) will not interpret it as a single string.

To solve this problem you can replace all space characters with a uncommon character for that string(in this example, I used '$' because is very uncommon when it comes to names).

No comments:

Post a Comment

Got a question regarding something in the article? Leave me a comment and I will get back at you as soon as I can!

Related Posts Plugin for WordPress, Blogger...
Recommended Post Slide Out For Blogger