C读取整行文件

时间:2016-08-04 18:40:48

标签: c fopen fgets

我正在尝试使用 C 编写工具。该程序的一部分是使用文本文件并逐行读取,同时将所有行存储到数组中以供将来使用。

到目前为止,这就是我所拥有的:

int main(){
    FILE *fp = fopen("file.txt", "ab+");
    if (fp == NULL) {
        printf("FILE ERROR");
        return 1;
    }

    int lines = 0;
    int ch = 0;

    while(!feof(fp)){
        ch = fgetc(fp);
        if(ch == '\n'){
        lines++;
        }
    }

    printf("%d\n", lines);
    if (lines>0){
        int i = 0;
        int numProgs = 0;
        char* programs[lines];
        char line[lines];
        FILE *file;
        file = fopen("file.txt", "r");
        while(fgets(line, sizeof(line), file) != NULL){
        programs[i] = strdup(line);
        i++;
        numProgs++;
    }
    for (int j= 0; j<sizeof(programs); j++){
        printf("%s\n", programs[j]);
    } 
    fclose(file);
    fclose(fp);
    return 0;
}

我的问题是我得到这个输出:

6(文件中的行数) Segmentation fault

如何在不知道行开头多长时间的情况下逐行阅读。在PHP中我可以很容易地做到这一点,但我怎么能在 C

中做到这一点

感谢任何提示!

4 个答案:

答案 0 :(得分:1)

<强> Try Online

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

char * readLine (FILE * file)
{
    size_t len = 0;
    int c = 0, i = 0;
    long pos = ftell(file);
    char * out = 0;

    // read the whole line
    do { c = fgetc(file); len++; }
    while (c!='\0' && c!='\n' && c!=EOF);

    // if the cursor didn't move return NULL
    if (pos == ftell(file) && c == EOF) return 0;

    // allocate required memory
    out = (char*)malloc(len+1);

    // rewind cursor to beginning of line
    fseek (file, pos, SEEK_SET);

    // copy the line
    do { out[i++] = fgetc(file); }
    while (c!='\0' && c!='\n' && c!=EOF);

    // make sure there's \0 at the end
    out[i] = '\0';

    return out;
}

答案 1 :(得分:1)

像这样解决:

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

int main(void){
    FILE *fp = fopen("file.txt", "r");//!
    if (fp == NULL) {
        fprintf(stderr, "FILE ERROR\n");
        return 1;
    }

    int lines = 0;
    int ch = 0;
    int len = 0;//! length of line
    int max_len = 0;//! max length of line

    while((ch = fgetc(fp))!=EOF){//!
        ++len;
        if(ch == '\n'){
            if(max_len < len)
                max_len = len;
            ++lines;
            len = 0;
        }
    }
    if(len)
        ++lines;

    fprintf(stderr, "%d lines.\n", lines);

    if (lines > 0){
        int numProgs = 0;
        char *programs[lines];//use malloc, char **programs = malloc(lines * sizeof(*programs));
        char line[max_len+1];//!

        rewind(fp);//!
        while(fgets(line, sizeof(line), fp))
            programs[numProgs++] = strdup(line);//!

        for (int j= 0; j < numProgs; j++){//!
            printf("%s", programs[j]);//!
            free(programs[j]);//!
        } 
    }
    fclose(fp);

    return 0;
}

答案 2 :(得分:1)

如果您真的想从未知行数中读取未知数量的字符并将这些行存储在数组中(或者实际上,在创建的对象中)从指针到指针到字符),你有很多选择。 POSIX getline是一个面向行的输入函数(如fgets),它将在每次调用时从给定文件中读取一行文本,并将分配足够的存储空间无论长度如何都要保持线。 (作为奖励 getline返回实际的字符数读取,如果需要长度则取消后续调用strlen

getline无需重复检查fgets是否实际读取整行,或只是部分读取。此外,如果您的行长度超过几个字符,getline(和fgets)提供的缓冲读取比面向字符的输入要快得多(例如{ {1}})。不要误解我的意思,fgetc没有任何问题,如果你的档案很小而你的线路很短,你就不会发现任何差别。但是,如果你正在读每百万行500,000个字符 - 你会注意到一个显着的差异。

对于一个数组,因为你不知道你会读多少行,所以你真的需要一个指向指针到char的指针(例如 double-ponter fgetc)所以你可以分配一些合理数量的指针来开始,分配并将行分配给单个指针,直到达到你的限制,然后char **array数组增加数量指针可用,并继续阅读/存储线。

与动态分配内存的任何代码一样,你的must(1)保留指向每个分配的内存块的指针,因此(2)内存可以在不再使用的情况下被释放。您还应该验证每个分配(和重新分配)以确保分配成功。使用realloc时,请始终使用临时指针,以便在将新块指定给原始指针之前验证realloc是否成功。如果不这样做,并且realloc失败,那么您丢失了指向原始内存块的指针,该内存块未被触及,未被释放,并且您刚刚创建了内存泄漏。

最后,始终使用内存错误检查程序验证内存使用情况,例如Linux上的realloc。有许多微妙的方法可以滥用一块内存。

将所有这些放在一起,您可以执行以下操作。代码将读取作为第一个参数提供的文件名中的所有行(如果没有给出文件名,则从valgrind读取):

stdin

示例使用/输出

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

enum { MAXA = 128 };    /* initial allocation size, MAXA must be >= 1 */

int main (int argc, char **argv) {

    char *line = NULL;
    char **arr = NULL;
    size_t i, maxa = MAXA, n = 0, ndx = 0;
    ssize_t nchr = 0;
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* allocate MAXA pointers to char -- initially & validate */
    if (!(arr = calloc (maxa, sizeof *arr))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

    while ((nchr = getline (&line, &n, fp)) != -1) {    /* read each line */

        while (line[nchr-1] == '\n') line[--nchr] = 0;  /* remove '\n'    */

        if (!(arr[ndx] = strdup (line))) {  /* allocate, copy, add to arr */
            fprintf (stderr, "error: virtual memory exhausted.\n");
            break;            /* leave read loop, preserving existing arr */
        }

        if (++ndx == maxa) {  /* if allocation limit reached, realloc arr */
            size_t asz = sizeof *arr;
            void *tmp = realloc (arr, (maxa + MAXA) * asz);
            if (!tmp) {     /* validate realloc succeeded */
                fprintf (stderr, "error: realloc, memory exhausted.\n");
                break;      /* preserving original arr */
            }
            arr = tmp;    /* assign & zero (optional) new memory */
            memset (arr + (maxa + MAXA) * asz, 0, MAXA * asz);
            maxa += MAXA; /* update current allocation limit */
        }
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */
    if (line) free (line);          /* free mem allocated by getline */  

    for (i = 0; i < ndx; i++)   /* output array */
        printf (" arr[%4zu] : %s\n", i, arr[i]);

    for (i = 0; i < ndx; i++)   /* free allocated memory */
        free (arr[i]);          /* free each line */
    free (arr);                 /* free pointers  */

    return 0;
}

仔细看看,如果您有任何问题,请告诉我。

答案 3 :(得分:0)

阅读malloc / realloc和朋友。

读取单行的第一种方法可能与以下内容类似(请注意,这是一个玩具程序,因此省略了错误检查):

size_t line_length = 0;
char *line = NULL;
char ch;
while ((ch = fgetc(fp)) != '\n') {
    line = realloc(line, line_length+1);
    line[line_length++] = ch;
}
// Add null character at end of line
line = realloc(line, line_length+1);
line[line_length] = 0;

这个问题最大的问题是它很慢,特别是对于长线来说很慢。更好的方法是跟踪分配和写入的大小,并根据需要以指数方式增加数组的大小,然后在最后修剪到实际所需的长度。

此外,为这种方法使用fgets可能更好(也更简单)。

对于阅读多行,您可以嵌套此方法。