C читать файл построчно - PullRequest
       95

C читать файл построчно

154 голосов
/ 17 августа 2010

Я написал эту функцию для чтения строки из файла:

const char *readLine(FILE *file) {

    if (file == NULL) {
        printf("Error: file pointer is null.");
        exit(1);
    }

    int maximumLineLength = 128;
    char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);

    if (lineBuffer == NULL) {
        printf("Error allocating memory for line buffer.");
        exit(1);
    }

    char ch = getc(file);
    int count = 0;

    while ((ch != '\n') && (ch != EOF)) {
        if (count == maximumLineLength) {
            maximumLineLength += 128;
            lineBuffer = realloc(lineBuffer, maximumLineLength);
            if (lineBuffer == NULL) {
                printf("Error reallocating space for line buffer.");
                exit(1);
            }
        }
        lineBuffer[count] = ch;
        count++;

        ch = getc(file);
    }

    lineBuffer[count] = '\0';
    char line[count + 1];
    strncpy(line, lineBuffer, (count + 1));
    free(lineBuffer);
    const char *constLine = line;
    return constLine;
}

Функция правильно читает файл, и, используя printf, я вижу, что строка constLine также правильно прочитана.

Однако, если я использую функцию, например, так:

while (!feof(myFile)) {
    const char *line = readLine(myFile);
    printf("%s\n", line);
}

printf выводит бессмысленно.Почему?

Ответы [ 15 ]

261 голосов
/ 17 августа 2010

Если ваша задача не изобретать функцию построчного чтения, а просто читать файл построчно, вы можете использовать типичный фрагмент кода, включающий функцию getline() (см. Страницу руководства здесь ):

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE * fp;
    char * line = NULL;
    size_t len = 0;
    ssize_t read;

    fp = fopen("/etc/motd", "r");
    if (fp == NULL)
        exit(EXIT_FAILURE);

    while ((read = getline(&line, &len, fp)) != -1) {
        printf("Retrieved line of length %zu:\n", read);
        printf("%s", line);
    }

    fclose(fp);
    if (line)
        free(line);
    exit(EXIT_SUCCESS);
}
20 голосов
/ 17 августа 2010

В вашей функции readLine вы возвращаете указатель на массив line (Строго говоря, указатель на его первый символ, но здесь разница не имеет значения). Поскольку это автоматическая переменная (т. Е. Она «в стеке»), память возвращается, когда функция возвращается. Вы видите тарабарщину, потому что printf поместил свои вещи в стек.

Вам необходимо вернуть динамически выделенный буфер из функции. У вас уже есть один, это lineBuffer; все, что вам нужно сделать, это обрезать его до желаемой длины.

    lineBuffer[count] = '\0';
    realloc(lineBuffer, count + 1);
    return lineBuffer;
}

ДОБАВЛЕНО (ответ на дополнительный вопрос в комментарии): readLine возвращает указатель на символы, составляющие строку. Этот указатель - то, что вам нужно для работы с содержимым строки. Это также то, что вы должны передать free, когда закончили использовать память, занятую этими персонажами. Вот как вы можете использовать функцию readLine:

char *line = readLine(file);
printf("LOG: read a line: %s\n", line);
if (strchr(line, 'a')) { puts("The line contains an a"); }
/* etc. */
free(line);
/* After this point, the memory allocated for the line has been reclaimed.
   You can't use the value of `line` again (though you can assign a new value
   to the `line` variable if you want). */
18 голосов
/ 31 августа 2016
FILE* fp;
char buffer[255];

fp = fopen("file.txt", "r");

while(fgets(buffer, 255, (FILE*) fp)) {
    printf("%s\n", buffer);
}

fclose(fp);
13 голосов
/ 23 октября 2014
//open and get the file handle
FILE* fh;
fopen_s(&fh, filename, "r");

//check if file exists
if (fh == NULL){
    printf("file does not exists %s", filename);
    return 0;
}


//read line by line
const size_t line_size = 300;
char* line = malloc(line_size);
while (fgets(line, line_size, fh) != NULL)  {
    printf(line);
}
free(line);    // dont forget to free heap memory
9 голосов
/ 17 августа 2010

readLine() возвращает указатель на локальную переменную, что вызывает неопределенное поведение.

