分享
三行代码  ›  专栏  ›  技术社区  ›  MTRNS

在C语言中将文件逐行读入字符串数组

  •  1
  • MTRNS  · 技术社区  · 1 周前

    我试图将以下文件逐行读入字符串数组,其中每行都是数组的一个元素:

    AATGC
    ATGCC
    GCCGT
    CGTAC
    GTACG
    TACGT
    ACGTA
    CGTAC
    GTACG
    TACGA
    ACGAA
    
    

    我的代码如下:

    void **get_genome(char *filename) {
        FILE *file = fopen(filename, "r");
        int c;
        int line_count = 0;
        int line_length = 0;
        for (c = getc(file); c != EOF; c = getc(file)) {
            if (c == '\n') line_count++;
            else line_length++;
        }
        line_length /= line_count;
        rewind(file);
    
        char **genome = calloc(line_length * line_count, sizeof(char));
        for (int i = 0; i < line_count; i++) {
            genome[i] = calloc(line_length, sizeof(char));
            fscanf(file, "%s\n", genome[i]);
        }
    
        printf("%d lines of %d length\n", line_count, line_length);
    
        for (int i = 0; i < line_count; i++)
            printf("%s\n", genome[i]);
    }
    

    但是,由于某些原因,我得到了数组前2个元素的垃圾输出。以下是我的输出:

    `NP��
    �NP��
    GCCGT
    CGTAC
    GTACG
    TACGT
    ACGTA
    CGTAC
    GTACG
    TACGA
    ACGAA
    
    
    2 回复  |  直到 1 周前
        1
  •  2
  •   chqrlie    1 周前

    你似乎认为所有的线都有相同的长度。如果是这样的话,你仍然有一些问题:

    • 行指针的内存分配不正确,应该是

        char **genome = calloc(line_count, sizeof(char *));
      

    或者更好、更少出错:

        char **genome = calloc(line_count, sizeof(*genome));
    
    • 每行的内存应该比null结束符长一个字节。

    • \n fscanf() 格式字符串匹配任何空格字符序列。它是多余的,因为 %s

    • 如果文件中包含任何空白字符,则用空格分隔的项目计数更安全,以避免计算错误。

    • 你不能关门 file .

    • 您不返回 genome

    • 您不检查错误。

    void **get_genome(const char *filename) {
        FILE *file = fopen(filename, "r");
        if (file == NULL)
            return NULL;
        int line_count = 1;
        int item_count = 0;
        int item_length = -1;
        int length = 0;
        int c;
        while ((c = getc(file)) != EOF) {
            if (isspace(c)) {
                if (length == 0)
                    continue;  // ignore subsequent whitespace
                item_count++;
                if (item_length < 0) {
                    item_length = length;
                } else
                if (item_length != length) {
                    printf("inconsistent item length on line %d\", line_count);
                    fclose(file);
                    return NULL;
                }
                length = 0;
            } else {   
                length++;
            }
        }
        if (length) {
            printf("line %d truncated\n", line_count);
            fclose(file);
            return NULL;
        }
        rewind(file);
    
        char **genome = calloc(item_count, sizeof(*genome));
        if (genome == NULL) {
            printf("out of memory\n");
            fclose(file);
            return NULL;
        }
        for (int i = 0; i < item_count; i++) {
            genome[i] = calloc(item_length + 1, sizeof(*genome[i]));
            if (genome[i] == NULL) {
                while (i > 0) {
                    free(genome[i]);
                }
                free(genome);
                printf("out of memory\n");
                fclose(file);
                return NULL;
            }
            fscanf(file, "%s", genome[i]);
        }
        fclose(file);
    
        printf("%d items of %d length on %d lines\n",
               item_count, item_length, line_count);
    
        for (int i = 0; i < item_count; i++)
            printf("%s\n", genome[i]);
    
        return genome;
    }
    
        2
  •  1
  •   bruno    1 周前
     char **genome = calloc(line_length * line_count, sizeof(char));
    

    必须是

    char **genome = calloc(line_count, sizeof(char*));
    

    或更“安全”

    char **genome = calloc(line_count, sizeof(*genome));
    

    基因组

    否则,如果在64b中,则分配的块不够长,因为 行数 是5而不是8,所以您用未定义的行为写出它

    你还需要回去 在函数末尾

    也可以不计算行数,使用 realloc 读取文件时递增数组

        3
  •  0
  •   P__J__    1 周前

    我看这两条线的长度是一样的。你的函数应该通知调用者已经读了多少行。这个文件不需要读两遍。没有必要 calloc (哪个功能更昂贵)。始终检查内存分配函数的结果。

    char **get_genome(char *filename, size_t *line_count) {
        FILE *file = fopen(filename, "r");
        int c;
        size_t line_length = 0;
        char **genome = NULL, **tmp;
    
        *line_count = 0;
        if(file)
        {
            while(1)
            {
                c = getc(file);
                if( c == EOF || c == '\n') break;
                line_length++;
            }    
            rewind(file);
    
            while(1)
            {
                char *line = malloc(line_length + 1);
                if(line)
                {
                    if(!fgets(line, line_length + 1, file))
                    {
                        free(line);
                        break;
                    }
                    line[line_length] = 0;
                    tmp = realloc(genome, (*line_count + 1) * sizeof(*genome));
                    if(tmp)
                    {
                        genome = tmp;
                        genome[*line_count] = line;
                        *line_count += 1;
                    }
                    else
                    {
                        // do some memory free magic
                    }
                }
            }
            fclose(file);
        }
        return genome;
    }