# Programar em C/Entrada e saída em arquivos
## Trabalhando com arquivos

Já vimos como podemos receber e enviar dados para usuário através do teclado e da tela; agora veremos também como ler e gravar dados em arquivos, o que é aliás muito importante ou até essencial em muitas aplicações.
Assim como as funções de entrada/saída padrão (teclado e tela), as funções de entrada/saída em arquivos estão declaradas no cabeçalho stdio.h que significa "STanDard Input-Output". Aliás, as funções para manipulação de arquivos são muito semelhantes às usadas para entrada/saída padrão. Como já dissemos na seção sobre a entrada e saída padrões, a manipulação de arquivos também se dá por meio de fluxos (streams).
Na manipulação de um arquivo, há basicamente três etapas que precisam ser realizadas:
* abrir o arquivo;
* ler e/ou gravar os dados desejados;
* fechar o arquivo.
Em C, todas as operações realizadas com arquivos envolvem seu identificador de fluxo, que é uma variável do tipo FILE * (sobre o qual não cabe agora falar). Para declarar um identificador de fluxo, faça como se fosse uma variável normal:

## Abrindo e fechando um arquivo

Não surpreendentemente, a primeira coisa que se deve fazer para manipular um arquivo é abri-lo. Para isso, usamos a função fopen. Sua sintaxe é:
* O nome do arquivo deve ser uma string ou com o caminho completo (por exemplo, /usr/share/appname/app.conf ou C:\Documentos\nomes.txt) ou o caminho em relação ao diretório atual (nomes.txt, ../app.conf) do arquivo que se deseja abrir ou criar.
* O modo de acesso é uma string que contém uma seqüência de caracteres que dizem se o arquivo será aberto para gravação ou leitura. Depois de aberto o arquivo, você só poderá executar os tipos de ação previstos pelo modo de acesso: não poderá ler de um arquivo que foi aberto somente para escrita, por exemplo. Os modos de acesso estão descritos na tabela a seguir.
| Modo | Significado |
| r | Abre o arquivo somente para leitura. O arquivo deve existir. (O r vem do inglês read, ler) |
| r+ | Abre o arquivo para leitura e escrita. O arquivo deve existir. |
| w | Abre o arquivo somente para escrita no início do arquivo. Apagará o conteúdo do arquivo se ele já existir, criará um arquivo novo se não existir. (O w vem do inglês write, escrever) |
| w+ | Abre o arquivo para escrita e leitura, apagando o conteúdo pré-existente. |
| a | Abre o arquivo para escrita no final do arquivo. Não apaga o conteúdo pré-existente. (O a vem do inglês append, adicionar, apender) |
| a+ | Abre o arquivo para escrita no final do arquivo e leitura. |
Em ambientes DOS/Windows, ao ler arquivos binários (por exemplo, programas executáveis ou certos tipos de arquivos de dados), deve-se adicionar o caractere "b" ao final da string de modo (por exemplo, "wb" ou "r+b") para que o arquivo seja lido/gravado corretamente.
Isso é necessário porque no modo texto (o padrão quando não é adicionado o b) ocorrem algumas traduções de caracteres (por exemplo, a terminação de linha "\r\n" é substituída apenas por "\n" na leitura) que poderiam afetar a leitura/gravação dos arquivos binários (indevidamente inserindo ou suprimindo caracteres).
* O valor de retorno da função fopen é muito importante! Ele é o identificador do fluxo que você abriu e é só com ele que você conseguirá ler e escrever no arquivo aberto.
* Se houver um erro na abertura/criação do arquivo, a função retornará o valor NULL. O erro geralmente acontece por duas razões:
** O arquivo não existe, caso tenha sido requisitado para leitura.
** O usuário atual não tem permissão para abrir o arquivo com o modo de acesso pedido. Por exemplo, o arquivo é somente-leitura, ou está bloqueado para gravação por outro programa, ou pertence a outro usuário e não tem permissão para ser lido por outros.
Ao terminar de usar um arquivo, você deve fechá-lo. Isso é feito pela função fclose:
* O único argumento é o identificador do fluxo (retornado por fopen). O valor de retorno indica o sucesso da operação com o valor zero.
* Fechar um arquivo faz com que qualquer caractere que tenha permanecido no "buffer" associado ao fluxo de saída seja gravado. Mas, o que é este "buffer"? Quando você envia caracteres para serem gravados em um arquivo, estes caracteres são armazenados temporariamente em uma área de memória (o "buffer") em vez de serem escritos em disco imediatamente. Quando o "buffer" estiver cheio, seu conteúdo é escrito no disco de uma vez. A razão para se fazer isto tem a ver com a eficiência nas leituras e gravações de arquivos. Se, para cada caractere que fôssemos gravar, tivéssemos que posicionar a cabeça de gravação em um ponto específico do disco, apenas para gravar aquele caractere, as gravações seriam muito lentas. Assim estas gravações só serão efetuadas quando houver um volume razoável de informações a serem gravadas ou quando o arquivo for fechado.
* A função exit fecha todos os arquivos que um programa tiver aberto.
* A função fflush força a gravação de todos os caracteres que estão no buffer para o arquivo.

