Enviar um café pro programador

Pode me ajudar a transformar cafeína em código?

Lendo arquivos em C: As funções fgetc, fscanf e fgets

Agora que já aprendemos a escrever em arquivos em C, vamos aprender agora em nossa apostila de C a outra parte: aprender como ler informações dos arquivos!

Iremos aprender como ler caractere por caractere, ou um trecho pré-formatado ou linha por linha.
Isso vai depender da necessidade de cada aplicativo em C.

fgetc() - Como ler caracteres de um arquivo

Se você leu o tutorial passado de nosso curso de C, vai notar que não tem muita diferença nem dificuldade na leitura de informações de um arquivo.

Quando estávamos trabalhando com os arquivos padrões (lendo informações do teclado), usávamos funções como getchar, scanf e gets.
Aqui, iremos ensinar a usar funções correspondentes, porém para trabalhar com arquivos que estão armazenados em nosso disco rígido.

A única diferença entre leitura e escrita, é a posição do arquivos que vamos ler.
Quando estávamos escrevendo, se usássemos o modo de abertura "w", escrevíamos desde o início do arquivo.
Se fosse usando o "a", escrevíamos ao final do arquivo.

Já na leitura, vamos usar funções que iniciarão a leitura sempre do começo do arquivo.
Quando usamos a função fgetc, por exemplo, ele lê o primeiro caractere e automaticamente já se posiciona no próximo. Ao ler o próximo, o C já se prepara para ler o próximo caractere do arquivo, e assim segue, até encontrar a constante EOF.

A sintaxe da função fgetc é:
int fgetc(FILE *arq)

Ela retorna um inteiro que representa o caractere, e EOF, que vale -1, caso aponte para o fim do arquivo. E como os caracteres são representados por inteiros que vão de 0 até 255, um caractere no arquivo nunca terá o valor -1, somente entre 0 e 255.

E como comentamos, uma coisa importante que ocorre 'por debaixo dos panos' é que após retornar um caractere, está função já passa a apontar para o próximo caractere, automaticamente, até encontrar -1 (EOF).

Exemplo de código C - Programa que lê o conteúdo de um arquivo, caractere por caractere

Faça um programa que leia de um arquivo "poema.txt" e imprima na tela, caractere por caractere.

Primeiramente, escreva algo no "poema.txt", pode ser um poema, seu nome completo, endereço etc.
Escreva algo e coloque esse arquivo na mesma pasta do executável.
Assim, nosso endereço é simplesmente "poema.txt", e como queremos ler, vamos usar o método de abertura "r".

Declaramos uma variável de nome "ch" do tipo char, que vai receber caractere por caractere do arquivo.
Isso é feito usando o seguinte código:
ch=fgetc(arq)

E isso deve ser feito enquanto o caractere apontando no arquivo não for EOF.
Fazemos essa checagem assim:
(ch=fgetc(arq))!= EOF

Assim, nosso programa que mostra todo o conteúdo de um arquivo na tela, é:


#include <stdio.h>

int main(void)
{
 char url[]="poema.txt";
 char ch;
 FILE *arq;
 
 arq = fopen(url, "r");
 if(arq == NULL)
     printf("Erro, nao foi possivel abrir o arquivo\n");
 else
     while( (ch=fgetc(arq))!= EOF )
  putchar(ch);
   
 fclose(arq);
 
 return 0;
}



Exercício resolvido - Contando o número de linhas de um arquivo

Escreva um programa que conte o número de linha do arquivo da questão anterior.

O que caracteriza uma linha?
O caractere new line "\n".
Para contar quantas linhas tem um arquivo de texto, basta percorrermos todos os caracteres do arquivo em busca dos "\n", pois cada caractere desse geralmente está no final de uma linha.

Vamos usar o código do exemplo passado deste tutorial para percorrer todos os caracteres, e cada vez que encontrar um "\n" incrementamos a variável "num", inicializada com 0.

