# Programar em C++/Entrada e saída de dados 2
## Entrada/Saída em ficheiros (arquivos)

Nota introdutória: Este capitulo geralmente é colocado uns capítulos mais para o fim, mas acho por bem que se torne as coisas mais interativas, o que é possível introduzindo agora operações em arquivos, dá muito mais entusiasmo. Encontramos aqui conceitos avançados mas poderão ser deixados para depois se o leitor não quiser observar o tema neste momento.

## Gravar (Salvar) os dados para um ficheiro(arquivo)

Os dados que mantemos nos programas estão guardados na memória RAM, que é limpa quando o programa ou computador para de funcionar. Isso implicaria que perderíamos toda a informação! Porém existe uma maneira para tornar os dados persistentes que é gravar os dados num ficheiro (arquivo) no "hard drive" (disco rígido) ou no outro meio persistente. Nas formas mais diretas de escrita podemos passar os dados em formato binário para o ficheiro(arquivo). Outros meios avançados para guardar dados podem envolver bases de dados relacionais ou XML.

## O que é um ficheiro(arquivo)?

Um arquivo é uma coleção de dados que estão localizados numa memória persistente tipo hard drive, cd-rom, etc. Para identificarmos o arquivo podemos atribuir-lhe um nome (filename). Os "filenames" têm usualmente uma extensão, que determina o tipo de arquivo em sistemas operacionais semelhantes aos da Microsoft$^®$, mas que podem ser dispensados em sistemas operacionais que guardam as características dos arquivos no meio de armazenamento, tais quais sistemas UNIX$^®$ e seus similares GNU/Linux, FreeBSD, etc... A extensão é representada por 3 ou 4 letras que seguem após o nome do arquivo e um ponto ".". Por exemplo: "joao.doc" ou "joao.odt". Isto diz-me que temos um ficheiro(arquivo) que se chama "joao", e que tem a extensão .doc que refere usualmente a documentos do WORD no primeiro caso e com extensão ".odt" do OpenOffice no segundo. Outros tipos de extensões podem ser ".xls" para documentos EXCEL, ".ods" para planilhas do OpenOffice. ou ainda ".cpp" para ficheiros(arquivos) de códigos de c++.

## Ficheiros(Arquivos) binários e tipo texto

Existem na verdade dois tipos de ficheiros(arquivos): os do tipo texto e os do tipo binário.
* Os arquivos tipo texto apenas armazenam texto obedecendo uma codificação de caracteres, a mais comum é a ASCII, isto implica o uso do código para armazenamento, ou seja, pode ser que a codificação seja interpretada antes de ser efetivada no meio de armazenamento.
* Os arquivos binários podem guardar mais informação, como imagens, base de dados, programas…Por exemplo, editores de texto com formatação, como o OpenOffice e o Word, guardam os seus arquivos em formatos binários, porque eles possuem além do texto, informação acerca da formatação do texto, para as tabelas, as listas numeradas, tipo de fonte, etc... daí aparecerem os caracteres de formatação tipo ã6, ÌL, h5…
Os arquivos binários poderão ser mais bem explorados em um tópico avançado, vamos trabalhar inicialmente com arquivos tipo texto, que poderemos operar de maneira mais simplificada.

## biblioteca padrão fstream