### Exemplo

Um pequeno exemplo apenas para ilustrar a abertura e fechamento de arquivos:

### Arquivos pré-definidos

Na biblioteca padrão do C, existem alguns fluxos pré-definidos que não precisam (nem devem) ser abertos nem fechados:
* stdin: dispositivo de entrada padrão (geralmente o teclado)
* stdout: dispositivo de saída padrão (geralmente o vídeo)
* stderr: dispositivo de saída de erro padrão (geralmente o vídeo)
* stdaux: dispositivo de saída auxiliar (em muitos sistemas, associado à porta serial)
* stdprn: dispositivo de impressão padrão (em muitos sistemas, associado à porta paralela)

## Escrevendo em arquivos

Para escrever em arquivos, há quatro funções, das quais três são análogas às usadas para saída padrão:
| Saída padrão | Arquivos | Explicação |
| putchar | fputc | Imprime apenas um caractere. |
| puts | fputs | Imprime uma string diretamente, sem nenhuma formatação. |
| printf | fprintf | Imprime uma string formatada. |
| N/A | fwrite | Grava dados binários para um arquivo. |
A seguir apresentamos os protótipos dessas funções:
* Sintaxe quase igual à de printf; só é necessário adicionar o identificador de fluxo no início.

### fwrite

* Esta função envolve os conceitos de ponteiro e vetor, que só serão abordados mais tarde.
A função fwrite funciona como a sua companheira fread, porém escreve no arquivo. Seu protótipo é:
A função retorna o número de itens escritos. Este valor será igual a count a menos que ocorra algum erro. O exemplo abaixo ilustra o uso de fwrite e fread para gravar e posteriormente ler uma variável float em um arquivo binário.
Nota-se o uso do operador sizeof, que retorna o tamanho em bytes da variável ou do tipo de dados.

### fputc

A função fputc é a primeira função de escrita de arquivo que veremos. Seu protótipo é:
Escreve um caractere no arquivo.O programa a seguir lê uma string do teclado e escreve-a, caractere por caractere em um arquivo em disco (o arquivo arquivo.txt, que será aberto no diretório corrente).
Depois de executar este programa, verifique o conteúdo do arquivo arquivo.txt (você pode usar qualquer editor de textos). Você verá que a string que você digitou está armazenada nele.

## Leitura de arquivos

Novamente, há quatro funções, das quais três se assemelham às usadas para a saída padrão:
| Saída padrão | Arquivos | Explicação |
| getchar | fgetc | Recebe apenas um caractere. |
| gets | fgets | Lê uma string (geralmente uma linha inteira). |
| scanf | fscanf | Recebe uma string formatada. |
| N/A | fread | Lê dados binários de um arquivo. |

### fgetc

* Está função requer como parâmetro o indicador de fluxo do arquivo, retorna um caractere do arquivo ou EOF, caso ocorra um erro ou o final do arquivo seja atingido, podendo ser verificado respectivamente por ferror e feof.
Exemplo:

### fgets