Nosso código é:

#include <stdio.h>

int main(void)
{
 char url[]="poema.txt",
      ch;
 int num=0;
 FILE *arq;
 
 arq = fopen(url, "r");
 if(arq == NULL)
   printf("Erro, nao foi possivel abrir o arquivo\n");
 else
  while( (ch=fgetc(arq))!= EOF )
   if(ch == '\n')
     num++;
 
 printf("Existem %d linhas no arquivo\n", num);
 fclose(arq);
 
 return 0;
}


fscanf() - Lendo uma entrada formatada

Assim como é possível escrever de maneira formatada através das funções printf e fprintf, também podemos ler de maneira formatada, como é possível na scanf, através do uso da função fscanf.

Isso é particularmente interessante se tivermos um arquivo com um determinado formato.
Ou seja, quando o conteúdo do arquivo obedecer um determinado padrão.

Por exemplo, vamos supor que você tenha uma lista com notas de 3 alunos.
Na primeira coluna as notas de Matemática, na segunda as de Física e na terceira coluna as notas de Química.
Cada linha é representada como: "%f %f %f\n"
Ou seja: número, espaço em branco, número, espaço em branco, número.e o new line.
Este é o formato, este é o padrão.

A sintaxe da fscanf é:
int fscanf(FILE *arq, char *string_formatada)

Como as outras, retorna EOF caso não tenha conseguido fazer a leitura de maneria correta.

Exemplo de código - Como usar a fscanf

Suponha que tenhamos um arquivo de texto chamado "arquivo.txt" com o seguinte conteúdo:
a b c
d e f
g h i
j k l
Como usar a fscanf para lê-lo?

Basta notar que o formato desse arquivo é: caractere, espaço, caractere, espaço, caractere e enter
Ou seja: "%c %c %c\n"
O formato se repete lina por linha, onde temos que receber 3 caracteres por linha.
Então vamos salvar esses três em três variáveis do tipo char e exibi-las.

Nosso código para ler e exibir esses caracteres é:

#include <stdio.h>

int main(void)
{
 char url[]="arquivo.txt",
      ch1, ch2, ch3;
 FILE *arq;
 
 arq = fopen(url, "r");
 if(arq == NULL)
   printf("Erro, nao foi possivel abrir o arquivo\n");
 else
  while( (fscanf(arq,"%c %c %c\n", &ch1, &ch2, &ch3))!=EOF )
   printf("%c %c %c\n", ch1, ch2, ch3);
 
 fclose(arq);
 
 return 0;
}


Exemplo de código - Nomes, notas e média

Em um arquivo chamado "notas.txt" está os dados dos nomes e notas de alunos.
Em cada linha há o nome do aluno, seguido de três notas:
Maria 8 8 10
Jose 6 6 8
Carlos 7 9.5 7.5
Programador 10 10 10

Crie um programa que exiba o nome de cada aluno e sua média.

A primeira coisa que devemos fazer é analisar o conteúdo do arquivo e procurar por padrões.
Nesse caso, o padrão se repete em toda linha, pois todas as linhas são iguais: string, espaço, número, espaço, número, espaço, número, enter
Ou seja: "%s %f %f %f\n"

Como há um padrão em toda linha, podemos usar a fscanf para armazenar esses dados.
Vamos precisar de uma string e três floats.
Após pegar esses dados, exibimos o nome do aluno e a média das notas.

Nosso código em C fica:

#include <stdio.h>

int main(void)
{
 char url[]="notas.txt",
      nome[20];
 float nota1, nota2, nota3;
 FILE *arq;
 
 arq = fopen(url, "r");
 if(arq == NULL)
   printf("Erro, nao foi possivel abrir o arquivo\n");
 else
  while( (fscanf(arq,"%s %f %f %f\n", nome, &nota1, &nota2, &nota3))!=EOF )
   printf("%s teve media %.2f\n", nome, (nota1+nota2+nota3)/3);
 
 fclose(arq);
 
 return 0;
}



