【问题标题】:Reading lines from file从文件中读取行
【发布时间】:2017-06-28 19:01:31
【问题描述】:

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

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

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

【问题讨论】:

    标签: c arrays file dynamic struct


    【解决方案1】:

    这是错误的:

    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 并将值直接存储在那里。

    【讨论】:

      【解决方案2】:
      • 第一期:

        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
      

      【讨论】:

      • 哇,谢谢你的详细回答:D 我会做一些改变
      【解决方案3】:

      您的内存分配不正确。该结构应该有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;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-10-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-10
        • 1970-01-01
        相关资源
        最近更新 更多