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

Ответ 1

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

Ответ 2

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

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

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

ADDED (ответ на следующий вопрос в комментарии): 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). */

Ответ 3

FILE* fp;
char buffer[255];

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

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

fclose(fp);

Ответ 4

//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

Ответ 5

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

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

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

Ответ 6

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

Ответ 7

Некоторые примеры не соответствуют этому примеру:

  • вы забыли добавить \n к вашим printfs. Также сообщения об ошибках должны идти в stderr i.e. fprintf(stderr, ....
  • (а не biggy, но) рассмотрите возможность использования 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.

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

btw, "явное" кастинг от char* до const char* является избыточным.

btw2, нет необходимости malloc() lineBuffer, просто определите его char lineBuffer[128], поэтому вам не нужно его освобождать

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

Ответ 11

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

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

Ответ 12

Внедрить метод для чтения и получения содержимого из файла (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);
}

Надеюсь на эту помощь. Счастливое кодирование!

Ответ 13

Вы делаете ошибку, возвращая указатель на автоматическую переменную. Переменная строка выделяется в стеке и живет только до тех пор, пока функция работает. Вам не разрешено возвращать указатель на него, потому что, как только он вернется, память будет указана в другом месте.

const char* func x(){
    char line[100];
    return (const char*) line; //illegal
}

Чтобы этого избежать, вы либо возвращаете указатель на память, который находится в куче, например. lineBuffer и на него должна возлагаться ответственность за вызов free(), когда он с этим справится. В качестве альтернативы вы можете попросить пользователя передать вам в качестве аргумента адрес памяти, на котором будет записываться содержимое строки.

Ответ 14

Мне нужен код из земли 0, поэтому я сделал это, чтобы прочитать содержимое словарного слова по строкам.

char temp_str [20];//вы можете изменить размер буфера в соответствии с вашими требованиями и длину одной строки в файле.

Примечание Я инициализировал буфер с символом Null каждый раз, когда я читаю строку. Эта функция может быть автоматизирована, но так как мне требуется доказательство концепции и вы хотите создать программу Byte By Byte

#include<stdio.h>

int main()
{
int i;
char temp_ch;
FILE *fp=fopen("data.txt","r");
while(temp_ch!=EOF)
{
 i=0;
  char temp_str[20]={'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'};
while(temp_ch!='\n')
{
  temp_ch=fgetc(fp);
  temp_str[i]=temp_ch;
  i++;
}
if(temp_ch=='\n')
{
temp_ch=fgetc(fp);
temp_str[i]=temp_ch;
}
printf("%s",temp_str);
}
return 0;
}

Ответ 15

Моя реализация с нуля:

FILE *pFile = fopen(your_file_path, "r");
int nbytes = 1024;
char *line = (char *) malloc(nbytes);
char *buf = (char *) malloc(nbytes);

size_t bytes_read;
int linesize = 0;
while (fgets(buf, nbytes, pFile) != NULL) {
    bytes_read = strlen(buf);
    // if line length larger than size of line buffer
    if (linesize + bytes_read > nbytes) {
        char *tmp = line;
        nbytes += nbytes / 2;
        line = (char *) malloc(nbytes);
        memcpy(line, tmp, linesize);
        free(tmp);
    }
    memcpy(line + linesize, buf, bytes_read);
    linesize += bytes_read;

    if (feof(pFile) || buf[bytes_read-1] == '\n') {
        handle_line(line);
        linesize = 0;
        memset(line, '\0', nbytes);
    }
}

free(buf);
free(line);

Ответ 16

Предоставьте переносимую и общую функцию read_line, и вы сможете обрабатывать построчное содержимое построчно.

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

void
read_line(const char *filename,
          size_t linecap,
          int delimiter,
          void (*process_line)(const char *line, ssize_t linelen)) {
    FILE *file = fopen(filename, "r");
    if (!file) {
        perror(filename);
        return;
    }
    int c;
    char *line = malloc(linecap);
    if (0 == line) {
        perror("linecap");
        goto close_exit;
    }
    char *p = line;
    ssize_t linelen;
    while (EOF != (c = fgetc(file))) {
        if (delimiter == c) {
            *p = delimiter, *++p = 0;
            process_line(line, linelen+1);
            p = line;
            linelen = 0;
        } else {
            *p++ = c;
            linelen++;
        }
    }
    free(line);
    if (ferror(file)) {
        perror(filename);
        goto close_exit;
    }
 close_exit:
    fclose(file);
}

void
print_line(const char *line, ssize_t linelen) {
    fwrite(line, 1, linelen, stdout);
    fflush(stdout);
}

int
main(int argc, char **argv) {
  read_line("/a/b/c/some.txt", 16, '\n', print_line);
  return 0;
}