Чтобы обойти это, вы можете:

  1. Создать переменную в функции вызывающей стороны и передать ее адрес readLine()
  2. Выделите память для line, используя malloc() - в этом случае line будет постоянным
  3. Используйте глобальную переменную, хотя обычно это плохая практика
5 голосов
/ 11 мая 2016

Используйте fgets() для чтения строки из дескриптора файла.

4 голосов
/ 17 августа 2010

Что-то не так с примером:

  • Вы забыли добавить \ n в свои printfs.Также сообщения об ошибках должны отправляться в stderr, т.е. fprintf(stderr, ....
  • (не так важно, но), рассмотрите возможность использования fgetc() вместо getc().getc() - это макрос, fgetc() - правильная функция
  • getc() возвращает int, поэтому ch должен быть объявлен как int.Это важно, поскольку сравнение с EOF будет обработано правильно.Некоторые 8-битные наборы символов используют 0xFF в качестве допустимого символа (например, ISO-LATIN-1) и EOF, который равен -1, будет 0xFF, если назначен char.
  • Возможно потенциальное переполнение буфера в строке

    lineBuffer[count] = '\0';
    

    Если длина строки ровно 128 символов, count равняется 128 в точке выполнения.

  • Как уже отмечали другие, line - это локально объявленный массив.Вы не можете вернуть указатель на него.

  • strncpy(count + 1) скопирует не более count + 1 символов, но прекратит работу, если оно достигнет '\0', поскольку вы установили lineBuffer[count] в '\0' Вы знаете, что оно никогда не достигнет count + 1.Однако, если бы он это сделал, он не поставил бы завершающий '\0', поэтому вам нужно это сделать.Вы часто видите что-то вроде следующего:

    char buffer [BUFFER_SIZE];
    strncpy(buffer, sourceString, BUFFER_SIZE - 1);
    buffer[BUFFER_SIZE - 1] = '\0';
    
  • если вы malloc() возвращаете строку (вместо вашего локального char массива), ваш тип возврата должен быть char* - сбросить const.

3 голосов
/ 29 ноября 2017

Вот мои несколько часов ... Чтение всего файла построчно.

char * readline(FILE *fp, char *buffer)
{
    int ch;
    int i = 0;
    size_t buff_len = 0;

    buffer = malloc(buff_len + 1);
    if (!buffer) return NULL;  // Out of memory

    while ((ch = fgetc(fp)) != '\n' && ch != EOF)
    {
        buff_len++;
        void *tmp = realloc(buffer, buff_len + 1);
        if (tmp == NULL)
        {
            free(buffer);
            return NULL; // Out of memory
        }
        buffer = tmp;

        buffer[i] = (char) ch;
        i++;
    }
    buffer[i] = '\0';

    // Detect end
    if (ch == EOF && (i == 0 || ferror(fp)))
    {
        free(buffer);
        return NULL;
    }
    return buffer;
}

void lineByline(FILE * file){
char *s;
while ((s = readline(file, 0)) != NULL)
{
    puts(s);
    free(s);
    printf("\n");
}
}

int main()
{
    char *fileName = "input-1.txt";
    FILE* file = fopen(fileName, "r");
    lineByline(file);
    return 0;
}
2 голосов
/ 12 августа 2015
void readLine(FILE* file, char* line, int limit)
{
    int i;
    int read;

    read = fread(line, sizeof(char), limit, file);
    line[read] = '\0';

    for(i = 0; i <= read;i++)
    {
        if('\0' == line[i] || '\n' == line[i] || '\r' == line[i])
        {
            line[i] = '\0';
            break;
        }
    }

    if(i != read)
    {
        fseek(file, i - read + 1, SEEK_CUR);
    }
}

как насчет этого?

1 голос
/ 16 ноября 2017

Реализуйте метод для чтения и получения содержимого из файла (input1.txt)

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

void testGetFile() {
    // open file
    FILE *fp = fopen("input1.txt", "r");
    size_t len = 255;
    // need malloc memory for line, if not, segmentation fault error will occurred.
    char *line = malloc(sizeof(char) * len);
    // check if file exist (and you can open it) or not
    if (fp == NULL) {
        printf("can open file input1.txt!");
        return;
    }
    while(fgets(line, len, fp) != NULL) {
        printf("%s\n", line);
    }
    free(line);
}

Надеюсь, это поможет.Удачного кодирования!

...