E/S: ARQUIVOS BINÁRIOS
Na aula passada a leitura e escrita em arquivos de texto foi estudada. Nessa aula, abordaremos a entrada e saída de dados brutos. Esse modo de escrita e/ou leitura é definido justamente no campo modo do método open (ou na criação da stream utilizando o construtor).
Para abrir um arquivo para ler e escrever dados brutos, é necessário informar que o modo que o arquivo é aberto é o modo binário, i.e, basta inserir ios::binary no modo (como veremos nos exemplos).
F
LUXOSB
INÁRIOSA escrita e leitura de dados binários ocorre de maneira um pouco menos intuitiva que a leitura e escrita de texto, porém é realizada maneira semelhante à linguagem C. A leitura e/ou escrita de dados brutos é efetuada utilizando os métodos: put(), get(), read() e write().
Os métodos put() e get() realizam a escrita e leitura de um byte no arquivo, respectivamente. Quando se deseja escrever ou ler mais de um byte, é necessário informar o tamanho do bloco que se deseja ler ou escrever. Por exemplo, caso se deseje escrever um inteiro curto (short int) , é necessário avisar as funções read() e write() que um dado com dois bytes está sendo manipulado.
O comando: arqo.put(79), por exemplo, escreve um byte que equivale ao número decimal 79 (0x4F ou o caractere O) no fluxo arqo associado a um arquivo de saída. Da mesma forma, arqo.put(c) escreve no mesmo arquivo o byte armazenado na variável c (lembre-se, apenas 8 bits). Para ler um byte, basta escrever: arqi.get(c). O programa irá ler um byte do arquivo associado ao fluxo arqi e armazenar o byte na variável c.
Perceba que no Programa 1, apresentado na sequencia, é instanciado um objeto da classe ftream (e não das classes ifstream e ofstream). Isso permite que se executem tarefas de leitura e escrita em um mesmo fluxo associado ao arquivo.
É importante, todavia, informar no ato da abertura do arquivo qual operação se deseja realizar. (É possível abrir o arquivo como leitura e escrita ao mesmo tempo também, porem isso demanda uma atenção redobrada no que se refere ao posicionamento do ponteiro de captura e
#include <iostream> #include <fstream> using namespace std;
int main(void) {
//observe que aqui é criado um objeto da classe fstream //MODO: Saída (escrita), Binário, Truncar Arquivo...
fstream arq("teste.txt", ios::out | ios::binary | ios::trunc ); char c;
if(arq.is_open()) {
arq.put(79); arq.put(107); arq.close();
}
else cout << "Problema...\n";
//reabro o arquivo, porém dessa vez: //MODO: Entrada (leitura), Binário...
arq.open("teste.txt", ios::in | ios::binary ); if(arq.is_open())
{
arq.get(c); cout << c; arq.get(c);
cout << c << endl; arq.close();
}
else cout << "Problema...\n"; system("PAUSE");
return 0; }
Programa 1 Utilizando put() e get().
As funções put e get, embora simples, são limitadas. Em sua implementação básica não é possível ler nem escrever mais de um byte. Para fazer isso, pode-se utilizar as funções read() e write(). Essas funções são apresentadas abaixo:
istream &read(char *buf, streamsize num);
ostream &write(const char *buf, streamsize num);
#include <iostream> #include <fstream> using namespace std;
int main(void) {
ofstream arq("teste2.txt", ios::out | ios::binary); char c = 112;
short int si = 1232; int i = 69476;
float f = 33.653;
long double ld = 123e223; if(arq.is_open())
{
arq.write(&c, sizeof c); //escrevendo um caractere
arq.write((char *)&si, sizeof si); //escrevendo um caractere arq.write((char *)&i, sizeof i); //escrevendo um caractere arq.write((char *)&f, sizeof f); //escrevendo um caractere arq.write((char *)&ld, sizeof ld); //escrevendo um caractere }
else cout << "Problema...\n"; system("PAUSE");
return 0; }
Programa 2 write.
Para ler utilizando o método read o processo é semelhante:
#include <iostream> #include <fstream> using namespace std;
int main(void) {
ifstream arq("teste2.txt", ios::in | ios::binary); char c;
short int si; int i;
float f;
long double ld; if(arq.is_open()) {
arq.read(&c, sizeof c); //lendo um caractere cout << c << endl;
arq.read((char *)&si, sizeof si); //lendo um inteiro curto cout << si << endl;
arq.read((char *)&i, sizeof i); //etc… cout << i << endl;
arq.read((char *)&f, sizeof f); cout << f << endl;
arq.read((char *)&ld, sizeof ld); cout << ld << endl;
}
else cout << "Problema...\n"; system("PAUSE");
return 0; }
Programa 3 read.
o tipo de dado. De certa forma isso se torna uma vantagem, pois possibilita ler e escrever arrays em um só comando. Estude atentamente o Programa 4.
#include <iostream> #include <fstream> using namespace std;
int main(void) {
fstream arq("teste2.txt", ios::out | ios::binary | ios::trunc ); int si[3] = {0x1234,0x5678,0x9ABC};
int i[3]; char a;
if(arq.is_open()) {
arq.write((char *)&si, sizeof si); //escrevendo vetor arq.close();
}
else cout << "Problema...\n";
arq.open("teste2.txt", ios::in | ios::binary); if(arq.is_open())
{
arq.read((char *)&i, sizeof i); //lendo vetor arq.close();
}
else cout << "Problema...\n";
for(a = 0; a < 3; a++ ) cout << i[a] << endl; system("PAUSE");
return 0; }
Programa 4 Escrevendo um vetor.
Observe que o vetor é lido e escrito diretamente, sem a necessidade de um loop.
A
CESSOA
LEATÓRIOPode-se acessar um arquivo de uma forma não sequencial, para isso, é necessário saber em que ponto a extração ou inserção será realizada. Para tanto, utiliza-se o ponteiro de captura e o ponteiro de extração e as funções seekg e seekp.
As funções seekg e seekp movem os ponteiros a partir de uma origem definida. Basicamente, as funções seekg e seekp são idênticas, porem manipulam o ponteiro para captura (seekg) e o ponteiro para inserção (seekp). Esses métodos funcionam da seguinte maneira:
Arq_saida.seekp(offset, origem); Arq_entrada.seekg(offset, origem);
Tabela 1 Referencia para offset nos métodos seek.
Origem Explicação
ios::beg Referencia a movimentação do cursor em relação ao início do arquivo.
ios::cur Referencia a movimentação do cursor em relação a posição atual do cursor.
ios::end Referencia a movimentação do cursor em relação ao final do arquivo.
É possível saber a posição dos ponteiros utilizando os métodos tellp e tellg. Estude com muita atenção o programa abaixo.
#include <iostream> #include <fstream> #include <cstdlib> using namespace std;
int main(void)
{ ofstream arq("teste_cursores.txt", ios::out | ios::binary | ios::trunc ); ifstream arq_in; char c; int i,num; srand(time(NULL));
if(arq.is_open())
{ //cria um arquivo com números aleatórios... for ( i = 0; i < 10; i++ )
{
cout << "Posicao do cursor de insercao: " << arq.tellp() << endl; num = rand()%100;
arq.write((char *)&num, sizeof num); }
arq.seekp(0,ios::beg);
cout << "Posicao do cursor de insercao: " << arq.tellp() << endl; arq.seekp(10,ios::beg);
cout << "Posicao do cursor de insercao: " << arq.tellp() << endl; arq.put('X');
cout << "Posicao do cursor de insercao: " << arq.tellp() << endl; arq.seekp(0,ios::end);
cout << "Posicao do cursor de insercao: " << arq.tellp() << endl; arq.seekp(7,ios::end);
cout << "Posicao do cursor de insercao: " << arq.tellp() << endl; arq.put('.');
arq.close(); }
else cout << "Problema ao abrir arquivo...\n";
arq_in.open("teste_cursores.txt", ios::in | ios::binary ); if(arq_in.is_open())
{
cout << "Posicao do cursor de captura: " << arq_in.tellg() << endl; arq_in.seekg(10,ios::beg);
cout << "Posicao do cursor de captura: " << arq_in.tellg() << endl; arq_in.get(c);
cout << c << endl;
cout << "Posicao do cursor de captura: " << arq_in.tellg() << endl; arq_in.read((char *)&num, sizeof num);
cout << num << endl;
cout << "Posicao do cursor de captura: " << arq_in.tellg() << endl; arq_in.seekg(-1,ios::end);
cout << "Posicao do cursor de captura: " << arq_in.tellg() << endl; arq_in.get(c);
cout << c << endl;
cout << "Posicao do cursor de captura: " << arq_in.tellg() << endl; arq_in.get(c);
if(arq_in.eof()) cout << "Final do arquivo...\n"; arq_in.close();
}
else cout << "Problema ao abrir arquivo...\n"; system("PAUSE");
return 0; }
A maioria dos arquivos trabalha com dados binários brutos. Por exemplo, a estrutura de um arquivo de som com extensão tipo *.wav é apresentada abaixo:
Figura 1 Exemplo de arquivo .wav (retirado de https://ccrma.stanford.edu/courses/422/projects/WaveFormat/)
Note que, além dos dados brutos de som (amostras) o arquivo trás muitas informações. Cabe ao programa (e quem fez o programa) processar esses dados da maneira correta.