0

I have a Linux C program that store configuration parameters in a text file. I read from the text file using

FILE *file = fopen(filename, "r"):

and write to the file using following code

FILE *file = fopen(filename, "w"):

I am getting a problem where the text file is cleared, it is blank the next time I come to read it. I understand that when the file is opened for write it gets overwritten, my program stores the contents of the file after it has read it in and writes it back out. It does in the main write it out correctly, but occasionally I will find that the text file is blank.

My initial thoughts were that this may be because the program was unsafely stopped, mid way through writing to the file leaving it blank or there were 2 instances of the program running and as one open it for writing, the other reads it in, meaning it would read in a blank file and then overwrite it with a blank file when it writes it out. After some testing this does not seem to be the case.

This leaves me unsure as to what is causing the text file to be cleared. Does anyone have any ideas?

See code below

char text_lines[200][54]; /* global variable */

void read_in_text_file()
{

  /* this sub reads in the text file */

 //printf("read in file\n");

    /* declares the variables */
    char line[128];
    int counter = 0;
    int length;

/* open a text file and read it in line by line, storing each line in a variable. also returning a value for the number of lines in each section */

    static const char filename[] = "config.txt";
    FILE *file = fopen(filename,"r"); /* opens the config file */
if (file==NULL){ /* checks if the file has successfully opened */
  perror ("Error opening file"); /* displays error message on stderr - that returns reason file did not open */
  printf("Error opening file\n"); /* tells the user that the file has not opened */
  exit(0); /* exits the program if the text file can not be read in */
}
else{

  //printf("the file is open\n");

  while ( fgets ( line, sizeof line, file ) != NULL) /* reads each line of the text file */
  {
sprintf(text_lines[counter],"%s",line); /* puts the line into a variable */

length = zstrlen(text_lines[counter]); /* calculates the length of the text not including \r or \n characters */
if(text_lines[counter][length-1] == '\n') /* checks if the last character is \n (a new line character) */
{
 text_lines[counter][length-1] = '\0';  /* removes this new line character and replaces it with end of line identifier */
}

  counter = counter + 1; /* uses a counter for each line */

  } /* end of while loop */
        number_of_lines = counter; /* puts the number of lines into a integer variable */

  fclose(file); /* closes the file */
}
} /* end of sub for reading in the text file */

/* some changes may be made to the config before it is printed to the file again */

void print_to_text_file()
{

  pthread_mutex_lock(&lock); /* block until thread has ownership */

  /* sub for printing all the lines in the text_lines variable to the text file "config.txt" */
  int counter;
  static const char filename[] = "config.txt";
  FILE *file = fopen(filename,"w"); /* opens the config.txt file, with write privileges */
  if (file==NULL){ /* checks if the file has successfully opened */
      perror ("Error opening file"); /* displays error message on stderr - that returns reason file did not open */
      printf("Error opening file\n"); /* tells the user that the file has not opened */
    }
  else{
    //printf("the file is open\n"); /* prints to the terminal screen the file has opened */
for (counter = 0; counter < number_of_lines; counter++) /* uses a for loop to scroll through all text lines */
{
// printf("%s\n",text_lines[counter]);
  fprintf(file, "%s\n",text_lines[counter]); /* prints current text line to the file */
}

      fclose(file); /* closes the file */
    }

  pthread_mutex_unlock(&lock); /* release blocking on thread */

} /* end of print text to file sub */
josh
  • 11
  • 2
  • 5
  • 1
    Are you sure you tagged the correct language? – DonBoitnott Jan 23 '14 at 16:51
  • To be able to actually answer, you'd have to show some more code so that it could be seen what's actually getting done there. –  Jan 23 '14 at 16:55
  • 1
    Can we see your main code? Did you use fclose()? If you want to keep the original contents why not append to the file? – JMercer Jan 23 '14 at 16:55
  • Re-tagged to C. Side note: Josh, Welcome to SO. For SO posts try to avoid extra text unrelated to the problem including thanks. Feel free to discuss on [meta](http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be-removed-from-posts). Instead actively answer in comments/accept answers. – Alexei Levenkov Jan 23 '14 at 16:56
  • This is not a duplicate since @josh is not trying to append to the file. He is reading the contents of the file into a variable, modifying it, then writing it back out to the file. However, his program is flawed and seems to have some incorrect 2D array indexing, and is missing some array bounds checking. – Colin D Bennett Jan 30 '14 at 18:02

