ti-enxame.com

C ler arquivo linha por linha

Eu escrevi essa função para ler uma linha de um arquivo:

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

A função lê o arquivo corretamente e, usando printf, vejo que a string constLine também foi lida corretamente.

No entanto, se eu usar a função, e. como isso:

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

o printf envia mensagens sem sentido. Por quê?

142
lron

Se a sua tarefa não é inventar a função de leitura linha por linha, mas apenas ler o arquivo linha por linha, você pode usar um trecho de código típico envolvendo a função getline() (veja a página de manual aqui ) :

#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);
}
246
mbaitoff

Em sua função readLine, você retorna um ponteiro para a matriz line (falando estritamente, um ponteiro para seu primeiro caractere, mas a diferença é irrelevante aqui). Como é uma variável automática (ou seja, "na pilha"), a memória é recuperada quando a função retorna. Você vê um jargão porque printf colocou suas próprias coisas na pilha.

Você precisa retornar um buffer alocado dinamicamente da função. Você já tem um, é lineBuffer; tudo o que você precisa fazer é truncá-lo para o tamanho desejado.

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

ADDED (resposta à pergunta de acompanhamento no comentário): readLine retorna um ponteiro para os caracteres que compõem a linha. Esse ponteiro é o que você precisa para trabalhar com o conteúdo da linha. É também o que você deve passar para free quando terminar de usar a memória desses caracteres. Veja como você pode usar a função 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). */
19
Gilles
FILE* fp;
char buffer[255];

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

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

fclose(fp);
16
Rob
//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
14
RevoLab

readLine() retorna o ponteiro para a variável local, o que causa um comportamento indefinido.

Para se locomover, você pode:

  1. Crie uma variável na função de chamador e passe seu endereço para readLine()
  2. Alocar memória para line usando malloc() - neste caso line será persistente
  3. Use a variável global, embora geralmente seja uma prática ruim
8
qrdl

Use fgets() para ler uma linha de um identificador de arquivo.

5
Raku Escape

Algumas coisas estão erradas com o exemplo:

  • você esqueceu de adicionar\n ao seu printfs. Também mensagens de erro devem ir para stderr, ou seja, fprintf(stderr, ....
  • (não um biggy mas) considere o uso fgetc() em vez de getc(). getc() é uma macro, fgetc() é uma função apropriada
  • getc() retorna um int então ch deve ser declarado como um int. Isso é importante, pois a comparação com EOF será tratada corretamente. Alguns conjuntos de caracteres de 8 bits usam 0xFF como um caractere válido (ISO-LATIN-1 seria um exemplo) e EOF que é -1, será 0xFF se atribuído a um char.
  • Há um estouro de buffer em potencial na linha

    lineBuffer[count] = '\0';
    

    Se a linha tiver exatamente 128 caracteres, count é 128 no ponto que é executado.

  • Como outros apontaram, line é uma matriz declarada localmente. Você não pode retornar um ponteiro para ele.

  • strncpy(count + 1) copiará no máximo count + 1 caracteres, mas terminará se ele for '\0' Como você configurou lineBuffer[count] para '\0', sabe que nunca chegará a count + 1. No entanto, se isso acontecesse, não colocaria '\0' de terminação, então você precisa fazê-lo. Você frequentemente vê algo como o seguinte:

    char buffer [BUFFER_SIZE];
    strncpy(buffer, sourceString, BUFFER_SIZE - 1);
    buffer[BUFFER_SIZE - 1] = '\0';
    
  • se você malloc() uma linha retornar (no lugar de sua matriz local char), seu tipo de retorno deve ser char* - solte o const.

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

que tal este?

2
Taner Mansur

Aqui estão minhas várias horas ... Lendo todo o arquivo linha por linha.

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

observe que a variável 'line' é declarada na função de chamada e, em seguida, passada, portanto, sua função readLine preenche o buffer predefinido e apenas o retorna. É assim que funciona a maioria das bibliotecas C.

Existem outras maneiras, das quais estou ciente: 

  • definindo o char line[] como estático (static char line[MAX_LINE_LENGTH] -> ele manterá seu valor AFTER retornando da função). -> ruim, a função não é reentrada, e condição de corrida pode ocorrer -> se você a chama duas vezes de dois tópicos, ela sobrescreverá resultados
  • malloc() na linha char [] e liberando-a em funções chamadoras -> muitos mallocs caros, e, delegando a responsabilidade de liberar o buffer para outra função (a mais solução elegante é chamar malloc e free em quaisquer buffers na mesma função)

btw, a conversão 'explícita' de char* para const char* é redundante.

btw2, não há necessidade de malloc() o lineBuffer, apenas defina char lineBuffer[128], então você não precisa liberá-lo

btw3 não usa 'dynamic stack arrays' (definindo o array como char arrayName[some_nonconstant_variable]), se você não sabe exatamente o que está fazendo, ele funciona apenas em C99.

1
nothrow

Você deve usar as funções ANSI para ler uma linha, por exemplo. fgets. Depois de chamar você precisa de free () no contexto de chamada, por exemplo:

...
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
user411313

Implemente o método para ler e obtenha o conteúdo de um arquivo (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);
}

Espero que esta ajuda. Codificação feliz!

1
Nhat Dinh

Meu implemento a partir do zero:

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);
0
tjeubaoit

Você comete o erro de retornar um ponteiro para uma variável automática. A linha da variável é alocada na pilha e só tem vida enquanto a função existir. Você não tem permissão para retornar um ponteiro para isso, porque assim que retornar a memória será dada em outro lugar.

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

Para evitar isso, você quer retornar um ponteiro para a memória que reside no heap, por exemplo. lineBuffer e deve ser de responsabilidade do usuário chamar free () quando tiver terminado. Alternativamente, você pode pedir ao usuário para passar a você como um argumento um endereço de memória no qual escrever o conteúdo da linha em.

0
Lefteris E

Eu quero um código do ground 0, então eu fiz isso para ler o conteúdo do dicionário Word linha por linha.

char temp_str [20]; // você pode alterar o tamanho do buffer de acordo com seus requisitos E o tamanho de uma única linha em um arquivo.

Nota Eu inicializei o buffer com caractere Nulo toda vez que li a linha.Esta função pode ser Automatizada, Mas Como Preciso de uma Prova de Conceito e quero criar um programa 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;
}
0
Mohit Dabas