2017-02-10 105 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)表示列表中有多少首歌曲。我做了一個結構,它能夠保存歌曲併爲每個指針動態地分配內存。 結構:

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

奧基謝謝你。這工作! – Henke

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 
+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; 
    } 
}