从文件

时间:2017-02-10 12:18:28

标签: c arrays file dynamic struct

我正在尝试从简单的文本文件中读取字符串和整数到我的数组。但问题是我在列表中间的一行中得到了一些随机字符。它可能与换行问题有关,但我不确定。文本文件如下所示:

4
Mr Tambourine Man
Bob Dylan
1965
Dead Ringer for Love
Meat Loaf
1981
Euphoria
Loreen
2012
Love Me Now
John Legend
2016

第一个数字(4)表示列表中有多少首歌曲。我已经制作了一个结构,它能够保存歌曲并为每个指针动态分配内存。 的结构:

typedef struct Song {
    char *song;
    char *artist;
    int *year;
} Song;

分配:

Song *arr;
arr = (Song*)malloc(sizeof(Song));

功能:

int loadFile(char fileName[], Song *arr, int nrOf) {

    FILE *input = fopen(fileName, "r");

    if (input == NULL) {
        printf("Error, the file could not load!\n");
    } else {
        int i = 0;
        fscanf(input, "%d\n", &nrOf);

        for (int i = 0; i < nrOf; i++) {
            arr[i].song = (char*)malloc(sizeof(char));
            arr[i].artist = (char*)malloc(sizeof(char));
            arr[i].year = (int*)malloc(sizeof(int));
            fgets(arr[i].song, 100, input);
            fgets(arr[i].artist, 100, input);
            fscanf(input, "%d\n", arr[i].year);
        }
        printf("The file is now ready.\n");
        fclose(input);
    }

    return nrOf;
}

你能找到问题吗?或者你有更好的解决方案吗?

3 个答案:

答案 0 :(得分:3)

这是错误的:

arr[i].song = (char*)malloc(sizeof(char));
arr[i].artist = (char*)malloc(sizeof(char));

您只分配大小为1的缓冲区,没有缩放。当您通过向缓冲区中加载比它们可以容纳的更多数据而超出缓冲区时,这会给您带来未定义的行为。

我希望那些人阅读:

arr[i].song = malloc(100);

等等。请注意,不需要强制转换,sizeof (char)始终为1。

另外,这个:

arr[i].year = (int*)malloc(sizeof(int));

非常奇怪。绝对没有理由动态分配单个整数,只需将字段设为int并将值直接存储在那里。

答案 1 :(得分:1)

  • 第一期:

    arr[i].song = (char*)malloc(sizeof(char));
    arr[i].artist = (char*)malloc(sizeof(char));
    

    仅为char*指针songartist分配1个字节。您可以为此分配大小:

    arr[i].song = (char*)malloc(100 * sizeof(char)); /* or malloc(100) */
    arr[i].artist = (char*)malloc(100 * sizeof(char));
    

    或者你可以从缓冲区中malloc()获得足够的空间:

    char buffer[100];
    fgets(buffer, 100, input);
    /* check for failure, remove newline */
    arr[i].song = malloc(strlen(buffer)+1);
    /* check error from malloc */
    strcpy(arr[i].song, buffer);
    

    甚至使用strdup()

    arr[i].song = strdup(buffer);
    

    可替代malloc() / strcpy()

    注意:您还可以阅读Do I cast the result of malloc?

  • 第二期:

    您当前的struct

    typedef struct Song {
        char *song;
        char *artist;
        int *year;
    } Song;
    

    可以简化为:

    typedef struct {
        char *song;
        char *artist;
        int year;
    } Song;
    

    因为year不需要是指针。如果只是int,则更容易管理。这避免了必须进行如下分配:

    arr[i].year = (int*)malloc(sizeof(int));
    
  • 其他建议: 您应该检查fscanf()fgets()的返回是否安全。它只会让您的文件包含不正确的数据。这与malloc()相同,可以返回NULL在堆上分配失败。

以下是一些考虑了上述注意事项的代码:

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

#define SIZE 100

typedef struct {
    char *song;
    char *artist;
    int year;
} Song;

Song *create_array(FILE *input, int *nrof);
void load_data(Song *arr, FILE *input, int nrof);
void print_free_data(Song *arr, int nrof);
void get_buffer(char buffer[], FILE *input);

int main(void) {
    FILE *input;
    Song *arr;
    int nrof;

    input = fopen("artist.txt", "r");
    if (input == NULL) {
        fprintf(stderr, "Error reading file\n");
        exit(EXIT_FAILURE);
    }

    arr = create_array(input, &nrof);

    load_data(arr, input, nrof);

    print_free_data(arr, nrof);

    fclose(input);

    return 0;
}

