-1

I'm trying to save the element of a file into an array of structs.

I want to dynamically increase the memory allocated in the array to fit just the total amount of lines (structs) in my file.

The .txt file looks like this:

R&B;6jvvpPJQJy5rMOEkLlADl6;Trey Songz
R&B;6p8JTP4A9NZHtxRVhkEo6s;T-Pain
R&B;3SwhPTNNU5hpF33bbCsji6;BJ The Chicago Kid
R&B;5yvhdo8FXbBsIllxv2Rr94;SZA
R&B;5Yq38evNk28qlTVHHtwBhT;James Blake
R&B;5yamjs92dcayRVlNuY116G;H.E.R.
R&B;4FjcZsKyGhhZnuYq0nzXpZ;T-Pain
R&B;22jnEneSABg4vRCR1vow7F;Daniel Caesar
R&B;57qiTKh8bVX0VtfUNTQqhw;The Weeknd

This is currently my code. (songFile and songsList does the same thing as genresList but is currently not implemented)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef struct genresFile{
    char* genre;
    char* idSong;
    char* artist;
} genresFile;

typedef struct songsFile{
    char* idSong;
    int popularity;
    char* mode;
} songsFile;


int main(int argc, char *argv[])
{
    FILE* songs = fopen("songs.txt", "r");
    FILE* genres = fopen("genres.txt", "r");
    
    genresFile* genresList = NULL;
    //songsFile* songsList = NULL;

    char *buf = malloc(256);
    char *tmp;

    int i = 0;
    while (fgets(buf, 255, genres)){      
        genresList = realloc(genresList, (i+1)*sizeof(genresFile));
        if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
            buf[strlen (buf) - 1] = '\0';       
        genresFile object;
        tmp = strtok(buf, ";");
        object.genre = tmp;

        tmp = strtok(NULL, ";");
        object.idSong = tmp;

        tmp = strtok(NULL, ";");
        object.artist = tmp;

        genresList[i] = object;
        i++;
    }
    
    printf("%s, %s, %s\n",genresList[3].genre, genresList[3].idSong, genresList[3].artist);

    
    free(genresList);
    //free(songsList);
    fclose(songs);
    fclose(genres);
    return 0;
}

This is the output I get from the print at the end:

Soul, qWZdkBl4UVPj9lK6HuuFM, r Thomas & The Volcanos

This is the last line of the .txt file and the artist's name is missing a letter at the beginning.

This is getting saved in every element of the array. Please help :D

Rodrigo Guzman
  • 487
  • 2
  • 9
  • Are the field widths within the file managed? for example, is there a maximum character count for each field. If so, it would greatly simplify your solution by limiting the dynamic memory allocation to grow proportionally to only lines in file. Otherwise, you will be allocating and freeing memory for way too many things. – ryyker Apr 30 '21 at 23:18

1 Answers1

0

Assigning pointers doesn't mean to copy strings.

The buffer buf is overwritten in the following lines, so what are pointed at by the pointers are also changed.

To avoid this, you should copy the strings instead of just assigning pointers.

It can be done like this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef struct genresFile{
    char* genre;
    char* idSong;
    char* artist;
} genresFile;

typedef struct songsFile{
    char* idSong;
    int popularity;
    char* mode;
} songsFile;

char* copy_string(const char* str) {
    char* copy = malloc(strlen(str) + 1); /* +1 for terminating null-character */
    if (copy != NULL) strcpy(copy, str);
    return copy;
}

int main(int argc, char *argv[])
{
    FILE* songs = fopen("songs.txt", "r");
    FILE* genres = fopen("genres.txt", "r");
    
    genresFile* genresList = NULL;
    //songsFile* songsList = NULL;

    char *buf = malloc(256);
    char *tmp;

    int i = 0;
    while (fgets(buf, 255, genres)){      
        genresList = realloc(genresList, (i+1)*sizeof(genresFile));
        if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
            buf[strlen (buf) - 1] = '\0';       
        genresFile object;
        tmp = strtok(buf, ";");
        object.genre = copy_string(tmp);

        tmp = strtok(NULL, ";");
        object.idSong = copy_string(tmp);

        tmp = strtok(NULL, ";");
        object.artist = copy_string(tmp);

        genresList[i] = object;
        i++;
    }
    
    printf("%s, %s, %s\n",genresList[3].genre, genresList[3].idSong, genresList[3].artist);

    
    free(genresList);
    //free(songsList);
    fclose(songs);
    fclose(genres);
    return 0;
}

You can use strdup() instead of this copy_string() if it is supported in your (target) environment.

MikeCAT
  • 73,922
  • 11
  • 45
  • 70