== Arquivos

:cap: cap5
:online: {gitrepo}/blob/{edition}/livro/capitulos/code/{cap}
:local: code/{cap}

.Objetivos do capítulo
____

Ao final deste capítulo você deverá ser capaz de:

* Entender o conceito de arquivos e seus tipos;
* Conhecer as semelhanças e diferenças entre Arquivos e Fluxos;
* Escrever programas em C que leiam e escrevam corretamente na entrada
  padrão, na saída padrão e na saída de erros;
* Verificar se houve erro durante as operações de leitura e escrita ou
  se chegou ao final do arquivo;
* Utilizar as funções `fgetc` ou `getc` para ler carácteres e `fputc`
  ou `putchar` para escrever carácteres em arquivos;
* Utilizar as funções `fgets` e `fputs` para ler e escrever linhas em
  arquivos somente texto;
* Utilizar as funções `fread` e `fwrite` para ler e escrever em
  arquivos binários;
* Compreender o que é o _indicador de posição_ de arquivos e
  identificar as funções que o manipula;

____


Até agora foi visto como é possível gravar e acessar informações/dados
na memória primária/principal do computador (também chamada memória
RAM). Para isso, foi visto que o uso de ponteiros é fundamental. O
problema dessa abordagem é que as informações/dados de um programa em
C gravadas na memória RAM são perdidas após o término da execução
desse programa. Ou seja, não se pode mais acessá-las ou recuperá-las.
Como fazer para, após a execução de um programa em C, ter acesso a
dados/informações manipuladas nesse programa? Uma solução é gravar
esses dados/informações em um arquivo na memória secundária do
computador (também chamado de Disco Rígido – Hard Disk drive). Nesse
caso, após a execução do programa em C, esses dados/informações
estarão disponíveis no arquivo gravado no disco rígido da máquina.

Nesse capítulo serão abordados conceitos básicos sobre arquivos e
formas de manipulação de dados no disco rígido na linguagem C, como
por exemplo, estratégias para salvar e recuperar dados/informações em
arquivos.


[[sec_tipos_de_arquivos]]
=== Os tipos de arquivos

(((Arquivo, somente texto)))

Para compreender a utilização de arquivos em C precisamos conhecer os
diferentes tipos de arquivos que existem:

Arquivos somente-texto:: Estes arquivos possuem somente carácteres de
texto, exemplos deles são arquivos TXT, XML, HTML entre outros.  Outra
característica importante é que seu conteúdo costuma ser separado por
_quebras de linha_. Além disso, os sistemas operacionais codificam a
quebra de linha de forma diferente. Alguns aplicativos aceitam
entradas no formato somente-texto, causando erro de execução caso
encontre carácteres binários.

(((Arquivo, binário)))

Arquivos binários:: Este arquivos possuem bytes que não representam
carácteres textuais, exemplos deles arquivos MP3, JPEG, ZIP etc.
O sistema operacional Windows requer que as funções de escrita e
leitura dos arquivos se comporte de forma diferente, de acordo com o
tipo de arquivo.

(((Arquivo, especial)))

Arquivos especiais:: São arquivos que não armazenam conteúdo, veremos
este tipo no <<ap_arquivos_especiais>>.

=== Arquivos e fluxos

Nesta seção vamos apresentar as características de fluxo de dados e
arquivos e suas similaridades importantes para linguagem C.

==== Fluxo de dados

(((Fluxo de dados)))

Para facilitar o aprendizado na utilização de arquivos em C precisamos
conhecer o conceito de fluxo de dados (_data stream_).

O áudio que escutamos numa rádio é exemplo de um _fluxo de dados_. 
As estações de rádio transmitem o fluxo e seu aparelho toca todos os
trechos que recebe. Uma vez sintonizado no fluxo, não há como pausar,
retroceder ou avançar nele, só é possível tocar o que está sendo
recebido.