1 Answers1

3

First, you should know that when you open a file with fopen and a mode of w, the file is immediately truncated to zero bytes (erasing its contents). You can use w+ or wa to prevent this.

Second, you might be missing an fclose() to close the file when you're done with it. Although when your program exits, all files are typically closed. However, if you don't close the file, changes made to it might not be committed to disk.

Since you updated the question to include some code, I can say that your program is unsafe and can overrun the text_lines buffer if the file contains more than 200 lines. If you can provide an actual complete but minimal test program that can be compiled and run, it would help you to get more answers next time.

Here is an actual program that can be compiled and run. It reads in the lines of config.txt, converts all characters to capitals, then writes the lines back out to the file. I removed your pthread mutex function calls since it was unnecessary in this test program (there's no multithreading).

#include <ctype.h>      /* toupper */
#include <stdio.h>      /* fopen, fclose, fgets, perror */
#include <stdlib.h>     /* exit */
#include <string.h>     /* strlen */

/* Constants */

/* Maximum number of lines to read */
#define TEXT_LINES_CAPACITY 54
/* Maximum length of a line including EOL and NUL */
#define MAX_LINE_LEN 200

/* Global variables */
char text_lines[TEXT_LINES_CAPACITY][MAX_LINE_LEN];  /* text of lines */
int number_of_lines;  /* number of lines in text_lines */

/* Function declarations */
void capitalize_string(char* s);
void strip_eol(char* s);
void read_in_text_file(void);
void print_to_text_file(void);

/* Function definitions */

int main()
{
    int i;

    /* Read in the contents of the file. */
    read_in_text_file();

    /* Modify it by capitalizing the text. */
    for (i = 0; i < number_of_lines; ++i)
    {
        capitalize_string(text_lines[i]);
    }

    /* Write out the modified contents to the same file. */
    print_to_text_file();

    return 0;
}

void capitalize_string(char* s)
{
    while (*s != 0)
    {
        *s = toupper(*s);
        ++s;
    }
}

/* Open a text file and read it in line by line.  The lines are stored in the
 * global variable text_lines and the number of lines in number_of_lines. */
void read_in_text_file(void)
{
    static const char filename[] = "config.txt";
    FILE *file = fopen(filename,"r"); /* opens the config file */

    if (file == NULL)
    {
        /* Print error message after the file name. */
        perror(filename);
        exit(1);  /* Exit with failure code (nonzero) */
    }
    else
    {
        /* Read each line of the text file. */
        while (number_of_lines < TEXT_LINES_CAPACITY &&
                fgets(text_lines[number_of_lines], MAX_LINE_LEN, file) != NULL)
        {
            strip_eol(text_lines[number_of_lines]);
            ++number_of_lines;
        }

        fclose(file);
    }
}

/* Remove LF and/or CR characters from the end of the string. */
void strip_eol(char* s)
{
    /* Loop while the string ends with a CR or LF character. */
    while (strlen(s) > 0 &&
            (s[strlen(s) - 1] == '\n' ||
             s[strlen(s) - 1] == '\r'))
    {
        /* Erase the last character. */
        s[strlen(s) - 1] = '\0';
    }
}

/* Write all the lines of text_lines to the text file "config.txt" */
void print_to_text_file(void)
{
    static const char filename[] = "config.txt";
    /* open the config.txt file, with write privileges */
    FILE *file = fopen(filename,"w");

    if (file == NULL)
    {
        /* Print error message after the file name. */
        perror(filename);
    }
    else
    {
        int i;

        /* Iterate over all text lines. */
        for (i = 0; i < number_of_lines; i++)
        {
            fprintf(file, "%s\n", text_lines[i]); /* prints current text line to the file */
        }

        fclose(file); /* closes the file */
    }
}
Colin D Bennett
  • 11,294
  • 5
  • 49
  • 66
  • I am aware it gets erased, I write back to the file with the data read in. I am using an fclose(). I have added code to make it clearer. – josh Jan 30 '14 at 13:44
  • Thanks for adding the code. I've edited the answer to add a test program that you can use. – Colin D Bennett Jan 30 '14 at 18:07