Question

This question might be common but I did not find anything close to this exact problem so I will ask. I have a file that looks like this.

ABCSTRX
ABCST
ABCS
ABCSTRXY

My task is to create 2D array of strings. For each string it allocates only needed space (length of string), so it will look like this.

the red area is for '\0'

So far in my code I have scanned through file and got number representing the amount of strings in file.

void function(FILE *txt){
    char arr[50];
    int counter=0;
    char **array;

    do {                                    
       fgets(arr,MAX,txt);
       counter++;
    } while(fgets(arr,MAX,txt)!=NULL);

    rewind(txt);
    array = malloc(counter * sizeof(char*));


    //FROM NOW ON THE CODE IS NOT CORRECT
    do {                                    
        fgets(arr,MAX,txt);
        counter++;

        for(int j=0;j<strlen(arr); j++){
            array[i]= malloc(strlen(arr) * sizeof(char));
            array[i]= arr;
        }
        i++;
    } while(fgets(arr,MAX,txt)!=NULL);

}

Problem is that I cant find a solution how to fill my dynamically allocated array with strings which size does not match. This is the code that I have come up with and I feel stuck. After the array is filled with strings it will be printed(with two for cycles I suppose). Thanks for comments

Was it helpful?

Solution

You may want to consider the following commented compilable C code.
Instead of writing some notes here, I've inserted several comments inline in the code, to try to clearly explain what the code is doing.

#include <stdio.h>      /* For printf and file management */
#include <stdlib.h>     /* For dynamic memory allocation */
#include <string.h>     /* For string functions */

/* 
 * Read all lines from text file, and store them in a dynamically 
 * allocated array. 
 * The count of lines is stored in the 'count' output parameter
 * The caller is responsible for freeing the allocated memory.
 */
char** read_lines(FILE* txt, int* count) {
    char** array = NULL;        /* Array of lines */
    int    i;                   /* Loop counter */
    char   line[100];           /* Buffer to read each line */
    int    line_count;          /* Total number of lines */
    int    line_length;         /* Length of a single line */

    /* Clear output parameter. */
    *count = 0;

    /* Get the count of lines in the file */
    line_count = 0;
    while (fgets(line, sizeof(line), txt) != NULL) {                                      
       line_count++;
    }

    /* Move to the beginning of file. */
    rewind(txt);

    /* Allocate an array of pointers to strings 
     * (one item per line). */
    array = malloc(line_count * sizeof(char *));
    if (array == NULL) {
        return NULL; /* Error */
    }

    /* Read each line from file and deep-copy in the array. */
    for (i = 0; i < line_count; i++) {    
        /* Read the current line. */
        fgets(line, sizeof(line), txt);

        /* Remove the ending '\n' from the read line. */
        line_length = strlen(line);        
        line[line_length - 1] = '\0';
        line_length--; /* update line length */

        /* Allocate space to store a copy of the line. +1 for NUL terminator */
        array[i] = malloc(line_length + 1);

        /* Copy the line into the newly allocated space. */
        strcpy(array[i], line);
    }

    /* Write output param */
    *count = line_count;

    /* Return the array of lines */
    return array;
}

int main(int argc, char * argv[]) {
    char**      array    = NULL;    /* Array of read lines */
    FILE*       file     = NULL;    /* File to read lines from */
    const char* filename = NULL;    /* Name of the input file */
    int         i;                  /* Loop index */
    int         line_count;         /* Total number of read lines *7

    /* Get filename from the command line. */
    if (argc != 2) {
        printf("Specify the input file from the command line.\n");
        return 1;
    }    
    filename = argv[1];

    /* Try opening the file. */
    file = fopen(filename, "rt");
    if (file == NULL) {
        printf("Can't open file %s.\n", filename);
        return 1;
    }

    /* Read lines from file. */
    array = read_lines(file, &line_count);

    /* Just for test, print out the read lines. */
    for (i = 0; i < line_count; i++) {
        printf("[%d]: %s\n", (i+1), array[i]);
    }

    /* Cleanup. */
    fclose(file);
    for (i = 0; i < line_count; i++) {
        free(array[i]);
    }
    free(array);
    array = NULL;

    /* All right */
    return 0;
}

With an input file like this:

ABCSTRX
ABCST
ABCS
ABCSTRXY

The output is:

C:\Temp>test.exe testfile.txt
[1]: ABCSTRX
[2]: ABCST
[3]: ABCS
[4]: ABCSTRX

OTHER TIPS

Your do while loop to count the number of lines in the file won't work because you are calling fgets twice in each iteration- once in the body of the loop and the next time to check the condition - while updating counter only once.

Also, strlen does not count the terminating null byte but you have to allocate space for it. Here is my update to your function.

void function(FILE *txt) {
    char arr[50];
    int counter = 0;
    char **array;

    while(fgets(arr, sizeof arr, txt)!=NULL)                                          
       counter++;

    rewind(txt);
    array = malloc(counter * sizeof *array);

    for(int i = 0; i < counter; i++) {
        fgets(arr, sizeof arr, txt);
        array[i] = malloc(strlen(a) + 1); // +1 for the terminating null byte
        strcpy(array[i], arr);  // copy the string to the buffer
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top