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

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

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 выводит тарабарщину. Почему?

164
задан lron 17 August 2010 в 10:08
поделиться

6 ответов

Если ваша задача состоит не в том, чтобы изобрести функцию построчного чтения, а просто прочитать файл построчно, вы можете использовать типичный фрагмент кода, включающий Функция 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);
}
281
ответ дан 23 November 2019 в 21:13
поделиться

В примере есть некоторые ошибки:

  • вы забыли добавить \ 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 в момент выполнения.

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

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

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

4
ответ дан 23 November 2019 в 21:13
поделиться

В своей функции 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). */
20
ответ дан 23 November 2019 в 21:13
поделиться

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

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

  1. Создать переменную в вызывающей функции и передать ее адрес в readLine ()
  2. Выделить память для строки с помощью malloc () - в этом случае строка будет постоянной
  3. Использовать глобальную переменную, хотя обычно это плохая практика
10
ответ дан 23 November 2019 в 21:13
поделиться

Вы должны использовать функции ANSI для чтения строки, например. fgets. После вызова вам понадобится free () в контексте вызова, например:

...
const char *entirecontent=readLine(myFile);
puts(entirecontent);
free(entirecontent);
...

const char *readLine(FILE *file)
{
  char *lineBuffer=calloc(1,1), line[128];

  if ( !file || !lineBuffer )
  {
    fprintf(stderr,"an ErrorNo 1: ...");
    exit(1);
  }

  for(; fgets(line,sizeof line,file) ; strcat(lineBuffer,line) )
  {
    if( strchr(line,'\n') ) *strchr(line,'\n')=0;
    lineBuffer=realloc(lineBuffer,strlen(lineBuffer)+strlen(line)+1);
    if( !lineBuffer )
    {
      fprintf(stderr,"an ErrorNo 2: ...");
      exit(2);
    }
  }
  return lineBuffer;
}
1
ответ дан 23 November 2019 в 21:13
поделиться
const char *readLine(FILE *file, char* line) {

    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);
    return line;

}


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

обратите внимание, что переменная 'line' объявляется в вызывающей функции и затем передается, поэтому ваша функция readLine заполняет предопределенный буфер и просто возвращает его. Так работает большинство библиотек C.

Есть и другие способы, о которых я знаю:

  • определение строки char [] как статической ( строка статических символов [MAX_LINE_LENGTH] -> он будет удерживать свое значение ПОСЛЕ возврата из функции).-> плохо, функция не реентерабельна, и состояние гонки может произойти -> если вы вызовите его дважды из двух потоков, это перезапишет его результаты
  • malloc () в строке char [], и освобождая его в вызывающих функциях -> слишком много дорогих malloc s, и, делегирование ответственности за освобождение буфера другой функции (наиболее элегантным решением является вызов malloc и free для любых буферов в той же функции)

кстати, «явное» приведение из char * - const char * является избыточным.

btw2, нет необходимости в malloc () lineBuffer, просто определите его char lineBuffer

, так что вам не нужно его освобождать

btw3 не используйте «массивы стека динамического размера» (определяя массив как char arrayName [some_nonconstant_variable] ), если вы точно не знаете, что делаете, это работает только в C99.

1
ответ дан 23 November 2019 в 21:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: