2017-02-10 13 views
3

Я пытаюсь прочитать строки и целые числа из простого текстового файла в мой массив. Но проблема в том, что я получаю некоторые случайные символы в строке в середине моего списка. Вероятно, это связано с проблемой новой строки, но я не уверен. Текстовый файл выглядит следующим образом:Чтение строк из файла

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

первый номер (4), указывает на то, сколько песен есть в списке. Я создал структуру, которая сможет удерживать песни и динамически распределять память для каждого указателя. Struct:

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

Это неправильно:

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 и сохраните значение там напрямую.

+0

Okey спасибо. Это сработало! – Henke

1
  • Первый выпуск:

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

    ли выделяет только 1 байт для char* указателей, song и artist. Можно выделить размер для этого:

    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 
+0

Ничего себе, спасибо за этот подробный ответ: D Я внесу некоторые изменения – Henke

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; 
    } 
} 
Смежные вопросы