fgets() - Capturando linha

Usávamos a função gets para capturar uma string do teclado do usuário.
Ela pegava do primeiro caractere até encontrar o new line("\n"), e colocava o caractere delimitador ao final ("\0").

Muitas vezes é interessante pegar uma linha inteira de um arquivo, principalmente se nesse arquivo existir textos.
Para fazer esta tarefa, uma boa opção é usar a função fgets, cuja sintaxe é:
char *fgets(char *minhaString, int numBytes, FILE *arq)

Essa função vai abrir o arquivo apontado por arq e vai pegar do primeiro caractere até o new line, ou até o limite de "numBytes" bytes e vai armazenar essa string na "minhaString".

Ou seja, vamos usar ela para pegar cada linha de um arquivo e armazenar na forma de string.
Por exemplo, escreva em um arquivo "dados.txt"o seguinte conteúdo:
"Meu nome: [escreva seu nome completo]
Moro em: [escreva seu endereço]
Estudo pelo C Progressivo
E pretendo ser programador C"

Agora vamos criar um programa que vai ler e exibir esses dados:

#include <stdio.h>

int main(void)
{
 char url[]="dados.txt",
      info[50];
 FILE *arq;
 
 arq = fopen(url, "r");
 if(arq == NULL)
   printf("Erro, nao foi possivel abrir o arquivo\n");
 else
  while( (fgets(info, sizeof(info), arq))!=NULL )
   printf("%s", info);
 
 fclose(arq);
 
 return 0;
}


Podemos usar cada linha dessas e armazenar em uma string diferente (string que guarda o nome, outra que guarda o endereço, outra que guarda o CPF, RG etc, igual aqueles formulários que preenchemos na internet).

Vale notar que, como a fgets retorna uma string, para checar se chegamos ao fim do arquivo, basta checarmos se o retorno dela é diferente de NULL.

16 comentários:

kaue disse...

ola, eu fiz um programa que pede o nome do usuario e armazena num arquivo .txt, e depois mostra um menu com as opções de mudar nome, mostrar nome e sair.
quando entro em mostrar nome, aparece o nome, mas sem o ultimo caractere!
usei o fgets.
por ex.:
seu nome e Kau.

Dav! disse...

como armazenar cada linha ou um conjunto determinado de linhas em uma string? estou querendo usar isso com o randpara criar um jogo de perguntas e respostas com as perguntas sempre em ordem diferente, a partir de um arquivo txt.

Anônimo disse...

Gostaria de saber como salvar dados de uma variavel de um quiz indicando o desempenho do usuario, acredito estar no caminho certo. Parabens pelo site, muito bem explicado. Obrigado.

Anônimo disse...

No primeiro exemplo seria melhor inicializar num como num = 1 porque geralmente a última linha não tem o caractere new line(\n).

Unknown disse...

No exemplo "Nomes,notas e média" como eu procederia caso o nome do aluno fosse escrito por completo?
Ex.:
Maria da Silva 8 8 10
Jose dos Santos 6 6 8

Obrigado.

ChicoMau disse...

Muito bom. Consegui fazer a minha tarefa, em que o usuasio digita o seu nome a password. De seguida, compara com o arquivo Txt. OBRIGADO!!!

Luis 1920
Carlos 8901
Rosa 902

Entendi a vossa lógica, e fiz assim:

while( (fscanf(arq,"%c %d %c\n", &ch1, &ch2, &ch3) )!=EOF ) {

if( (nome==ch1) && (pass==ch2) )
printf("Sucesso");
else
printf("Erro");
}

Unknown disse...

Bom dia,

Gostaria de saber se é possível abrir o arquivo txt gerado, no próprio bloco de notas sem ter que arquivo o arquivo dando dois cliques pelo windows explore