O sistema de televisão também transmite as imagens através de fluxo,
no entanto, existes aparelhos que possibilitam pausar a programação. 
Neste caso o aparelho passa a gravar todo o conteúdo que continua
sendo recebido pelo fluxo em um Buffer, geralmente um disco interno.
Quando o usuário remove o pause, a programação é exibida a partir do
conteúdo salvo. Também é possível avançar no vídeo, mas somente até o
final do Buffer, quando atinge o ponto de recepção do fluxo novamente.

IMPORTANT: O fluxo diferencia de arquivo pois, no fluxo, não podemos
avançar ou retroceder na leitura. A leitura do fluxo costuma ser
sequencial, realizando o processamento dos dados logo após a leitura.
Como os arquivos possuem início e fim é possível determinar o seu
tamanho. Já os fluxos possuem início, mas nem todos possuem fim.

==== Arquivos

(((Arquivo)))

O principal propósito dos arquivos é salvar um conteúdo para ser
acessado futuramente. Para isso os arquivos possuem nomes e são
organizados em diretórios (ou pastas), para facilitar sua identificação. 

A organização dos nomes dos arquivos não é a mesma em todos os
sistemas operacionais. No Windows, por exemplo, existe um diretório
raiz por disco (`C:\`, `D:\` etc.) e os diretórios são separados pela
carácter ``\'' (_Contra barra_). Já no Unix, existe um único diretório
raiz `/` onde todos os demais diretórios são _descendentes_ dele, o
carácter separador de diretório é o ``/'' (_Barra_), diferente do
Windows.

Os arquivos somente texto também são diferentes, enquanto no Windows o
final de linha é codificado com dois carácteres, o Linux utiliza
apenas um.

==== Arquivos em C

IMPORTANT: A principal noção que precisamos ter em mente quando
trabalhamos com arquivos em C é que *fluxos e arquivos são manipulados
através de uma interface única*.

O tipo de dado para manipular Arquivos e Streams é o `FILE`, definido
no cabeçalho <<stdio_h>>.

Na estrutura `FILE` são registradas todas as informações necessárias
para sua manipulação:

* Um indicador da posição no arquivo.
* Um ponteiro para seu Buffer (caso exista).
* Um indicador de erro, que registra caso houve algum erro de
  leitura/escrita.
* Um indicativo de _final de arquivo_, onde é registrado se o final do
  arquivo foi atingido.  

NOTE: Perceba que a estrutura `FILE` não possui registro do nome do
arquivo que está sendo manipulado.

=== Cabeçalho `stdio.h`

Todas as funções que serão apresentadas neste capítulo estão definidas
no cabeçalho `stdio.h`, requerendo a inclusão deste arquivo para sua
utilização:

[source,c]
----
#include <stdio.h>
----

=== Abrindo e Fechando arquivos em C

Toda vez que desejamos ler ou escrever em um arquivo precisamos *abrir o
arquivo* indicando esta intenção.

==== Abrindo um arquivo com `fopen`

(((Arquivo, abrindo))) (((fopen)))

A abertura de arquivos em C é realizada através da função <<fopen>>,
que retorna um ponteiro `*FILE` caso a operação foi realizada com
sucesso ou `NULL` caso houve erro na abertura:

[source,c]
----
FILE* arquivo = fopen(nomeDoArquivo, modo); 
----

A intenção da abertura do arquivo deve ser especificada no parâmetro
`modo`:

r:: abre arquivo de texto para leitura
w:: abre arquivo de texto para escrita
wx:: cria arquivo de texto para escrita
a:: adiciona ao final; o indicador de posição de arquivo é
posicionado no final do arquivo
rb:: abre arquivo binário para leitura
wb:: abre arquivo binário para escrita
ab:: abre arquivo binário para escrita, no final do arquivo

Os códigos a seguir demonstram a abertura de arquivo para _leitura no
modo texto_ (<<ex_abrindo_arquivo_de_texto>>) e outro para _escrita no
modo binário_ (<<ex_abrindo_arquivo_binario_para_escrita>>).

[[ex_abrindo_arquivo_de_texto]]
.Abrindo um arquivo de texto para leitura
====
:srcfile: abrindo_arquivo_de_texto.c
:saida:   abrindo_arquivo_de_texto.out
.Código fonte
{online}/{srcfile}[{local}/{srcfile}]

[source, c]
----
include::{local}/{srcfile}[]
----

.Saída da execução
....
include::{local}/{saida}[]
....

====

[[ex_abrindo_arquivo_binario_para_escrita]]
.Abrindo um arquivo binário para escrita
====
:srcfile: abrindo_arquivo_binario_para_escrita.c
:saida:   abrindo_arquivo_binario_para_escrita.out
.Código fonte
{online}/{srcfile}[{local}/{srcfile}]

[source, c]
----
include::{local}/{srcfile}[]
----

.Saída da execução
....
include::{local}/{saida}[]
....

====

[TIP]
====

A especificação dos nomes dos arquivos requer que recordemos como os
strings são escritos e representados na linguagem C:

["graphviz", scaledwidth="70%"]
----
include::../images/cap5/string.dot[]
----

====

==== Entrada e saídas padrões

(((stdin))) (((stdout))) (((stderr)))


O cabeçalho <<stdio_h>> também define os seguintes arquivos como
entradas e saídas padrões, que dispensam a operação de abertura:

`FILE* stdin`:: Arquivo que representa a entrada padrão.
`FILE* stdout`:: Arquivo que representa a saída padrão.
`FILE* stderr`:: Arquivo que representa a saída de erro padrão.

NOTE: Perceba que nos exemplos <<ex_abrindo_arquivo_binario_para_escrita>> e
<<ex_abrindo_arquivo_de_texto>> a impressão das mensagens de erro são
escritas da saída de erro `stderr`.

==== Fechando um arquivo

(((Arquivo, fechando))) (((fclose)))

Independente do modo utilizado para abrir o arquivo, todo arquivo
aberto deve ser fechado após sua utilização. Para fechar um arquivo
utilizamos a função <<fclose>>, passando como parâmetro o ponteiro
`FILE*` retornado na abertura do arquivo:

[[ex_fechando_arquivo]]
.Fechando um arquivo
====

:srcfile: fechando_arquivo.c
:saida:   fechando_arquivo.out
.Código fonte
{online}/{srcfile}[{local}/{srcfile}]

[source, c]
----
include::{local}/{srcfile}[]
----
<1> A função para fechar o arquivo só deve ser invocada caso o arquivo
    foi aberto com sucesso.

.Saída da execução
....
include::{local}/{saida}[]
....

====

=== Indicadores de erro e final de arquivo

(((Arquivo, indicadores)))

Na próximas seções serão apresentadas várias funções para ler e
escrever em arquivos. Todas todas as funções que serão apresentadas
registram eventos de erro e final de arquivo em indicadores na
estrutura `FILE` que poderão ser consultado através das funções que
serão apresentadas nesta seção.

==== Erro na leitura/escrita

(((Arquivo, indicador de erro)))

Toda vez que um erro ocorreu nas operações de escrita ou leitura, a
função que estava realizando a operação registra o ocorrido num
indicador de erro em `FILE`, você poderá consultar o erro invocando a
seguinte função:

[source,c]
----
int ferror(FILE *stream);
----

Haverá ocorrido erro caso o valor retornado for *diferente de zero*.

==== Atingiu o final do arquivo

(((Arquivo, indicador final de arquivo)))

Quando uma função atingiu o final do arquivo ela registra o ocorrido
no indicador de final de arquivo, que pode ser consultado através da
seguinte função:

[source,c]
----
int feof(FILE *stream);
----

Um retorno diferente de zero indica que o final do arquivo foi
atingido.


=== Lendo e escrevendo um carácter por vez

Nesta seção vamos apresentar como ler e escrever no arquivo um
carácter por vez.

==== Lendo um carácter com `fgetc` ou `getc`

(((fgetc)))

A função <<fgetc>> ler um carácter do arquivo, converte-o para `int` e
retorna-o. Caso não haja mais carácteres disponível, devido ao final do
arquivo, retorna a constante `EOF` (do inglês, _End of File_, que
indica o final do arquivo):

[source,c]
----
int fgetc(FILE *arquivo);
----

Após a leitura do carácter o indicador de posição de leitura do
arquivo é incrementado em uma posição.

(((getchar)))

A função <<getchar>> realiza a mesma operação, mas não requer parâmetro,
utilizando a entrada padrão (`stdin`) como arquivo de entrada:

[source,c]
----
int getchar(void);
----

NOTE: Perceba a função de ler um carácter não retorna um *`char`*,
retorna um *`int`*. Isto porque a `EOF` não é um carácter que indica o
final final do arquivo, mas uma constante do tipo `int` que a função
de leitura de carácter retorna ao atingir o final do arquivo.

==== Escrevendo um carácter por vez com `fputc` e `putchar`

(((fputc)))

A função <<fputc>> escreve um carácter (convertido para `unsigned char`)
na posição atual do arquivo:

[source,c]
----
int fputc(int caracter, FILE *stream);
----

A função retorna o carácter escrito ou `EOF` se houve erro durante a
escrita.

(((putchar)))

A função <<putchar>> realiza a mesma função que <<fputc>> para opera
somente na saída padrão (`stdout`):

[source,c]
----
int putchar(int caracter);
----

==== Exemplo completo de leitura e escrita

O <<ex_lendo_e_escrevendo_linha>> demonstra um a utilização das
funções de leitura e escrita com carácteres:

[[ex_lendo_e_escrevendo_linha]]
.Lendo e escrevendo carácteres
====

:srcfile: lendo_e_escrevendo_char.c
:saida:   lendo_e_escrevendo_char.out
.Código fonte
{online}/{srcfile}[{local}/{srcfile}]

[source, c]
----
include::{local}/{srcfile}[]
----
<1> Abertura dos arquivos para leitura e escrita respectivamente.
<2> Verificando se os arquivos foram abertos com sucesso.
<3> Ler um carácter da entrada e escreve o carácter lido na saída.
<4> Verifica se atingiu o final do arquivo.
<5> Fecha os arquivos abertos.

.Saída da execução
....
include::{local}/{saida}[]
....

====


=== Lendo e escrevendo uma linha por vez com `fgets` e `fputs`

Nesta seção vamos apresentar como ler e escrever no arquivo uma
linha por vez.

==== Lendo uma linha com `fgets`

(((fgets)))

A função <<fgets>> ler uma linha do arquivo e salva o conteúdo lido no
`buffer` passado como parâmetro. O final de linha é retido e retornando
junto com a linha lida. A função copia carácteres do arquivo até
encontrar o final da linha, ou chegou ao final do arquivo ou leu `n-1`
carácteres. O carácter `\0` é escrito após o último carácter lido,
transformando `buffer` em um string terminado em zero, compatível com
as funções <<string_h>>. 

[source,c]
----
char *fgets(char * buffer, int n, FILE * arquivo);
----

A função retorna `buffer`, contendo a linha lida, ou `NULL` se houve
algum erro durante a leitura.


[NOTE]
====
Para entender o funcionamento da função <<fgets>> é importante
perceber como o computador funciona. Inicialmente alguém poderia
desejar que esta função retornasse um string contendo a linha lida.
Mas pense bem: Se se o arquivo lido for enorme e não possuir quebra de
linhas, a operação de leitura de uma linha poderia retornar um string
que não coubesse na memória. 

Como evitar que isto aconteça? Passando um parâmetro `n`, indicando a
quantidade máxima de carácteres que deve ser lido na procura do final
de linha.

Além disso, para evitar erros de alocação de memória, é solicitado que
o usuário forneça previamente o Buffer onde será copiado os carácteres
da linha lida.

====


==== Escrevendo um string com `fputs`

(((fputs)))

A função <<fputs>> escreve um string, terminado com o carácter
zero, no arquivo.

[source,c]
----
int fputs(const char * string, FILE * arquivo);
----

A função retorna `EOF` em caso de erro.

NOTE: Perceba que não há necessidade de especificar o tamanho na
escrita, ela termina quando encontra o carácter nulo (`\0`). Outro
fato importante é que *esta função não deve ser utilizado para escrita
binária, pois ela não escreve o carácter nulo*.

==== Exemplo completo de leitura e escrita

O seguinte código é um exemplo completo utilizando as funções de
leitura e escrita com linha:

.Lendo e escrevendo linhas
====

:srcfile: lendo_e_escrevendo_linha.c
:saida:   lendo_e_escrevendo_linha.out
.Código fonte
{online}/{srcfile}[{local}/{srcfile}]

[source, c]
----
include::{local}/{srcfile}[]
----
<1> Ler uma linha da entrada.
<2> Escreve o string lido na saída.

.Saída da execução
....
include::{local}/{saida}[]
....
====

=== Lendo e escrevendo dados binários

As funções de leitura e escrita de dados binários diferem dos arquivos
de textos devido a ao conteúdo que será salvo e lido.

==== Lendo uma sequencia de elementos de um arquivo com `fread`

(((fread)))

A função <<fread>> ler uma sequência de elementos de um arquivo. Na
função são passados como parâmetro o `buffer` onde será salvo a
sequência lida, o `tamanho` do tipo de dado que será lido, a
`quantidade` de elementos que serão lidos e o `arquivo` de onde será
lido os dados.

[source,c]
----
size_t fread(void * buffer, size_t tamanho, 
    size_t quantidade, FILE * arquivo);
----

A função retorna a quantidade de elementos lidos, que pode ser menor
do que `quantidade`, caso o final do arquivo foi encontrado ou houve
algum erro durante a leitura. Se `quantidade` ou `tamanho` for zero,
então `buffer` permanece inalterado e a função retorna zero.

==== Escrevendo uma sequencia de elementos de um arquivo com `fwrite`

(((fwrite)))

A função <<fwrite>> escreve uma sequência de elementos num arquivo.
Na função são passados como parâmetro o `buffer` que é um ponteiro
para o início da sequência de dados que se deseja escrever, o
`tamanho` do tipo de elemento, a `quantidade` de elementos que se
deseja escreve e o `arquivo` onde o conteúdo será escrito.

[source,c]
----
size_t fwrite(const void * buffer, size_t tamanho, 
    size_t quantidade, FILE * arquivo);
----

A função retorna o número de elementos que foram escritos, que pode
ser menor do que `quantidade` somente se houve erro durante a escrita.
Caso `quantidade` ou `tamanho` seja zero então o arquivo permanece
inalterado e a função retorna zero.

==== Exemplo completo de leitura e escrita binária

O seguinte código é um exemplo completo utilizando as funções de
leitura e escrita apresentadas nesta seção.

Alguns jogos mantém uma lista de recordistas, que salva o nome do
jogador e pontuação obtida. Neste código criamos uma lista contendo a
pontuação inicial de três jogadores fictícios e salvamos num arquivo.
Em seguida, demonstramos a leitura do arquivo e finalmente imprimimos
os recordes lidos.

.Escrevendo e lendo elementos em binário
====

:srcfile: escrevendo_e_lendo_binario.c
:saida:   escrevendo_e_lendo_binario.out
.Código fonte
{online}/{srcfile}[{local}/{srcfile}]

[source, c]
----
include::{local}/{srcfile}[]
----
<1> Declaração do tipo `Jogador`, com dois dados: seu nome e sua
    pontuação.
<2> Inicialização de um array de recordistas iniciais que serão salvos
    mais adiante.
<3> Salva os três recordistas no arquivo.
<4> Ler os três recordistas do arquivo e salva no buffer `jogadores`.
<5> Imprime os recordistas.

.Saída da execução
....
include::{local}/{saida}[]
....
====

=== Descarregando o buffer com `fflush`

(((fflush)))

Alguns arquivos são abertos com buffers para escrita, isto implica que
as operações de escritas no arquivo não são persistidas no arquivo até
que o buffer esteja cheio ou o arquivo seja fechado. Para determinar
que o conteúdo do buffer seja escrito no arquivo utilizamos a função
<<fflush>>:

[source,c]
----
int fflush(FILE *stream);
----

A função retorna `EOF` caso houver erro na escrita do arquivo, caso
contrário retorna zero.

=== Escrevendo e lendo dados formatados

Finalizando as seções de escrita e leitura de dados, vamos conhecer
funções provavelmente lhe são familiares.

==== Ler dados formatados

(((fscanf)))(((scanf)))

Para ler dados formatados você pode utilizar a função <<fscanf>>, que é
similar a função <<scanf>>, a única diferença é que `scanf` trabalha
apenas com a entrada padrão, enquanto `sscanf` especifica o arquivo de
entrada:

[source,c]
----
int fscanf(FILE * arquivo, 
    const char * formato, ...);
----

==== Escrever dados formatados

(((fprintf)))(((printf)))

A escrita de dados formatados é feita através da função <<fprintf>>, que
é similar a <<printf>> (que trabalha somente na saída padrão), a única
diferença é que `fprintf` permite especificar o arquivo de saída:

[source,c]
----
int fprintf(FILE * arquivo, 
    const char * formato, ...);
----

=== Movendo o indicador de posição

Como foi apresentado anteriormente, o *((indicador de posição))*
mantém o registro da posição atual do arquivo.

Em alguns casos podemos ter a necessidade de avançar ou retroceder o
ponto de leitura ou escrita do arquivo, as funções a seguir
servem a este propósito.

IMPORTANT: As funções `rewind`, `ftell` e `fseek` serão utilizadas no
<<cap_lingua_do_i>>. Por enquanto é importante apenas que você
reconheça a sua existência.

==== Retrocede para o início do arquivo com `rewind`

(((rewind)))

A função <<rewind>> retrocede o indicador de posição para o início do
arquivo:

[source,c]
----
void rewind(FILE *arquivo);
----

==== Lendo a posição atual

(((ftell)))

A função <<ftell>> retorna a posição atual no arquivo:

[source,c]
----
long int ftell(FILE *arquivo);
----

==== Indo para o final do arquivo

(((fseek)))

Para ir para o final do arquivo invocamos podemo utilizar a função
<<fseek>> da seguinte forma:

[source,c]
----
long fseek(arquivo, 0, SEEK_END);
----

=== Recapitulando

Neste capítulo foi apresentado os tipos de arquivos que existem, as
similaridades entre fluxos e arquivos e como a linguagem abstrai os
arquivos como fluxos. Em seguida aprendemos como abrir os arquivos
antes de realizar operações neles, e como fechar para finalizar a
operação. As operações para ler e escrever carácteres no arquivo
possuiam um exemplo demonstrando sua utilização. Aprendemos que os
arquivos possuem indicadores de erro, final de arquivo e de posição
que são alterados durante as execuções das funções e leitura e
escrita, e como consultar o estado destes indicadores. Vimos que
existem funções próprias para trabalhar com arquivos de texto, que
processam o arquivo por linhas e como utilizá-las. As funções de
escrita e leitura de arquivos binários necessitam do tamanho do tipo
de dado, a quantidade de dados que seriam processados e um buffer para
realização das operações. Por último, vimos como alterar e consultar o
indicador de posição do arquivo.

////
Terminando arquivo com linha em branco
////