Song *create_array(FILE *input, int *nrof) {
    Song *arr;

    if (fscanf(input, "%d ", nrof) != 1) {
        fprintf(stderr, "Cannot find number of songs\n");
        exit(EXIT_FAILURE);
    }

    arr = malloc(*nrof * sizeof(*arr));
    if (arr == NULL) {
        fprintf(stderr, "Cannot allocate %d spaces for array\n", *nrof);
        exit(EXIT_FAILURE);
    }

    return arr;
}

void load_data(Song *arr, FILE *input, int nrof) {
    char buffer[SIZE];

    for (int i = 0; i < nrof; i++) {
        get_buffer(buffer, input);
        arr[i].song = malloc(strlen(buffer)+1);
        if (arr[i].song == NULL) {
            fprintf(stderr, "Cannot allocate song\n");
            exit(EXIT_FAILURE);
        }
        strcpy(arr[i].song, buffer);

        get_buffer(buffer, input);
        arr[i].artist = malloc(strlen(buffer)+1);
        if (arr[i].artist == NULL) {
            fprintf(stderr, "Cannot allocate artist\n");
            exit(EXIT_FAILURE);
        }
        strcpy(arr[i].artist, buffer);

        if (fscanf(input, "%d ", &arr[i].year) != 1) {
            fprintf(stderr, "Cannot find year for Song: %s Album: %s\n", 
                             arr[i].song, arr[i].artist);
            exit(EXIT_FAILURE);
        }
    }
}

void get_buffer(char buffer[], FILE *input) {
    size_t slen;

    if (fgets(buffer, SIZE, input) == NULL) {
        fprintf(stderr, "Error from fgets(), line not read\n");
        exit(EXIT_FAILURE);
    }

    slen = strlen(buffer);
    if (slen > 0 && buffer[slen-1] == '\n') {
        buffer[slen-1] = '\0';
    } else {
        fprintf(stderr, "Too many characters entered\n");
        exit(EXIT_FAILURE);
    }
}

void print_free_data(Song *arr, int nrof) {
    for (int i = 0; i < nrof; i++) {
        printf("%s\n%s\n%d\n\n", arr[i].song, arr[i].artist, arr[i].year);

        free(arr[i].song);
        arr[i].song = NULL;

        free(arr[i].artist);
        arr[i].artist = NULL;
    }

    free(arr);
    arr = NULL;
}

哪个输出正确的数据:

Mr Tambourine Man
Bob Dylan
1965

Dead Ringer for Love
Meat Loaf
1981

Euphoria
Loreen
2012

Love Me Now
John Legend
2016

答案 2 :(得分:1)

您的内存分配不正确。该结构应该有歌曲和艺术家名称的char数组以及年份的int,您应该修改您的API以将数组及其大小返回给调用者:

int loadFile(const char *fileName, Song **arr, int *numberp);

以下是您的计划的更正和简化:

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

typedef struct Song {
    char song[100];
    char artist[100];
    int year;
} Song;

/* call as
   if (loadFile(fileName, &songs, &songs_size) < 0) {
       // deal with error...
   }
*/

int loadFile(const char *fileName, Song **arrp, int *numberp) {
    FILE *input;
    Song *arr;
    int i, nrOf;

    input = fopen(fileName, "r");
    if (input == NULL) {
        fprintf(stderr, "Cannot open file %s\n", filename);
        return -1;
    } else {
        if (fscanf(input, "%d\n", &nrOf) != 1) {
            fprintf(stderr, "%s: missing number of items\n", filename);
            fclose(intput);
            return -1;
        }
        arr = calloc(sizeof(*arr), nrOf);
        if (arr == NULL) {
            fprintf(stderr, "cannot allocate memory for %d items\n", nrOf);
            fclose(intput);
            return -1;
        }    
        for (int i = 0; i < nrOf; i++) {
            char cc;
            if (fscanf(input, "%99[^\n]%*c%99[^\n]%*c%d%c",
                       sarr[i].song, arr[i].artist,
                       &arr[i].year, &cc) != 4 || cc != '\n') {
                fprintf(stderr, "%s: invalid format for item %d\n",
                        filename, i);
                break;
            }
        }
        printf("The file is now ready.\n");
        fclose(input);
        *arrp = arr;
        *numberp = i;
        return i;
    }
}