Esse programa do exemplo, salva o arquivo txt no mesmo local onde o arquivo.c foi salvo e no final exibe o que foi digitado no próprio prompt. É possível abri-lo no bloco de notas?

Maycon disse...

Estou com um problema nessa função, ela recebe o nome, senha e idade, mais quando vai gravar no arquivo, o programa fecha. Por favor pode me ajudar?
void cadastro()
{
char nome[30],senha[30];
int idade;
printf("Seu nome: ");
scanf("%s",nome);
printf("Sua senha: ");
scanf("%s",senha);
printf("Sua idade: ");
scanf("%d",idade);
FILE *arquivo;
arquivo=abreArquivo('a',"cadastros.txt");
fprintf(arquivo,"%s %s %d\n",nome,senha,idade);
fecharArquivo(arquivo);
system("pause");
}

void abreArquivo(char modo, char caminho[30])
{
FILE *arquivo;
switch (modo)
{
case 'g':
arquivo=fopen(caminho,"wt");
break;
case 'l':
arquivo=fopen(caminho,"rt");
break;
case 'a':
arquivo=fopen(caminho,"a");
break;
}
if (arquivo==NULL)
{
printf("Desculpe, não foi possivel abrir o arquivo");
exit(0);
}
return arquivo;
}

void fecharArquivo(FILE *arquivo)
{
fclose(arquivo);
}

Anônimo disse...

Desenvolva uma aplicação para avaliar se são primos os seguintes números do ficheiro nums.txt, com o seguinte arranjo:
3
8
10
12
14
15
30
149
150

admrockstar disse...

Como faço pra criar um programa que leia uma string de qualquer tamanho de um arquivo??

Unknown disse...

Olá eu preciso criar uma lista de Album contendo as musicas dentro de cada album, mas eu preciso fazer uma busca no arquivo atravez do nome do album ou musica vc poderia me ajudar?

Anônimo disse...

Como ficaria esta linha em c++?
fscanf(arq,"%s %f %f %f\n", nome, &nota1, &nota2, &nota3)

Nicollas Miyashiro disse...

O Maycon esqueceu de colocar o caractere '&' antes da variavel no scanf, para informar o seu local de memória. Caso queira entender o '&' no scanf é só acessar: http://www.cprogressivo.net/p/aprenda-tudo-sobre-ponteiros-em-c.html.

Pablo Santos disse...

Tem como criar essa função sem o método main? Isso é para que ele possa ser chamado pelo main?

Anônimo disse...

bom dia. estou tentando fazer um programa que leia um arquivo e imprima na tela o número de caracteres que ele possui. Poderia me ajudar? Esse é o programa:
#include
#include
#define SUCESSO (0)

int main(int argc, char ** argv) {
int comparador, contador = 0;
char texto_do_arquivo = ("Meu primeiro arquivo\nde linguagem C\n");
//abre arquivo "arquivo.txt"
FILE* arquivo = fopen("arquivo.txt", "w");
//escreve o texto no arquivo
fprintf(arquivo, texto_do_arquivo);
fflush(arquivo);
//saída de erro
if(arquivo == NULL) {
fprintf(stderr, "Erro ao abrir o arquivo.txt.");
return 1;
}
//posiciona leitor
fseek(arquivo, 0, SEEK_SET);
//lê o arquivo, comparando cada caracter com a variável inteira "comparador", cujo valor foi atribuído por fgetc
do {
fscanf(arquivo,"%c", texto_do_arquivo[contador]);
if((comparador == '\n')||(comparador == ' ')) {
continue;
} else {
contador ++;
}
} while(comparador != EOF);
//exibe resultados na stdout
fprintf(stdout, "O arquivo possui %d caracteres.\n", strlen(arquivo));
//fecha o arquivo
fclose(arquivo);
return SUCESSO;
}

Anônimo disse...

Tem alguma forma de detectar se o caracter lido do arquivo é um número e atribuir pra int caso seja?
Isto é tem alguma atribuicao dinâmica?