C: Ler linhas de um arquivo texto de maneira portável

Palavras-chave: C, arquivo texto, linhas, leitura, getline

Como dito num post anterior, a glibc possui uma função para ler linhas de comprimento arbitrário de um arquivo texto. Mas como esta função é especifica a glibc, alguns leitores pediram uma versão que faça o mesmo em qualquer sistema. Uma das muitas formas de fazer isto é:

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

char *pegalinha(FILE *f, char **buffer, size_t *buflen)
{
  char *ptr;
  char *tmp;
  int c;
  if (!*buffer || !*buflen)
  {
    *buflen= 32;
    *buffer= (char*)malloc(*buflen);
    if (!*buffer)
      return NULL;
  }

  ptr= *buffer;
  while ((c= fgetc(f)) != EOF) {
    *ptr++= c;
    if (c == '\\n') break;

    if (ptr - *buffer >= *buflen-1)
    {
      // aumenta o buffer um pouco mais
      tmp= (char*)realloc(*buffer, *buflen+32);
      if (!tmp)
        return NULL;
      // atualiza ptr para o caso do novo buffer estar em outro lugar
      ptr= tmp + (ptr - *buffer);
      *buflen+= 32;
      *buffer= tmp;
    }
  }
  *ptr= 0;
  return c==EOF ? NULL : *buffer;
}

int main(int argc, char **argv)
{
   FILE *file;
   char *linha= NULL;
   size_t tamlinha= 0;

   if (argc != 2) { printf("%s \\n", argv[0]); exit(1); }

   if (!(file= fopen(argv[1], "r"))) { perror(argv[1]); exit(1); }

   while (pegalinha(file, &linha, &tamlinha))
   {
     printf("%i:%s", tamlinha, linha);
   }
   if (linha)
     free(linha);
   fclose(file);
   return 0;
}
This entry was posted in C. Bookmark the permalink.

12 Responses to C: Ler linhas de um arquivo texto de maneira portável

  1. eljunior says:

    pra que serve o
    ——
    *ptr= ”;
    ——
    na penúltima linha da pegalinha() ?

  2. Alexandre says:

    fim de string, é o mesmo que *ptr = ” ou *ptr = (char) 0.

  3. Aspas simples vazias?
    Não aparece caracter algum para mim dentro das aspas simples, apenas:

    *ptr = ”;

    que não compila.

  4. cezeiro says:

    Morreu o site ?

  5. Ops, era para ser
    *ptr = ”, o blog deve ter comido a barra invertida.

  6. Uh, as aspas estão vazias de novo. :P

    Talvez tenha que colocar duas barras invertidas:

    *ptr = ‘\’;

  7. Anonymous says:

    Hum, parece meio óbvio mas é possível chamar o fgets() várias vezes. Se uma nova linha não foi encontrada, chama realloc no buffer antes de cada chamada nova do fgets(). Dependendo do tipo de uso, pode até manter em buffers separados mesmo.

  8. Se a intenção é ler o arquivo inteiro, separando linha por linha, porque simplesmente não copiá-lo para uma string e repartí-lo com strtok()? Seria mais eficiente que com realloc() (teoricamente).

  9. Reign of Erebus: você não vai querer ler todo o conteúdo do arquivo pra memória. E se seu arquivo tiver vários gigabytes?

  10. Eduardo, você pode lê-lo em bloco se quiser, e programar o reconhecimento de limite. A eficiência seria virtualmente a mesma.

  11. E se a linha for maior que o tamanho de bloco que você escolheu? :)

    A idéia da função apresentada é facilitar a leitura de linha de tamanho arbitrário sem maiores complicações e sem limites arbitrários.

    Sempre há espaço para otimização, claro. Mas “otimização prematura é a raiz de todo o mal”, como costumam dizer. :)

Comments are closed.