Existem diversas extensões de arquivos no computador, mas em geral apenas 2 tipos de arquivos:
Cada byte do arquivo representa um caractere. Assim, o conjunto de bytes forma um texto. Um programa C, por exemplo, é um arquivo desse tipo, pode ser facilmente visualizado com qualquer editor de texto. Arquivos texto são fáceis de serem lidos por pessoas, mas costumam ocupar mais espaço para armazenar informações.
são gravados os dados como estariam na memória, byte a byte. Por exemplo, uma variável inteira é gravada com 4 bytes com o conteúdo exato que está na memória. Não conseguimos visualizar com um editor de texto, é necessário um programa que reconheça aquela sequência de bytes e dê significado a ela. Difícil de ser lido por pessoas, mas costuma ocupar menos espaço para armazenar informações, uma vez que cada byte não precisa representar um caractere válido. Imagens e áudios são exemplos de arquivos binários.
Nesta aula vamos conhecer melhor os arquivos de texto. Os arquivos binários ficarão para uma aula futura.
FILE* fd; // ponteiro para arquivoNeste caso o ponteiro se chama "fd", mas pode ter qualquer nome.
Antes de operarmos com os arquivos precisamos saber algumas informações sobre tal arquivo. Por exemplo:
Sabendo essas informações poderemos abrir o arquivo corretamente. A função que faz isso é fopen() (file open), que recebe 2 parâmetros, nesta ordem, nome do arquivo e modo em que abriremos o arquivo. Os modos disponíveis estão listados na tabela abaixo.
FILE* fd; // file descriptor char file[] = "file.txt"; // file name fd = fopen(file, "w"); // abrindo arquivo para escrita
Code | Nome | Descrição |
---|---|---|
"r" | Read | Abre um arquivo TEXTO para leitura, já deve existir o arquivo. |
"w" | Write | Abre (ou cria) um arquivo TEXTO para escrita. Se o arquivo já existir seu conteúdo anterior é descartado. |
"a" | Append | Abre (ou cria) um arquivo TEXTO para escrita. Ponteiro começa no final do arquivo. |
"r+" | Read Plus | Abre um arquivo TEXTO para leitura ou escrita. O arquivo deve existir e poder ser modificado. |
"w+" | Write Plus | Abre (ou cria) um arquivo TEXTO para leitura e escrita. Se o arquivo já existir seu conteúdo anterior é descartado. |
"a+" | Append Plus | Abre (ou cria) um arquivo TEXTO para leitura e escrita. Ponteiro começa no final do arquivo. |
A função fopen() retorna um descriptor do arquivo, que é gerado pelo SO e associa o arquivo desejado ao ponteiro que criamos. Caso o arquivo não possa ser aberto por qualquer motivo o retorno será NULL. Assim, para saber se o arquivo foi aberto com sucesso basta testar se o ponteiro tem valor NULL.
Veja nos materiais extras alguns motivos que podem causar falha na abertura do arquivo.
FILE* fd; char file[] = "file.txt"; fd = fopen(file, "r+"); // testar se arquivo existe if(fd == NULL) fd = fopen(file, "w"); // se nao existe, cria o arquivo
Depois de utilizarmos o arquivo é necessário fechá-lo. Isso é importante por 2 motivos:
Para fechar um arquivo usamos a função fclose(). Após fecharmos um arquivo a associação entre o arquivo e o ponteiro é desfeita, e poderemos associar o ponteiro à outro arquivo.
FILE* fd = fopen("file.txt", "r+"); // utiliza o arquivo no programa.... fclose(fd); // fecha o arquivo
O ponteiro do arquivo também guarda a posição atual em que o arquivo está sendo manipulado. Lembre-se que um arquivo é uma sequência de bytes, assim, a posição atual significa qual byte da sequência estamos olhando. Toda vez que uma operação de leitura ou escrita acontece esta posição é automaticamente atualizada, mas por vezes queremos ir para uma posição específica no arquivo. Vejamos algumas das funções que nos ajudam a fazer isso.
rewind(fd); // retorna para o inicio do arquivo
int pos; pos = ftell(fd);
fseek(fd, 15, SEEK_SET); // go to 15th byte from beggining
Existem muitas outras funções para manipulação de arquivos. Elas fazem parte da biblioteca stdio.h . Leia a referência da biblioteca para conhecer mais funções (lembre-se que nem todas as funções desta biblioteca são sobre arquivos).
Para facilitar o entendimento, utilizaremos um arquivo chamdo notas.txt, com os seguintes dados:
Thiago 9.0 Joao 8.7 Maria 8.1 Henrique 7.6
Para conseguir ler os dados de um arquivo, basta seguir os seguintes passos:
Declarar as variáveis para leitura e um ponteiro para o arquivo;
typedef struct { char nome[100]; double nota; }Estudante; int main(){ FILE *arquivo; int i,n = 0; Estudante aluno[100];
Abrir o arquivo no modo leitura;
arquivo = fopen("notas.txt","r");
Verificar se o arquivo foi aberto com sucesso;
if (arquivo == NULL){ printf("Error! opening file"); return 1; }
Percorcorrer o arquivo e ler os dados
Existem várias funções para ler os dados do arquivo, essas funções possuem os mesmos nomes que as funções já conhecidas, porém precedem de um 'f'.
As funções mais usadas são : fscanf, fgetchar, fgets.
A função fscanf é uma função que recebe 3 parâmetros, o primeiro é o ponteiro para o arquivo, o segundo são os parametros de leitura (%d pra numero, %s pra sting ...) e o ultimo os endereços para salvar os dados (igual já conhecemos com o scanf)
for(int i = 0; i < 4; i++){ fscanf(arquivo, "%s %lf",aluno[n].nome, &aluno[n].nota); }
Porém, caso não for conhecido a quantidade de linhas do arquivo, devemos ler os dados enquanto (While) não chegarmos ao final do arquivo (End of File).
while(fscanf(arquivo, "%s %lf", aluno[n].nome, &aluno[n].nota) != EOF){ n++; }
Vai do programador decidir o que fazer com os dados lidos.
for(i = 0; i < n; i++){ printf("Aluno: %s\n",aluno[i].nome); printf("Nota: %.2lf\n",aluno[i].nota); }
Fechar o arquivo
fclose(arquivo);
Para conseguir escrever os dados em um arquivo, basta novamente seguir os 3 primeiros passos mostrado anteriormente (com uma pequena mudança no passo 2) junto com esses novos passos:
Abrir o arquivo no modo escrita
arquivo = fopen("notas.txt","w");
Percorrer os dados para salvar no arquivo;
A função utilizada para escrever em um arquivo de texto é a função fprintf, ou seja, a função printf imprime na tela uma mensagem, e a função fprintf imprime no arquivo a mensagem.
Vamos supor que as notas dos alunos tiveram um aumento de 10% e agora necessita de atualizar o arquivo
for(i = 0; i < n; i++){ aluno[i].nota *= 1.1; fprintf(arquivo,"%s %lf\n",aluno[i].nome,aluno[i].nota); }
Alguns dos erros que podem ocorrer na abertura de arquivos são:
Se queremos acessar um arquivo em um diretório diferente daquele do programa podemos passar o caminho completo do arquivo, ou um relativo em relação ao diretório que estamos. Por exemplo, se estivermos no diretório "/Desktop" e queremos acessar um arquivo "file.txt" dentro da pasta "files/", que fica no Desktop podemos fazer
FILE* fd = fopen("files/file.txt", "r+");
Se a pasta "files/" ficar no diretório "Documents" podemos fazer assim:
FILE* fd = fopen("../Documents/files/file.txt", "r+");
Lembre-se que ".." acessa o diretório acima do diretório atual. No caso, o diretório acima de Desktop é o diretório home.
O tempo de acesso de disco é muito inferior ao tempo de acesso da RAM, por isso o computador procura fazer o menor número possível de operações em disco possível. Uma das técnicas que permitem isso é o buffering.
Você irá aprender mais sobre isso na matéria de Organização de Arquivos, mas o importante agora é saber que quando o SO faz a ligação entre o ponteiro de arquivo e o arquivo ele também reserva um espaço na memória principal para operações com esse arquivo, i.e. o buffer do arquivo. Qualquer operação que fazemos com o arquivo, de leitura ou escrita, na verdade é realizada nesse buffer. Quando operações suficientes foram realizadas, ou se uma operação não pode ser feita no buffer, então o buffer é escrito no arquivo, e depois modificado na nova operação.
De qualquer forma, quando o arquivo é fechado, usando a função fclose(), esse buffer é então escrito para o arquivo. Caso o arquivo não seja fechado e o programa termine, é possível que algumas operações feitas no buffer não sejam passadas para o arquivo, e sejam perdidas. Por isso é fundamental fechar os arquivos antes do final do programa.