Até agora temos usado a biblioteca iostream (i de input + o de output + stream), que suporta, entre várias funcionalidades, o objeto cin para ler da "standard input" (que é usualmente o teclado) e o objeto cout para "standard output" (que usualmente é o monitor)
Ora, agora queremos é ler e escrever para ficheiros(arquivos) e isso requer a biblioteca fstream (f de file + stream). Esta biblioteca define 3 novos tipos de classe:
* ofstream (apenas para saída – "out to a file". serve para criar, manipular ficheiros (arquivos) e escrever, não serve para ler).
* ifstream (apenas para entrada – "in from a file" . serve para ler ficheiros (arquivos), receber dados dos mesmos, não serve para criar nem escrever).
* fstream (este conjuga os dois tipos anteriores, "input and output to file". cria ficheiros (arquivos), escreve e lê informação dos mesmos.

## Abrir um ficheiro(arquivo)

Um ficheiro(arquivo) deve ser aberto pelo programa para que o mesmo possa ser manipulado, a abertura do arquivo implica, entre outras coisas, atribuir um identificador que nos permita ter acesso aos seus dados. É necessário criar uma linha de comunicação entre o arquivo e o objeto stream.
Podemos recorrer a dois métodos para abrir um ficheiro (arquivo):
* Usando um construtor;
* Usando a função membro chamada de "open".

## Usando o Construtor

O construtor é uma função que é automaticamente chamada quando tentamos criar uma instância de um objeto.
Os construtores de objetos podem ser sobrecarregados, ou seja, para a mesma classe podemos ter um construtor sem argumentos, com um argumento, dois argumentos, etc. No exemplo anterior criamos um sem argumentos. Os construtores não retornam valores, geralmente o compilador reporta erro quando se declara funções que retornam valor e estas têm o mesmo nome da classe, pois este nome é reservado para os construtores. Vamos dar um exemplo com dois argumento:
Chama o construtor com dois argumentos, criando uma instância de ofstream e abrindo o ficheiro(arquivo) "joao.doc" para operações de saída.

## Usando a função membro "open"

Esta função tem como primeiro argumento o nome e localização do ficheiro/(arquivo) a ser aberto, o segundo argumento especifica o modo de abertura.
Sobre a questão da localização existem 2 tipos, o "path" relativo e o "path" absoluto. Para este último indicamos o caminho todo: "c:\\....\\joao.doc" em sistemas Microsoft$^®$ ou "/home/joao/joao.odt" para sistemas UNIX$^®$ e similares. O "path" relativo dispensa essa descrição se o ficheiro/(arquivo) estiver (na mesma directoria)/(no mesmo diretório) que o programa.
Sobre a questão do modo de abertura temos as seguintes modalidades:
| Modo do abertura | sinalizador (Flag) | Descrição |
| ios::app | "Append mode" | Todos os dados do arquivo são preservados e qualquer saída é escrita a partir do fim do arquivo. |
| ios::ate | | Se o arquivo já existe,o programa vai diretamente ao seu fim.O modo de escrita é então feito de forma aleatória.Usado normalmente com arquivos do modo binário(binary mode). |
| ios::binary | "Binary mode" | Informações são escritas na forma binária e não na forma textual(text mode). |
| ios::in | "Input mode" | Leitura de informações de arquivo(não irá criar um arquivo novo) |
| ios::out | "Output mode" | Informações serão escritas no arquivo. |
| ios::trunc | | Se o arquivo já existe,suas informações serão truncadas, outra forma de se dizer: deletadas e reescritas. |
Os sinalizadores (flags) são números em potências da base binária, portanto podemos ter vários flags ao mesmo tempo se usarmos o operador unário para a operação "OU", como no exemplo abaixo:
Observe que estamos abrindo o arquivo "students.dat" em modo binário e ao mesmo tempo com o modo "append", isto significa que abriremos o arquivo e poderemos preservar o seu conteúdo anterior inserindo os novos dados no fim do arquivo.

## Comparando os dois métodos (pela função membro e pelo construtor)

O primeiro método é similar a ter
O segundo método é similar a
A escolha do melhor método em cada situação depende do contexto em que estamos criando o código, geralmente quando já temos o objeto criado e ele está fechado podemos abrir um novo arquivo com ele e depois fechá-lo novamente, isto nos sugere que usemos a função open quando o objeto deve abrir arquivos diferentes em cada trecho de código, embora que possam surgir outras funcionalidades, dependendo de como o projeto foi idealizado.

## Abrir um arquivo para leitura

A história aqui é a mesma só tem uma diferença: é que no caso de leitura, não será criado nenhum ficheiro (arquivo) caso ele não exista.
Poderíamos fazer o mesmo com o construtor:
Ou ainda
ou ainda
Há mais uma nota a fazer, se quisermos ler e escrever, não podemos usar o ofstream e o ifstream ao mesmo tempo, teremos de usar o fstream. Teremos de fazer:
Neste caso, o comportamento padrão é preservar o conteúdo do ficheiro (arquivo) ou criá-lo caso ele não exista.

## Verificar se o ficheiro (arquivo) foi aberto.

Vamos verificar o que acontece quando tentamos abrir um arquivo que não existe, a primeira versão do nosso exemplo observa o comportamento básico do fstream:
No caso do ficheiro (arquivo) “joao.doc” não existir:
No caso do ficheiro (arquivo) “joao.doc” existir no mesmo diretório que o programa:
Repare que o resultado é a impressão do endereço, do objeto a de ifstream. dá um ponteiro!!
Agora, vajamos um exemplo mais completo:
Observe que aqui verificamos se o arquivo foi aberto com a função membro is_open que retorna verdadeiro "true" caso o arquivo foi aberto, depois verificamos se o arquivo foi aberto satisfatoriamente através da função membro good, que também retorna verdadeiro se o arquivo pode ser usado.

## Fechar um ficheiro (arquivo)

Devemos fechar depois de ler e/ou escrever. Mas por que, se o objeto do ficheiro irá ser fechado assim que o programa acabar? Porque estamos a utilizar recursos com um ficheiro (arquivo) aberto, porque alguns sistemas operativos (operacionais) limitam o nº de ficheiros (arquivos) abertos, e estando este aberto impede que outros se possam abrir e por fim porque se não fecharmos, outros programas não poderão abri-lo até que o fechemos. Este comportamento faz parte de um esquema de controle de acesso usado pelo sistema para assegurar que os arquivos não serão usados por processos diferentes ao mesmo tempo.
Vamos criar um exemplo mais real. Queremos criar um programa que escreva informação inserida pelo utilizador num ficheiro por nós escolhido
Podemos ir ver o novo ficheiro/arquivo com o nome joao.doc e tem lá escrito aquilo que digitamos.
Agora vamos tentar ler o que escrevemos no documento criado.
Repare que se tivéssemos escrito duas palavras, apenas uma era apresentada (ela pára no primeiro whitespace), para isso necessitaríamos de repetir:
Agora já obtemos 2 palavras e são apresentadas em linhas diferentes. Mas temos de arranjar um método para não estar a repetir constantemente, podemos fazer isso com
Então ficamos com:

## Looping pelo ficheiro (arquivo).

E se não soubermos quantas linhas tem o arquivo? O objeto ifstream tem uma função membro que é a eof (e-end+o-of+f-file). Esta função não tem parâmetros e retorna "true" se o fim do arquivo for alcançado e "false" caso contrário. No entanto, esta função eof não é de confiança com os ficheiros (arquivos) texto como o é para os binários (é que nos ficheiros (arquivos) binários não existem espaços em branco).
A melhor alternativa é usar a função membro fail.
Refazendo tudo
Agora vamos fazer o nosso programa mais modular:
* . writeFile – para abrir um arquivo para escrita usando o ofstream e
* . readFile - ler do ficheiro (arquivo) usando o ifstream
* . Cada função irá verificar se o ficheiro (arquivo) foi aberto com sucesso

## Manipuladores

Os objetos das classes "stream" podem ser configurados para fornecer e reportar os dados de maneira pré-formatada. Da mesma maneira que temos a formatação quando usamos funções de formatação, como printf e scanf, na linguagem C, podemos usar os manipuladores na linguagem C++ para informar os objetos streams em que formato desejamos receber os dados deles ou fornecer para eles.
Abaixo temos uma série de manipuladores úteis:
| Manipulator | Uso |
| boolalpha | Faz com que variáveis tipo bool sejam reportadas como "true" ou "false". |
| noboolalhpa (padrão) | Faz com que variáveis tipo bool sejam reportadas omo 0 ou 1. |
| dec (padrão) | Determina que variáveis tipo inteiras (int) sejam reportadas na base 10. |
| hex | Determina que variáveis tipo inteiras (int) sejam reportadas em hexadecimal. |
| oct | Determina que variáveis tipo inteiras (int) sejam reportadas em octal. |
| left | Faz com que textos sejam justificados a esquerda no campo de saída. |
| right | Faz com que textos sejam justificados a direita no campo de saída. |
| internal | Faz com que o sinal de um número seja justificado a esquerda e o número seja justificado a direita. |
| noshowbase (padrão) | Desativa a exibição do prefixo que indica a base do número. |
| showbase | Ativa a exibição do prefixo que indica a base do número. |
| noshowpoint (padrão) | Mostra o ponto decimal apenas se uma parte fracionária existe. |
| showpoint | Mostra o ponto decimal sempre. |
| noshowpos (padrão) | Nenhum sinal "+" prefixado em números positivos. |
| showpos | Mostra um sinal "+" prefixado em números positivos. |
| skipws (padrão) | Faz com que espaços em branco, tabulações, novas linhas "\n" sejam descartados pelo operador de entrada >>. |
| noskipws | Faz com que espaços em branco, tabulações, novas linhas "\n" não sejam descartados pelo operador de entrada >> |
| fixed (padrão) | Faz com que números com ponto flutuante sejam mostrados em notação fixa. |
| Scientific | Faz com que números com ponto flutuante sejam mostrados em notação científica. |
| nouppercase (padrão) | 0x é mostrado para números em hexadecimal e para notação científica. |
| uppercase | 0X é mostrado para números em hexadecimal e para notação científica. |

### Ajustando a largura da entrada/saída

* setw(w) - Ajusta a largura da saída e entrada para w; precisa ser incluído.
* width(w) - Uma função membro das classes iostream.

### Preenchimento de espaços em branco

* setfill(ch) - Preenche os espaços em branco em campos de saída com ch; precisa ser incluído.
* fill(ch) - Uma função membro das classes iostream.

### Ajustando a precisão

* setprecision(n) - Ajusta a precisão de casas decimais em números com ponto flutuante, para n dígitos. Este ajuste é apenas visual, de forma que o manipulador não afeta o modo de cálculo do número pelo programa.
Exemplificando o uso de manipuladores:

## Exercícios

* Quero colocar num documento, uma lista das combinações possíveis entre a,b,c e d. com a respectiva ordenação e quantidade;
* . Quero que seja a pessoa a escolher o nome do ficheiro (arquivo) e escrever também a localização;
* . Quero que seja depois tansformado num sistema modular;
* . Encontrar uma maneira para contar o nº de espaços em branco, o nº de caracteres "." que quisermos de um dado documento.