0

I'm new in C programming and I am trying to open a .txt file for reading it.

I have a text file with different file names I want to read, each one in a different line. I created a function txt_to_stations() that reads this file and returns a double pointer to char, I guess it has to be a double pointer because I want to save a string of char strings. This is the function.

char** txt_to_stations(const char* txt_file) {

    FILE* file;
    file = fopen(txt_file, "r");
    int line_count = 0;
    int char_count = 0;
    int len = 1;
    char tmp_station[25];
    char** stations = (char**)malloc(len*sizeof(char*));
    char character;
    while(!feof(file)) {
        character = fgetc(file);
        if(character != '\n') {
            tmp_estation[char_count] = character;
            char_count++;
        }else if(character == '\n') {
            stations = (char**)realloc(stations, len*sizeof(char*));
            stations[line_count] = (char*)malloc(char_count*sizeof(char));
            strcpy(stations[line_count], tmp_station);
            len++;
            line_count++;
            char_count = 0;
        }
    }
    fclose(file);
    return stations;
}

My text file is this one. "stations.txt"

weatherdata-429-81.csv
weatherdata-429-84.csv
weatherdata-429-88.csv

The problem comes when I try from the main function to read this files. The function works great because if I char** stations = txt_to_stations("stations.txt") and then for example printf("station1: %s\n", stations[0]) it prints weatherdata-429-81.csv in the terminal.

But if I define a new file in main function

FILE* reading;
reading = fopen(stations[0]);
if(reading == NULL) {
    printf("csv file cant be opened");
}

It prints "csv file cant be opened", which means fopen(stations[0]) == NULL, but it does not because if I simply change stations[0] by fopen("weatherdata-429-81.csv") it works. It may be a rookie error, but I understand that stations[0] == weatherdata-429-81.csv (as char*)

I really tried converting stations[0](char*) to a const char*, and also in "stations.txt" writing each name into double quotes, but anyway it did not work at all. How can I fix this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dacalite
  • 1
  • 1
  • 1
    I suggest you print out the values of `stations` inside some quotation marks, to make sure that they really are what you expect them to be. And also take this as the perfect time to learn how to use a *debugger* to step through your code statement by statement. Lastly, the problem is the file-name in `stations[0]`, not the difference between `char *` and `const char *`. – Some programmer dude Oct 28 '21 at 14:08
  • 1
    dacalite, Who or what text suggested using `feof()`? – chux - Reinstate Monica Oct 28 '21 at 14:18
  • @Someprogrammerdude thanks! I will try with NetBeans debugger – dacalite Oct 28 '21 at 14:19
  • @chux-ReinstateMonica feof (FILE* reading_file) returns 0 when it iterates the EOF (end of file) character, so it is supposes to go until the end of my text file – dacalite Oct 28 '21 at 14:31
  • 2
    [Why is “while ( !feof (file) )” always wrong?](https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) . Also strongly consider [fgets](https://en.cppreference.com/w/c/io/fgets). There is no point in reading character by character. – bolov Oct 28 '21 at 14:32
  • 1
    dacalite, I am aware of what `feof()` does. My [question](https://stackoverflow.com/questions/69755574/issue-opening-a-double-pointer-char-element-for-reading-it-as-text-file?noredirect=1#comment123301153_69755574) was about the origin of your use. Who or what text suggested using `feof()`? Knowing that helps in making a good answer for you. – chux - Reinstate Monica Oct 28 '21 at 14:51
  • @chux-ReinstateMonica oh srry I had misunderstood. I have the stations.txt file with csv files to read from main function, so I thought it would be a good idea to create that txt_to_stations function that returns me an array in which element is a different .csv file. Then if you asked about that, I want to read each csv file separately in a for loop and save parameters into structures, one for each station, and that second part is already working, if I simply pass it the csv file by writing it into double quotes, it returns me that structure I want and I can acess perfectly to any element – dacalite Oct 28 '21 at 15:10
  • 1
    @dacalite Sigh, my question was about `feof()` usage. Good luck. – chux - Reinstate Monica Oct 28 '21 at 15:24
  • 1
    @chux-ReinstateMonica : it was probably "teach youself bad C in zero days" By Herb Schildt... – wildplasser Oct 28 '21 at 16:16

1 Answers1

0

I ran your code through a debugger, and found some mistakes. I commented them(and some other points) out for you.

char** txt_to_stations(const char* txt_file)
{
    FILE* file;
    file = fopen(txt_file, "r");
    if(file == NULL)  // You forgot to error-check file. fopen is one
                      // of the most likely functions to return NULL,
                      // so you really can't forget that
    {
        printf("Error opening file.");
        exit(0); // Exits your program, regardless of where
                 // you are, defined in stdlib.h
    }

    int line_count = 0;
    int char_count = 0;
    int len = 2;   // I made len slightly bigger, nothing with a
                   // little bit of buffer and also allows you
                   // to keep space for the NULL terminator

    char tmp_station[25] = {0}; // It is always a good idea to zero out
                                // an array you're creating. While testing
                                // I found some stray garbage characters
                                // at the end of one of the strings...

    char** stations = (char**) malloc(len * sizeof(char*));

    char character;

    while (1) // This should run forever and you should break out
              // of this loop when you reach the end of the file
    {
        // This is one way to properly find the end of a file.
        // Don't put feof call inside the while condition
        if (feof(file))
            break;

        // fgets() would save you a lot of time, but oh well,
        // I don't want to change your code too much...
        character = fgetc(file);
        if(character != '\n')
        {
            tmp_station[char_count] = character;
            char_count++;
        }
        else // Removed the if here because it was redundant... else
             // it means that it must be a newline character
        {
            tmp_station[char_count] = '\0';  // You forgot to null-terminate
                                             // the string you took into
                                             // tmp_station. This is the
                                             // main reason the function
                                             // wasn't working
            stations = (char**) realloc(stations, len*sizeof(char*));
            stations[line_count] = (char*) malloc(char_count*sizeof(char));
            strcpy(stations[line_count], tmp_station);
            len++;
            line_count++;
            char_count = 0;

            // It's a good idea to terminate an array of pointers
            // with NULL. How will you know you reached the end
            // of the array?
            stations[line_count] = NULL;
        }
    }
    fclose(file);
    return stations;
}

There's a tiny little problem with your code as of now. See, because you only import a line into the array of pointers if and only if a newline is found, that means if the last line of your stations.txt file does not have a newline, that line will not get imported into the array. There are two solutions.

  1. Add an extra newline at the end of your text file like this
weatherdata-429-81.csv
weatherdata-429-84.csv
weatherdata-429-88.csv
  1. Change the code so you can import the last line regardless of whether it has a newline character or not.

  2. Use fgets(). It will keep reading the entire file until it reaches the end of the file, so you can store whatever it returns inside an array and manually remove the newline characters that it generates yourself.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131