* Ao chamar a função fgets, você deve fornecer o ponteiro para a string onde os dados lidos devem ser guardados, além do tamanho máximo dos dados a serem lidos (para que a memória reservada à string não seja ultrapassada).
Para se ler uma string num arquivo podemos usar fgets cujo protótipo é:
A função recebe 3 argumentos: a string a ser lida, o limite máximo de caracteres a serem lidos e o ponteiro para FILE, que está associado ao arquivo de onde a string será lida. A função lê a string até que um caracter de nova linha seja lido ou tamanho-1 caracteres tenham sido lidos. Se o caracter de nova linha ('\n') for lido, ele fará parte da string, o que não acontecia com gets.
A função fgets é semelhante à função gets, porém, além dela poder fazer a leitura a partir de um arquivo de dados e incluir o caracter de nova linha na string, ela ainda especifica o tamanho máximo da string de entrada. Como vimos, a função gets não tinha este controle, o que poderia acarretar erros de "estouro de buffer". Portanto, levando em conta que o ponteiro fp pode ser substituído por stdin, como vimos acima, uma alternativa ao uso de gets é usar a seguinte construção:

### fscanf

* Sintaxe quase igual à de scanf; só é necessário adicionar o identificador de fluxo no início.

### fscanf

A função fscanf funciona como a função scanf. A diferença é que fscanf lê de um arquivo e não do teclado do computador. Protótipo:

### fread

* Essa função envolve os conceitos de ponteiro e vetor, que só serão abordados mais tarde.
Podemos escrever e ler blocos de dados. Para tanto, temos as funções fread e fwrite. O protótipo de fread é:
O buffer é a região de memória na qual serão armazenados os dados lidos. O número de bytes é o tamanho da unidade a ser lida. count indica quantas unidades devem ser lidas. Isto significa que o número total de bytes lidos é:
A função retorna o número de unidades efetivamente lidas. Este número pode ser menor que count quando o fim do arquivo for encontrado ou ocorrer algum erro.
Quando o arquivo for aberto para dados binários, fread pode ler qualquer tipo de dados.

## Movendo pelo arquivo
### fseek

Para se fazer procuras e acessos randômicos em arquivos usa-se a função fseek. Esta move a posição corrente de leitura ou escrita no arquivo de um valor especificado, a partir de um ponto especificado. Seu protótipo é:
O parâmetro origem determina a partir de onde os numbytes de movimentação serão contados. Os valores possíveis são definidos por macros em stdio.h e são:
Tendo-se definido a partir de onde irá se contar, numbytes determina quantos bytes de deslocamento serão dados na posição atual.

### rewind

Volta para o começo do arquivo de um fluxo

### feof

EOF ("End of file") indica o fim de um arquivo. Às vezes, é necessário verificar se um arquivo chegou ao fim. Para isto podemos usar a função feof. Ela retorna não-zero se o arquivo chegou ao EOF, caso contrário retorna zero. Seu protótipo é:
Outra forma de se verificar se o final do arquivo foi atingido é comparar o caractere lido por getc com EOF. O programa a seguir abre um arquivo já existente e o lê, caracter por caracter, até que o final do arquivo seja atingido. Os caracteres lidos são apresentados na tela:
Verifique o exemplo.

## Outras funções

| Função | Explicação |
| remove | Remove um arquivo especificado |

### ferror e perror

Protótipo de ferror:
A função retorna zero, se nenhum erro ocorreu e um número diferente de zero se algum erro ocorreu durante o acesso ao arquivo. se torna muito útil quando queremos verificar se cada acesso a um arquivo teve sucesso, de modo que consigamos garantir a integridade dos nossos dados. Na maioria dos casos, se um arquivo pode ser aberto, ele pode ser lido ou gravado.
Porém, existem situações em que isto não ocorre. Por exemplo, pode acabar o espaço em disco enquanto gravamos, ou o disco pode estar com problemas e não conseguimos ler, etc. Uma função que pode ser usada em conjunto com ferror é a função perror (print error), cujo argumento é uma string que normalmente indica em que parte do programa o problema ocorreu.