Erro ao ler uma linha usando fscanf ()

Estou tentando ler uma linha usando o seguinte código:

while(fscanf(f, "%[^\n\r]s", cLine) != EOF ) { /* do something with cLine */ } 

Mas de alguma forma eu recebo apenas a primeira linha todas as vezes. Esta é uma maneira ruim de ler uma linha? O que devo corrigir para que funcione como esperado?

É quase sempre uma má idéia usar a function fscanf() , já que ela pode deixar seu ponteiro de arquivo em um local desconhecido na falha.

Eu prefiro usar fgets() para obter cada linha e, em seguida, sscanf() isso. Você pode então continuar a examinar a linha lida como achar melhor. Algo como:

 #define LINESZ 1024 char buff[LINESZ]; FILE *fin = fopen ("infile.txt", "r"); if (fin != NULL) { while (fgets (buff, LINESZ, fin)) { /* Process buff here. */ } fclose (fin); } 

fgets() parece ser o que você está tentando fazer, lendo em uma string até encontrar um caractere de nova linha.

Se você quiser ler um arquivo linha por linha (aqui, separador de linha == ‘\ n’) basta fazer isso:

 #include  #include  #include  int main(int argc, char **argv) { FILE *fp; char *buffer; int ret; // Open a file ("test.txt") if ((fp = fopen("test.txt", "r")) == NULL) { fprintf(stdout, "Error: Can't open file !\n"); return -1; } // Alloc buffer size (Set your max line size) buffer = malloc(sizeof(char) * 4096); while(!feof(fp)) { // Clean buffer memset(buffer, 0, 4096); // Read a line ret = fscanf(fp, "%4095[^\n]\n", buffer); if (ret != EOF) { // Print line fprintf(stdout, "%s\n", buffer); } } // Free buffer free(buffer); // Close file fclose(fp); return 0; } 

Apreciar 🙂

Se você tentar while( fscanf( f, "%27[^\n\r]", cLine ) == 1 ) você pode ter um pouco mais de sorte. As três mudanças do seu original:

  • length-limit o que é lido – usei 27 aqui como exemplo e, infelizmente, a família scanf() requer literalmente a largura do campo na string de formato e não pode usar o mecanismo * que o printf() pode passar o valor em
  • livrar-se dos s na string de formato – %[ é o especificador de formato para “todos os caracteres correspondentes ou não correspondentes a um conjunto”, e o conjunto é terminado por a ] por conta própria
  • compare o valor de retorno com o número de conversões que você espera que aconteça (e, para facilitar o gerenciamento, garanta que esse número seja 1)

Dito isso, você obterá o mesmo resultado com menos dor usando fgets() para ler o máximo de uma linha que couber no seu buffer.

Usar o fscanf para ler / tokenizar um arquivo sempre resulta em código frágil ou dor e sofrimento. Ler uma linha e tokenizar ou digitalizar essa linha é seguro e eficaz. Ele precisa de mais linhas de código – o que significa que leva mais tempo para PENSAR sobre o que você quer fazer (e você precisa lidar com um tamanho finito de buffer de input) – mas depois disso a vida simplesmente fede menos.

Não lute contra o fscanf. Apenas não use. Sempre.

Parece-me que você está tentando usar os operadores regex em sua string fscanf. A string [^\n\r] não significa nada para o fscanf, e é por isso que o seu código não funciona como esperado.

Além disso, fscanf () não retorna EOF se o item não corresponder. Em vez disso, ele retorna um inteiro que indica o número de correspondências – que, no seu caso, é provavelmente zero. EOF é retornado somente no final do stream ou em caso de erro. Então, o que está acontecendo no seu caso é que a primeira chamada para fscanf () lê todo o caminho até o final do arquivo procurando por uma string correspondente, então retorna 0 para que você saiba que nenhuma correspondência foi encontrada. A segunda chamada retorna EOF porque o arquivo inteiro foi lido.

Por fim, observe que o operador de formato% s scanf captura apenas o próximo caractere de espaço em branco, portanto, você não precisa excluir \ n ou \ r em nenhum caso.

Consulte a documentação do fscanf para mais informações: http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/

Seu loop tem vários problemas. Você escreveu:

 while( fscanf( f, "%[^\n\r]s", cLine ) != EOF ) /* do something */; 

Algumas coisas a considerar:

  1. fscanf () retorna o número de itens armazenados. Ele pode retornar EOF se ler além do final do arquivo ou se o identificador de arquivo tiver um erro. Você precisa distinguir um retorno válido de zero, caso em que não há novo conteúdo no buffer cLine de uma leitura bem-sucedida.

  2. Você tem um problema quando ocorre uma falha na correspondência porque é difícil prever onde o identificador de arquivo está apontando agora no stream. Isso faz com que a recuperação de um jogo com falha seja mais difícil do que o esperado.

  3. O padrão que você escreveu provavelmente não faz o que você pretendia. Ele está combinando com qualquer número de caracteres que não sejam CR ou LF e, em seguida, esperando encontrar um s literal.

  4. Você não protegeu seu buffer de um estouro. Qualquer número de caracteres pode ser lido a partir do arquivo e gravado no buffer, independentemente do tamanho alocado para aquele buffer. Este é um erro infelizmente comum, que em muitos casos pode ser explorado por um invasor para executar código arbitrário dos atacantes que escolherem.

  5. A menos que você tenha solicitado especificamente que f seja aberto no modo binário, a tradução de término de linha ocorrerá na biblioteca e, em geral, você nunca verá caracteres CR e, normalmente, não em arquivos de texto.

Você provavelmente quer um loop mais parecido com o seguinte:

 while(fgets(cLine, N_CLINE, f)) { /* do something */ ; } 

onde N_CLINE é o número de bytes disponíveis no buffer iniciando um cLine .

A function fgets() é uma maneira muito preferida de ler uma linha de um arquivo. Seu segundo parâmetro é o tamanho do buffer, e ele lê até 1 menos que o tamanho dos bytes do arquivo no buffer. Ele sempre termina o buffer com um caractere nul para que ele possa ser passado com segurança para outras funções de string C.

Ele pára no primeiro do final do arquivo, newline ou buffer_size-1 bytes lidos.

Ele deixa o caractere de nova linha no buffer, e esse fato permite distinguir uma única linha maior que o seu buffer de uma linha menor que o buffer.

Ele retorna NULL se nenhum byte for copiado devido ao final do arquivo ou a um erro, e o ponteiro para o buffer, caso contrário. Você pode querer usar feof() e / ou ferror() para distinguir esses casos.

Eu acho que o problema com este código é porque quando você lê com% [^ \ n \ r] s, na verdade, você está lendo até chegar ‘\ n’ ou ‘\ r’, mas você não está lendo o ‘\ n ‘ou’ \ r ‘também. Então você precisa pegar este caractere antes de ler com fscanf novamente no loop. Faça algo assim:

 do{ fscanf(f, "%[^\n\r]s", cLine) != EOF /* Do something here */ }while(fgetc(file) != EOF)