C read entire line of file [closed]

一世执手 提交于 2020-01-07 06:36:50

问题


I am trying to program a tool in C. Part of this program is to use a text file and read it line by line, while storing all lines into an array to have it available for future use.

That's what I have so far:

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

My problem is im getting this output:

6 (the number of lines in the file) Segmentation fault

How can I read a complete line by line , without knowing how long the line is in the beginning. in PHP I can do that very easily, but how can I do that in C?

Thanks for any hint!


回答1:


fix like this:

#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:


If you truly want to read an unknown number of characters from an unknown number of lines and store those lines in an array (or, actually, in an object created from a pointer-to-pointer-to-char), then you have a number of options. POSIX getline is a line oriented input function (like fgets) which will read a line of text from the give file each time it is called, and will allocate sufficient storage to hold the line regardless of the length. (as a bonus getline returns the actual number of characters read, eliminating a subsequent call to strlen if the length is needed)

getline eliminates the need for repeated checks on whether fgets actually read the whole line, or just a partial. Further, if your lines are more than a few characters long, the buffered read provided by getline (and fgets) is quite a bit faster than character oriented input (e.g. fgetc). Don't get me wrong, there is nothing wrong with fgetc, and if your files are small and your lines short, you are not going to notice any difference. However, if you are reading a million lines of 500,000 chars each -- you will notice a significant difference.

As for an array, since you don't know how many lines you will read, you really need a pointer-to-pointer-to-char (e.g a double-ponter, char **array) so you can allocate some reasonable number of pointers to begin with, allocate and assign the lines to individual pointer until your limit is reached, then realloc array to increase the number of pointers available, and keep on reading/storing lines.

As with any code that dynamically allocates memory, your must (1) preserve a pointer to each block of memory allocated, so (2) the memory can be freed with no longer in use. You should also validate each allocation (and reallocation) to insure the allocations succeed. When using realloc, always use a temporary pointer so you can validate that realloc succeeds before assigning the new block to the original pointer. If you don't, and realloc fails, you have lost the pointer to your original block of memory that is left untouched, not freed, and you have just created a memory leak.

Lastly, always verify your memory use with a memory error check program such as valgrind on Linux. There are a number of subtle ways to misuse a block of memory.

Putting all that together, you could do something like the following. The code will read all lines from the filename provided as the first argument (or from stdin if no filename is given):

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

Example Use/Output

$ ./bin/getline_realloc_arr < dat/words_554.txt
 arr[   0] : Aam
 arr[   1] : Aard-vark
 arr[   2] : Aard-wolf
 arr[   3] : Aaronic
...
 arr[ 549] : Accompaniment
 arr[ 550] : Accompanist
 arr[ 551] : Accompany
 arr[ 552] : Accompletive
 arr[ 553] : Accomplice

Look things over and let me know if you have any questions.




回答3:


Try Online

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

int main (void)
{
//  FILE * file = fopen("test.txt", "r");
    char * line = readLine(stdin);

    while(line)
    {
        printf(line); // print current line
        free(line); // free allocated memory
        line = readLine(stdin); // recur
    }

    return 0;
}



回答4:


Read up on malloc / realloc and friends.

A first approach for reading a single line might be something along the lines of the following (note that this is a toy program, and as such omits error-checking):

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;

The biggest problem with this is that it's slow, and especially slow for long lines. A better approach would be to keep track of the allocated and written size, and exponentially-grow size of the array as necessary, and then trim to the actual required length at the end.

Also, it'd probably be better (and simpler) to use fgets for that approach.

For reading multiple lines, you can nest this approach.



来源:https://stackoverflow.com/questions/38775052/c-read-entire-line-of-file

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!