Buscar

17 - Capítulo 14 - Processamento de Arquivos

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 44 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 44 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 44 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

14 
Processamento de arquivos 
Objetivos 
• Ser capaz de criar, ler, gravar e atualizar arquivos. 
• Familiarizar-se com o processamento de arquivos de acesso seqüencial. 
• Familiarizar-se com o processamento de arquivos de acesso aleatório. 
• Ser capaz de especificar operações de EIS não-formatadas de alto desempenho. 
• Compreender a diferença entre o processamento de arquivos com dados formatados e dados brutos. 
• Construir um programa de processamento de transações com processamento aleatório de arquivos. 
Li parte dele até ofim. 
Samuel Goldwyn 
Só posso supor que um documento marcado como “Não arquivar” está arquivado em um documento marcado como “Não arquivar”. 
Senador Frank Church 
Depoimento ao Subcomitê de Inteligência do Senado, 1975 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 727 
Visão geral 
14.1 Introdução 
14.2 A hierarquia de dados 
14.3 Arquivos e streams 
14.4 Criando um arquivo de acesso seqüencial 
14.5 Lendo dados de um arquivo de acesso seqüencial 
14.6 Atualizando arquivos de acesso seqüencial 
14.7 Arquivos de acesso aleatório 
14.8 Criando um arquivo de acesso aleatório 
14.9 Gravando dados aleatoriamente em um arquivo de acesso aleatório 
14.10 Lendo dados seqüencialmente de um arquivo de acesso aleatório 
14.11 Exemplo: um programa de processamento de transações 
14.12 Entrada/saída de objetos 
Resumo• Terminologia. Erros comuns de programação . Boa prática de programação Dica de desempenho 
Dica de portabilidade . Exercícios de auto-revisão Respostas aos exercícios de auto-revisão • Exercícios 
14.1 Introdução 
O armazenamento de dados em variáveis e arrays é temporário. Arquivos são usados para conservação permanente de grandes quantidades de dados. Os computadores armazenam arquivos em dispositivos secundários de armazenamento, tais como discos magnéticos, discos ópticos e fitas. Neste capítulo, explicamos como os arquivos de dados São criados, atualizados e processados por programas em C++. Examinamos aqui tanto os arquivos de acesso seqüencial como os arquivos de acesso aleatório. Comparamos o processamento de arquivos com dados formatados e dados não-formatados. Examinamos técnicas para a entrada de dados de, e a saída de dados para, strings, em vez de arquivos, no Capítulo 19. 
14.2 A hierarquia de dados 
Basicamente, todos os itens de dados processados por um computador são reduzidos a combinações de zeros e uns. Isso ocorre porque é simples e econômico se construir dispositivos eletrônicos que podem assumir dois estados estáveis - um dos estados representa O e o outro representa 1. E notável que as impressionantes funções executadas pelos computadores envolvam apenas as manipulações mais elementares de Os e is. 
O menor item de dados em um computador pode assumir o valor O ou o valor 1. Tal item de dados é chamado de bit (abreviação de “binary digit”, ou “dígito binário” - um dígito que pode assumir um de dois valores). Os circuitos computacionais realizam várias manipulações simples de bits, tais como determinar o valor de um bit, redefinir o valor de um bit e inverter o valor de um bit (de 1 para O ou de O para 1). 
E complicado para os programadores trabalhar com dados na forma de baixo nível dos bits. Em vez disso, os programadores preferem trabalhar com dados na forma de dígitos decimais (i.e., O, 1, 2, 3, 4, 5, 6, 7, 8 e 9), letras (i.e., de A até Z e de a até z) e símbolos especiais (i.e., $, @, %, &, , (, ), -, +,“, :, ?,Ie muitos outros). Dígitos, letras e símbolos especiais são chamados de caracteres. O conjunto de todos os caracteres usados para escrever programas e representar itens de dados em um determinado computador é chamado de conjunto de caracteres daquele computador. Como os computadores podem processar apenas is e Os, qualquer caractere do conjunto de caracteres de um computador é representado por uma combinação de is e Os (chamada de byte). Atualmente, os bytes são compostos normalmente de oito bits. Os programadores criam programas e itens de dados como caracteres; os computadores manipulam e processam esses caracteres como combinações de bits. 
728 C++ COMO PROGRAMAR 
Da mesma forma que os caracteres são compostos de bits, os campos são compostos de caracteres. Um campo é um grupo de caracteres que possui um significado. Por exemplo, um campo consistindo unicamente em letras maiúsculas e minúsculas pode ser usado para representar o nome de uma pessoa. 
Os itens de dados processados pelos computadores formam uma hierarquia de dados na qual os itens de dados se tornam maiores e mais complexos na estrutura à medida que evoluímos de bits para caracteres, campos e assim por diante. 
Um registro (i.e., uma struct ou class em C++) é composto de vários campos (chamados de membros 
em C++). Em um sistema de folha de pagamento, por exemplo, um registro de um determinado empregado pode consistir nos seguintes campos: 
1. Número de identificação de empregado 
2. Nome 
3. Endereço 
4. Valor do salário-hora 
5. Número de dispensas solicitadas 
6. Total de vencimentos no ano em curso 
7. Total de impostos federais retidos na fonte, etc. 
Assim, um registro é um grupo de campos relacionados. No exemplo anterior, cada um dos campos pertence ao mesmo empregado. Naturalmente, uma empresa específica pode ter muitos empregados e terá um registro da folha de pagamento para cada um. Um arquivo é um grupo de registros relacionados. Um arquivo de folha de pagamento de uma empresa contém um registro para cada empregado. Assim, o arquivo de folha de pagamento de uma pequena empresa pode conter apenas 22 registros, ao passo que o arquivo da folha de pagamento de uma grande empresa pode conter 100.000 registros. Não é raro uma organização ter muitos arquivos, cada um deles possuindo milhões de caracteres de informações. A Fig. 14.1 ilustra a hierarquia de dados. 
Para facilitar a recuperação de registros específicos de um arquivo, pelo menos um campo em cada registro é escolhido como uma chave de registro. A chave de registro identifica um registro como pertencendo a uma determinada pessoa ou entidade que é distinto de todos os outros registros no arquivo. No registro de folha de pagamento descrito anteriormente, o número de identificação do empregado seria normalmente escolhido como a chave dos registros. 
Sally Ireto 1 
ITom 1Azu1 1 
Judy Iverae 1 1 Arquivo 
Verde JJ Registro 
t 
Judy Campo 
t 
01001010 Byte (Caractere ASCII J) 
1 
Fig. 14.1 A hierarquia de dados. 
Iris 
ILarafia 1 
1 
1 
Randy 
It1h0I 
1 
1 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 729 
Há muitas maneiras de se organizar registros em um arquivo. No tipo mais comum de organização, denominado arquivo seqüencial, registros são normalmente armazenados em ordem segundo o campo-chave dos registros. Em um arquivo de folha de pagamento, os registros geralmente são colocados em ordem segundo o número de identificação do empregado. O registro do primeiro empregado no arquivo contém o menor número de identificação do empregado, e os registros subseqüentes possuem números de identificação do empregado em ordem crescente. 
A maioria das empresas utiliza muitos arquivos diferentes para armazenar dados. Por exemplo, as empresas podem ter arquivos de folha de pagamento, arquivos de contas a receber (listando o dinheiro devido pelos clientes), arquivos de contas a pagar (listando o dinheiro devido aos fornecedores), arquivos de estoques (listando características a respeito de todos os itens manipulados pela empresa) e muitos outros tipos de arquivos. Algumas vezes, um grupo de arquivos relacionados entre si é chamado de banco de dados. Um conjunto de programas que se destina a criar e gerenciar bancos de dados é chamado de sistema de gerenciamento de banco de dados (SGBD, ou database management system, DBMS). 
14.3 Arquivos e streams 
C++ vê cada arquivo simplesmente como uma seqüência de bytes (Fig. 14.2). Cada arquivo termina ou com um marcador defim de arquivo ou em um byte específico, cujo número é gravado em uma estrutura administrativa de dados mantida pelo sistema. Quando um arquivo é aberto, um objetoé criado e um stream é associado àquele objeto. No Capítulo II, vimos que quatro objetos são criados para nós - cm. cout. cerr e clog - quando <iostreani> é incluído. Os streams associados com estes objetos fornecem canais de comunicação entre um programa e um arquivo ou dispositivo particular. Por exemplo, o objeto cm (o objeto stream padrão de entrada) permite que um programa leia dados do teclado ou de outros dispositivos, o objeto cout (o objeto stream padrão de saída) permite que um programa exiba dados na tela ou em outros dispositivos e os objetos cerr e clog (os objetos stream padrão de erros) permitem a um programa enviar mensagens de erro para a tela ou outros dispositivos. 
i O 1 2 3 4 5 6 8 9 marcador de fim de arquivo 
Fig. 14.2 Como a linguagem C++ visualiza um arquivo de n bytes. 
Para executar o processamento de arquivos em C++, devem ser incluídos os arquivos de cabeçalho <iostream> e <fstream>. O cabeçalho <fstream> inclui as definições para as classes stream ifstream (para fazer entrada de um arquivo), ofstream (para fazer saída para um arquivo) e fstream (para entrada e saída de um arquivo). Os arquivos são abertos criando-se objetos destas classes stream. Estas classes stream são derivadas das (i.é, herdam a funcionalidade de) classes istreain, ostream e iostream. respectivamente. Assim, as funções membro, operadores e manipuladores descritos no Capítulo 11, “Entradalsaída com streams em C++”, podem todos também ser aplicados a streams de arquivos.Os relacionamentos de herança das classes de EIS discutidas até aqui são resumidas na Fig. 14.3. 
14.4 Criando um arquivo de acesso seqüencial 
C++ não impõe nenhuma estrutura a um arquivo. Assim, conceitos como o de “registro” não existem em arquivos em C++. Portanto, o programador deve estruturar os arquivos de modo a satisfazer as exigências das aplicações. No exemplo a seguir, vemos como o programador pode impor uma estrutura de registros simples a um arquivo. Primeiro, apresentamos o programa e, então, o analisamos em detalhe. 
A Fig. 14.4 cria um arquivo simples de acesso seqüencial que pode ser usado em um sistema de contas a receber para ajudar a controlar as quantias devidas pelos clientes devedores de uma empresa. Para cada cliente, o programa obtém um número de conta, o nome do cliente e o saldo do cliente (i.e., a quantia que o cliente deve à empresa por bens e serviços recebidos no passado). Os dados obtidos para cada cliente constituem um registro para aquele cliente. O número da conta é usado como campo-chave dos registros nessa aplicação isto é, o arquivo será 
730 C++ COMO PROGRAMAR 
jos 
istream ostream 
/N 
ifstream iostream ofstream 
fs tream 
Fig. 14.3 Parte da hierarquia de classes de EIS com streams. 
criado e mantido segundo a ordem dos números de contas. Esse programa assume que o usuário fornece os registros na ordem dos números das contas. Em um grande sistema de contas a receber, seria fornecido um recurso de ordenação para que o usuário pudesse entrar com os registros em qualquer ordem. Os registros seriam então ordenados e gravados no arquivo. 
1 II Fig. 14.4: figl4_04.cpp 
2 // Criando um arquivo seqüencial 
3 #include <iostream> 
4 
5 using std::cout; 
6 using std: :cin; 
7 using std: :ios; 
8 using std: :cerr; 
9 using std::endl; 
lo 
11 #include <fstream> 
12 
13 using std::ofstreani; 
14 
15 #include <cstdlib> 
16 
17 int main() 
18 
19 II construtor de ofstream abre o arquivo 
20 ofstream outClientFile( “clients.dat”, ios::out ); 
21 
22 if ( ‘outClientFile ) { II operador ! sobrecarregado 
23 cerr « “Arquivo não pode ser aberto « endi; 
24 exit( 1 ); II protótipo em cstdlib 
25 
26 
27 cout « “Digite a conta, o nome e o saldo.\n” 
28 « “Digite fim de arquivo para terminar entrada.\n? “; 
29 
30 int account; 
31 char name[ 30 ]; 
32 double balance; 
33 
34 while ( cm » account » name » balance ) 
Fig. 14.4 Criando um arquivo seqüencial (parte 1 de 2). 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 731 
35 outClientFile « account « « naxne 
36 « « balance « ‘\n’; 
37 cout « “? 
38 
39 
40 return 0; // destruidor de ofstream fecha o arquivo 
41 
Digite a conta, o nome e o saldo. 
Digite fim de arquivo para terminar entrada. 
? 100 Jones 24.98 
? 200 Doe 345.67 
? 300 White 0.00 
? 400 Stone -42.16 
? 500 Rich 224.62 
? AZ 
Fig. 14.4 Criando um arquivo sequerluial parte ue ). 
Agora, examinemos esse programa. Como afirmado anteriormente, arquivos são abertos criando-se objetos das classes stream ifstream. ofstream ou fstream. Na Fig. 14.4,0 arquivo deve ser aberto para saída, assim um objeto ofstream é criado. Dois argumentos são passados para o construtor do objeto - o nome do arquivo e o modo de abertura do arquivo. Para um objeto ofstream, o modo de abertura do arquivo pode ser tanto ios: : out para fazer saída para um arquivo como ios: app para acrescentar dados ao final de um arquivo (sem modificar qualquer dado já existente no arquivo). Arquivos existentes abertos com modo ios: out são truncados - todos os dados no arquivo são eliminados. Se o arquivo especificado ainda não existe, então é criado um arquivo com aquele nome de arquivo. 
A declaração 
ofstream outClientFile( “clients.dat”, ios::out ); 
na linha 20 cria um objeto ofstreani de nome outClientFile associado ao arquivo clients . dat que é aberto para saída. Os argumentos “clients . dat” e ios: : out são passados para o construtor de ofstreaxn. que abre o arquivo. Isto estabelece uma “linha de comunicação” com o arquivo. Por default, objetos ofstream são abertos para saída, de modo que o comando 
ofstream outClientFile( “clients.dat” ); 
poderia ter sido usado para abrir clients . dat para saída. A Fig. 14.5 lista os modos de abertura de arquivos. 
Fig. 14.5 Modos de abertura de arquivos. 
Erro com um de programação 14.1 
Abrir um arquivo existente para saída (ios: : out) quando, na verdade, o usuário quer preservar o arquivo; o conteúdo do arquivo é descartado sem aviso. 
Modo 
Descrição 
ios: 
: app 
Grava toda a saída no fim do arquivo. 
ios: 
ate 
Abre um arquivo para saída e avança até o fim do arquivo (normalmente usado para acrescentar dados a um arquivo). Os dados podem gravados em qualquer lugar no arquivo. 
ios: 
: in 
Abre um arquivo para entrada. 
ios: 
out 
Abre um arquivo para saída. 
ios: 
: trunc 
Elimina o conteúdo do arquivo se ele existe (esta também é a ação default para ios: : out) 
ios: 
: binary 
Abre um arquivo para entrada ou saída binária (i.e., não-texto). 
732 C++ COMO PROGRAMAR 
Erro comum de programação 14.2 
Usar um objeto ofstream incorreto para se referir a um arquivo. 
Um objeto ofstream pode ser criado sem abrir um arquivo específico - um arquivo pode ser associado ao objeto posteriormente. Por exemplo, a declaração 
ofstream outclientFile; 
cria o objeto ofstreain de nome outClientFile. A função membro open de ofstream abre um arquivo e o associa a um objeto ofstream existente como segue: 
outClientFile.open( “clients.dat”, ios: :out ); 
Erro comum de programação 14.3 
Não abrir um arquivo antes de tentar referenciá-lo em um programa. 
Após criar um objeto ofstream e tentar abri-lo, o programa testa se a operação de abertura foi bem-sucedida. A estrutura if nas linhas 22 a 25 
if ( !outClientFile ) { II operador sobrecarregado 
cerr « ‘Arquivo não pode ser aberto” « endi; 
exit( 1 ); // protótipo em cstdlib 
usa a função membro operador operator! sobrecarregada de ios para determinar se a operação de abertura foi bem-sucedida. A condição retorna um valor não-zero (verdadeiro) se ou o failbit ou o badbit é ligado para o stream na operação open. Alguns erros que podem acontecer são: tentar abrir um arquivo não-existente para leitura, tentar abrir um arquivo para leitura sem permissão e abrir um arquivo para gravação quando não há espaço disponível em disco. 
Quando a condição indica que a tentativa de abertura foi mal-sucedida, a mensagem de erro “Arquivo não pode ser aberto’ é exibida e a função exit termina o programa. O argumento para exit é retornado para o ambiente do qual o programa foi invocado. Oargumento O indica que o programa terminou normalmente; qualquer outro valor indica que o programa terminou devido a um erro. O ambiente chamador (muito provavelmente o sistema operacional) usa o valor retornado por exit para responder adequadamente ao erro. 
Uma outra função membro operador sobrecarregada de ios - operator void* - converte o stream em um ponteiro, de modo que ele possa ser testado como O (o ponteiro nulo) ou não-zero (qualquer outro valor de ponteiro). Se o failbit ou o badbit (ver Capítulo II) foram ligados para o stream, O (falso) é retornado. A condição no cabeçalho de while seguinte (linha 34) invoca implicitamente a função membro operator void*: 
while ( cm » account » name » balance 
A condição permanece true enquanto nem o failbit nem obadbit forem ligados para cm. Digitar o indicador de fim de arquivo liga o failbit para cm. A função operator void * pode ser usada para testar um objeto de entrada quanto ao fim de arquivo em vez de chamar explicitamente a função membro eof para o objeto de entrada. 
Se o arquivo é aberto com sucesso, o programa começa a processar dados. O comando seguinte (linhas 27 e 28) solicita ao usuário para digitar os diversos campos para cada registro, ou para digitar fim de arquivo quando a entrada de dados está completa: 
cout « “Digite a conta, o nome e o saldo.\n” 
« “Digite fim de arquivo para terminar entrada.\n? “; 
A Fig. 14.6 lista as combinações de teclas para digitar fim de arquivo em diversos sistemas de computadores. 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 733 
Fig. 14.6 Combinações de teclas que indicam fim de arquivo para vários sistemas computacionais populares. 
A linha 34 
while ( cm » account » name » balance 
extrai cada conjunto de dados e determina se fim de arquivo foi digitado. Quando o fim de arquivo ou dados inválidos são digitados, a operação » de extração do stream de cm retorna O (normalmente operator void* 
retorna verdadeiro) e a estrutura whi le termina. O usuário digita fim de arquivo para informar ao programa que não há mais dados para serem processados. O indicador de fim de arquivo é inicializado quando a combinação de teclas que indica fim de arquivo é digitada pelo usuário. A estrutura while continua a executar o laço enquanto o indicador de fim de arquivo não for digitado. 
As linhas 35 e 36 
outClientFile « account « « name 
« ‘ « balance « \n’; 
gravam um conjunto de dados no arquivo “clients .dat” usando o operador « de inserção em stream e o objeto outClientFile associado com o arquivo no começo do programa. Os dados podem ser recuperados por um programa projetado para ler o arquivo (ver Seção 14.5). Note que o arquivo criado na Fig. 14.4 é um arquivo- texto. Ele pode ser lido por qualquer editor de texto. 
Uma vez que o indicador de fim de arquivo é digitado, main termina. Isto faz com que o objeto outClientFile seja destruído, invocando desta forma sua função destruidor, a qual fecha o arquivo clients . dat. Um objeto ofstream pode ser fechado explicitamente pelo programador usando a função membro dose como segue: 
outClientFile . dose O; 
Dica de desempenho 14.1 
______ Feche explicitamente cada arquivo tão logo se saiba que o programa não fará referência ao arquivo novamente. isso pode reduzir o consumo de recursos no programa que vai continuar sendo executado 
após não mais necessitar de um arquivo particular Essa prática também melhora a clareza do programa. 
No exemplo de execução do programa da Fig. 14.4, o usuário digita informações para cinco contas e, então, sinaliza que a entrada de dados está completa digitando-se fim de arquivo (AZ aparece nas telas de compatíveis com o IBM PC). Esta janela de diálogo não mostra como os registros de dados efetivamente aparecem no arquivo. Para verificar se o arquivo foi criado com sucesso, na próxima seção criamos um programa para ler o arquivo e imprimir seu conteúdo. 
14.5 Lendo dados de um arquivo de acesso seqüencial 
Os dados são armazenados em arquivos de modo que possam ser recuperados para processamento quando necessário. A seção anterior demonstrou como criar um arquivo para acesso seqüencial. Nesta seção, analisaremos como ler dados seqüencialmente de um arquivo. 
Sistema computacional 
Combinação de teclas 
Sistemas UNIX 
<ctrl> d (em uma linha isolada) 
IBM PC e compatíveis 
<ctrl> z (às vezes seguido pelo pressionamento da tecla Enter) 
Macintosh 
<ctrl> d 
VAX (VMS) 
<ctrl> z 
734 C++ COMO PROGRAMAR 
A Figura 14.7 lê registros do arquivo “clients dat” criados pelo programa da Fig.14.4 e imprime o 
conteúdo dos registros. Arquivos são abertos para entrada criando-se um objeto da classe ifstream. Dois argumentos são passados para o objeto - o nome do arquivo e o modo de abertura do arquivo. A declaração 
ifstream inClientFile( “clients.dat”, ios::in ); 
na linha 29 cria um objeto ifstream denominado inClientFile e o associa com o arquivo clients dat que será aberto para entrada. Os argumentos entre parênteses são passados à função construtor de ifstream, a qual abre o arquivo e estabelece uma “linha de comunicação” com o arquivo. 
1 // Fig. 14.7: figl4O7.cpp 
2 // Lendo e imprimindo um arquivo seqüencial 
3 #include <iostreain> 
4 
5 using std: :cout; 
6 using std::cin; 
7 using std::ios; 
8 using std: :cerr; 
9 using std: :endl; 
10 
11 #include <fstream> 
12 
13 using std::ifstream; 
14 
15 #include <iomanip> 
16 
17 using std::setiosflags; 
18 using std::resetiosflags; 
19 using std::setw; 
20 using std::setprecision; 
21 
22 #include <cstdlib> 
23 
24 void outputLine( int, const char * const, double ); 
25 
26 int main() 
27 { 
28 II construtor de ifstream abre o arquivo 
29 ifstream inClientFile( “clients.dat”, ios::in ); 
30 
31 if ( inC1ientFi1e ) { 
32 cerr « “Arquivo não pode ser aberto\n; 
33 exit( 1 ); 
34 1 
35 
36 int account; 
37 char name[ 30 ]; 
38 double balance; 
39 
40 cout « setiosflags( ios::left ) « setw( 10 ) « “Conta’ 
41 « setw( 13 ) « “Nome” « “Saldo\n” 
42 « setiosflags( ios::fixed 1 ios::showpoint ); 
43 
44 while ( inClientFile » account » name » balance 
45 outputLine( account, name, balance ); 
46 
Fig. 14.7 Lendo e imprimindo um arquivo seqüencial (parte 1 de 2). 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 735 
47 
48 } 
49 
50 void outputLine( int acct, const char * const name, double bal 
51 
52 cout « setiosflags( ios::left ) « setw( 10 ) « acct 
53 « setw( 13 ) « name « setw( 7 ) « setprecision( 2 
54 « resetiosflags( ios: :left 
55 « bal « ‘\n’; 
56 } 
Fig. 14.7 Lendo e imprimindo um arquivo seqüencial (parte 2 de 2). 
Objetos da classe ifstream são abertos para entrada por default; assim, o comando 
ifstream inclientFile( ‘clients.dat” ); 
poderia ter sido usado para abrir clients . dat para entrada. Exatamente como com um objeto ofstreain, um objeto ifstream pode ser criado sem abrir um arquivo específico e um arquivo pode ser associado a ele mais tarde. 
Boa prática de programação 14.1 
Abrir um arquivo somente para entrada (usando ios: : in), se o conteúdo do arquivo não deve ser modificado. isso evita a modificação não-intencional do conteúdo do arquivo. Esse é um exemplo do princípio do privilégio mínimo. 
O programa usa a condição ! inClientEile para determinar se o arquivo foi aberto com sucesso, antes de tentar recuperar dados do arquivo. A linha 44 
while ( inClientFile » account » name » balance 
lê um conjunto de dados (i.e., um registro) do arquivo. Após a linha precedente ser executada pela primeira vez, account tem o valor 100, name tem o valor “Jones” e balance tem o valor 24 . 98. Cada vez que a linha é executada, um outro registro é lido do arquivo para as variáveis account. name e balance. Os registros são exibidos usando-se a função outputLine. a qual usa manipuladores de stream parametrizados para formatar os dados para exibição. Quando o fim do arquivo é encontrado, a chamada implícita para operator void* na estrutura while retorna O (normalmente operator void* retorna verdadeiro), o arquivo é fechado pela função destruidor de ifstream e o programa termina. 
Para recuperar seqüencialmentedados de um arquivo, os programas normalmente começam a ler a partir do início do arquivo e lêem todos os dados, um após o outro, até que os dados desejados sejam encontrados. Pode ser necessário processar o arquivo seqüencialmente várias vezes (desde o início do arquivo) durante a execução de um programa. Tanto a classe is treaxn como a classe os tream fornecem funções membro para reposicionar o ponteiro de posição do arquivo (o número de byte do próximo byte do arquivo a ser lido ou gravado). Estas funções membro são seekg (“seek get”) para a classe istream e seekp (“seek put”) para a classe ostream. Cada objeto istream tem um “ponteiro get” que indica o número do byte do arquivo do qual a próxima leitura deve ocorrer e cada objeto ostrearn tem um “ponteiro put” que indica o número do byte do arquivo no qual a próxima saída deverá ser colocada. O comando 
return 0; // destruidor de ifstream fecha o arquivo 
inclientFile.seekg( O ); 
Conta 
Nome 
Saldo 
100 
Jones 
24.98 
200 
Doe 
345.67 
. 
300 
White 
0.00 
400 
Stone 
-42.16 
500 
Rich 
224.62 
736 C++ COMO PROGRAMAR 
reposiciona o ponteiro de posição do arquivo no começo do arquivo (posição O) associado com inClientFile. O argumento para seekg é normalmente um inteiro long. Um segundo argumento pode ser especificado para indicar a direção de seek. A direção de seek pode ser ios: : beg (o default), para posicionamento relativo ao início de um stream, ios: : cur para posicionamento relativo à posição corrente em um stream ou ios: : end para posicionamento relativo ao fim de um stream. O ponteiro de posição do arquivo é um valor inteiro que especifica a posição no arquivo como um número de bytes desde a posição de início do arquivo (isso é, algumas vezes, chamado de offset - ou deslocamento - desde o começo do arquivo). Alguns exemplos de posicionamento do ponteiro get de posição do arquivo são: 
II posiciona no n-ésimo byte do fileObject 
// assume ios: :beg 
fileObject.seekg( n ); 
II posiciona n bytes para a frente no fileObject 
fileObject.seekg( n, ios::cur ); 
II posiciona y bytes para trás a partir do fim do fileObject 
fileObject.seekg( y, ios::end ); 
// posiciona no fim do fileObect 
fileObject.seekg( O, ios::end ); 
As mesmas operações podem ser executadas com a função membro seekp de ostream. As funções membro tellg e tellp são fornecidas para retornar as posições correntes dos ponteiros gel e put, respectivamente. O comando seguinte atribui o valor do ponteiro de posição get do arquivo para a variável location do tipo long: 
location = fileObect.tellg 
A Fig. 14.8 possibilita a um gerente de crédito exibir as informações da conta para aqueles clientes com saldo zero ______ (i.e., clientes que não devem dinheiro à empresa), saldos credores (i.e., clientes para os quais a empresa deve dinheiro) e saldos devedores (i.e., clientes que devem dinheiro à empresa por bens e serviços recebidos no passado). O programa exibe um menu e permite ao gerente de crédito digitar uma de três opções para obter informações de crédito. A opção 1 produz uma lista de contas com saldo zero. A opção 2 produz uma lista de contas com saldos credores. A opção 3 produz uma lista de contas com saldos devedores. A opção 4 termina a execução do programa. Digitar uma opção inválida simplesmente exibe a solicitação para digitar uma outra opção. A saída do programa é mostrada na Fig. 14.9. 
1 1/ Fig. 14.8: figl4O8.cpp 
2 II Programa de consulta de crédito 
3 #include <iostream> 
4 
5 using std::cout; 
6 using std::cin; 
7 using std::ios; 
8 using std: :cerr; 
9 using std::endl; 
10 
11 #include <fstream> 
12 
13 using std: :ifstream; 
1 14 
15 #include <iomanip> 
16 
17 using std::setiosflags; 
18 using std::resetiosflags; 
Fig. 14.8 Programa de consulta de crédito (parte 1 de 3). 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 737 
19 using std::setw; 
20 using std: :setprecision; 
21 
22 #include <cstdlib> 
23 
24 enum RequestType { ZERO BALANCE = 1, CREDIT BALANCE, 
25 DEBIT BALANCE, END }; 
26 int getRequestO; 
27 bool shouldDisplay( int, double ); 
28 void outputLine( int, const char * const, double ); 
29 
30 int main() 
31 { 
32 II construtor de ifstream abre o arquivo 
33 ifstream inClientFile( “clients.dat”, ios: :in ); 
34 
35 if ( !inClientFile ) { 
36 cerr « ‘Arquivo não pode ser aberto” « endl; 
37 exit( 1 ); 
38 } 
39 
40 int request, account; 
41 char name[ 30 ); 
42 double balance; 
43 
44 cout « “Digite opção\n” 
45 « “ 1 - Listar contas com saldo zero\n” 
46 « “ 2 - Listar contas com saldo credor\n” 
47 « “ 3 - Listar contas com saldo devedor\n” 
48 « “ 4 - Fim da execução” 
49 « setiosflags( ios: :fixed 1 ios: ;showpoint ); 
50 request = getRequestQ; 
51 
52 while ( request != END ) { 
53 
54 switch ( request 
55 case ZERO BALANCE: 
56 cout « “\nContas com saldo zero:\n”; 
57 break; 
58 case CREDIT BALANCE: 
59 cout « “\nContas com saldo credor:\n”; 
60 break; 
61 case DEBIT BALANCE: 
62 cout « “\nContas com saldo devedor:\n”; 
63 break; 
64 
65 
66 inClientFile » account » naxne » balance; 
67 
68 while ( !inClientFile.eof O) 
69 if ( shouldDisplay( request, balance 
70 outputLine( account, name, balance 
71 
72 inClientFile » account » name » balance; 
73 } 
74 
75 inClientFile.clearO; II desliga fim de arquivo p/próxima entrada 
76 inClientFile.seekg( O ); II vai para o início do arquivo 
77 request getRequestO); 
Fig. 14.8 Programa de consulta de crédito (parte 2 de 3). 
738 C÷+ COMO PROGRAMAR 
78 } 
79 
80 cout « Fim da execução.” « endi; 
81 
82 return 0; // destruidor de ifstream fecha o arquivo 
83 ) 
84 
85 int getRequest() 
86 { 
87 int request; 
88 
89 do{ 
90 cout « “\n? 
91 cm » request; 
92 } while( request < ZERO_BALANCE && request > END ); 
93 
94 return request; 
95 } 
96 
97 bool shouldDisplay( int type, double balance 
98 { 
99 if ( type == CREDIT BALANCE && balance < O 
100 return true; 
101 
102 if ( type == DEBIT BALANCE && balance > O 
103 return true; 
104 
105 if ( type ZERO BALANCE && balance == O 
106 return true; 
107 
108 return false; 
109 } 
110 
111 void outputLine( int acct, const char * const name, double bal 
112 
113 cout « setiosflags( ios::left ) « setw( 10 ) « acct 
114 « setw( 13 ) « name « setw( 7 ) « setprecision( 2 
115 « resetiosflags( ios::left 
116 « bal « ‘\n; 
117 ) 
Fig. 14.8 Programa de consulta de crédito (parte 3 de 3). 
Digite opção 
1 - Listar contas com saldo zero 
2 - Listar contas com saldo credor 
3 - Listar contas com saldo devedor 
4 - Fim da execução 
Contas com saldo zero: 
300 White 0.00 
Contas com saldo credor: 
400 Stone -42.16 
Fig. 14.9 Exemplo de saída do programa da Fig. 14.8 (parte 1 de 2). 
CAPÍTULO 14- PROCESSAMENTO DE ARQUIVOS 739 
Fig. 14.9 Exemplo de saída do programa da Fig. 14.8 (parte 2 de 2). 
14.6 Atualizando arquivos de acesso seqüencial 
Dados que são formatados e gravados em um arquivo de acesso seqüencial como mostrado na Seção 14.4 não podem ser modificados sem o risco de destruir outros dados no arquivo. Por exemplo, se o nome “White” precisa ser mudado para Worthington, o nome velho não pode ser simplesmente gravado por cima, O registro para White foi gravado no arquivo como 
300 White 0.00 
Se esse registro fosse regravado começando na mesma posição no arquivo e usando o nome mais longo, o registro ficaria 
300 Worthington 0.00 
O novo registro contém seis caracteres a mais que o registro original. Portanto, os caracteres depois do segundo “o” 
em “Worthington” seriam sobrepostos ao começo do próximo registro seqüencial no arquivo. O problema aqui 
é que no modelo de entradaJsaída formatada usando o operador de inserção « e o operador de extração», campos 
- e portanto registros - podem variar em tamanho. Por exemplo, 7, 14, -117, 2074 e 27383 são todos ints e cada um 
é armazenado no mesmo número de bytes de “dados brutos” internamente, mas quando estes inteiros são enviados 
como texto formatado (seqüências de caracteres) para a tela ou para um arquivo em disco, eles se tornam campos de 
tamanhos diferentes. Portanto, o modelo de entradalsaída formatada nãoé normalmente usado para atualizar registros em sua posição original. 
Tal atualização pode ser feita, mas é complicada. Por exemplo, para efetuar a mudança de nome precedente, os registros antes de 300 White 0. 00 em um arquivo de acesso seqüencial poderiam ser copiados para um novo arquivo, o registro atualizado seria então gravado no novo arquivo e os registros após 300 White O . 00 seriam copiados para o novo arquivo. Isto requer processar todos os registros no arquivo para atualizar um registro. Se muitos registros estão sendo atualizados em uma única passagem pelo arquivo, então essa técnica pode ser aceitável. 
14.7 Arquivos de acesso aleatório 
Até aqui, vimos como criar e acessar arquivos seqüenciais e pesquisá-los para localizar informações particulares. Arquivos de acesso seqüencial são inadequados para as chamadas aplicações de acesso instantâneo, nas quais um registro de informações particular deve ser localizado imediatamente. Aplicações comuns de acesso instantâneo são sistemas de reservas de linhas aéreas, sistemas bancários, sistemas de pontos de venda, caixas automáticos e outros tipos de sistemas de processamento de transações que exigem acesso a dados específicos. O banco no qual você tem sua conta pode ter centenas de milhares ou mesmo milhões de outros clientes e, ainda assim, quando você usa um caixa automático, é verificado se sua conta tem fundos suficientes em segundos. Este tipo de acesso instantâneo é possível com arquivos de acesso aleatório. Os registros individuais de um arquivo de acesso aleatório podem ser acessados diretamente (e rapidamente) sem pesquisar os outros registros. 
Como já dissemos, C++ não impõe estruturas a um arquivo. Assim, uma aplicação que queira usar arquivos de 
acesso aleatório deve criá-los. Diversass técnicas podem ser usadas para criar arquivos de acesso aleatório. Talvez a 
Contas 
100 
200 
500 
com saldo devedor: 
Jones 
Doe 
Rich 
24.98 
345.67 
224.62 
Fim da execução. 
740 C++ COMO PROGRAMAR 
mais simples seja exigir que todos os registros em um arquivo sejam do mesmo comprimento fixo. Usar registros de comprimento fixo torna fácil para um programa calcular (como uma função do tamanho do registro e da chave do registro) a localização exata de qualquer registro em relação ao começo de um arquivo. Logo veremos como isso facilita o acesso imediato a registros específicos, mesmo em grandes arquivos. 
A Fig. 14.10 ilustra a visão de C++ de um arquivo de acesso aleatório composto por registros de comprimento 
fixo (cada registro tem 100 bytes de comprimento). Um arquivo de acesso aleatório é como um comboio ferroviário com muitos vagões - alguns vazios e alguns com conteúdo. 
Fig. 14.10 A visão de C++ de um arquivo de acesso aleatório. 
Dados podem ser inseridos em um arquivo de acesso aleatório sem destruir outros dados no arquivo. Dados armazenados previamente também podem ser atualizados ou excluídos sem regravar todo o arquivo. Nas seções seguintes, explicamos como criar um arquivo de acesso aleatório, digitar dados, ler os dados tanto seqüencial como aleatoriamente, atualizar os dados e eliminar dados não mais necessários. 
14.8 Criando um arquivo de acesso aleatório 
A função membro write de ostream envia um número fixo de bytes começando em uma posição específica da memória para o stream de saída especificado. Quando o stream é associado a um arquivo, os dados são gravados começando na posição do arquivo especificada pelo ponteiro put de posição do arquivo. A função membro read de istream lê um número fixo de bytes do stream de entrada especificado para uma área na memória começando em um endereço especificado. Se o stream está associado a um arquivo, os bytes são lidos começando na posição do arquivo especificada pelo ponteiro get de posição do arquivo. 
Agora, quando gravarmos um inteiro nurnber em um arquivo, em vez de usar 
outFile « number; 
o qual poderia imprimir tanto somente 1 dígito como até 11 dígitos (10 dígitos mais um suial, cada uni dos quais exige 1 byte de memória) para um inteiro de 4 bytes, podemos usar 
outFile.write( reiriterpret_cast<const char *>( &number ), 
sizeof( nuniber ) ); 
a qual sempre grava 4 bytes (em uma máquina com inteiros de 4 bytes). A função write espera um dado do tipo 
const char * como seu primeiro argumento, por isso usamos o operador reinterpret cast<const char* 
> para converter o endereço de number para um ponteiro const char* . O segundo argumento de write é um 
inteiro do tipo size_t que especifica o número de bytes a serem gravados. Como veremos, a função read de 
istream pode então ser usada para ler os 4 bytes de volta para a variável inteira number. 
Se um programa vai ler dados não-formatados (gravados por write), ele precisa ser compilado e executado 
em um sistema que seja compatível com o programa que gravou os dados. 
Programas de processamento de arquivos de acesso aleatório raramente gravam um único campo em um 
arquivo. Normalmente, eles gravam um objeto struct ou class por vez, como mostraremos nos exemplos seguintes. Considere a seguinte definição de problema: 
F 
O 1 
100 1 
200 Ir 
300 , 
400 , 
500 
offsets dos bytes 
1 
II 
II 
II 
II 
1 
100 
100 
100 
100 
100 
100 
bytes 
bytes 
bytes 
bytes 
bytes 
bytes 
CAPÍTULO 14- PROCESSAMENTO DE ARQUIVOS 741 
Crie um programa de processamento de crédito capaz de armazenar até 100 registros de comprimento fixo para uma empresa que pode ter até 100 clientes. Cada registro deve consistirem um número de conta que será usado como a chave do registro, um sobrenome, um nome e um saldo. O programa deve ser capaz de atualizar uma conta, inserir uma nova conta, eliminar uma conta e listar todos os registros de conta em um arquivo-texto formatado para impressão. 
As seções seguintes apresentam as técnicas necessárias para criarmos esse programa de processamento de crédito. A Fig. 14.11 ilustra a abertura de um arquivo de acesso aleatório, a definição do formato do registro usando uma struct (definida no arquivo de cabeçalho clntdata . h) e gravação de dados no disco em formato binário (o modo binário é especificado na linha 33). Este programa inicializa todos os 100 registros do arquivo “credit. dat” 
com structs vazias, usando a função write. Cada struct vazia contém O no campo de número da conta, o _______ string nulo (representado por aspas vazias) no campo de sobrenome, o string nulo no campo de nome e O . O no 
campo de saldo. Cada registro é inicializado com a quantidade de espaço vazio na qual os dados da conta serão armazenados e para determinar nos programas subseqüentes se um registro está vazio ou contém dados. 
1 // clntdata.h 
2 1/ Definição da struct clientData usada 
3 1/ nas Figs. 14.11, 14.12, 14.14 e 14.15. 
4 #ifndef CLNTDATAH 
5 #define CLNTDATAH 
6 
7 struct clientData { 
8 int accountNuniber; 
9 char lastName[ 15 ]; 
10 char firstName[ 10 ]; 
11 double balance; 
12 ); 
13 
14 #endif 
Fig. 14.11 Arquivo de cabeçalho clntdata.h. 
15 II Fig. 14.11: figl4ll.cpp 
16 /1 Criando um arquivo de acesso aleatório seqüencialmente 
17 #include <iostream> 
18 
19 using std::cerr; 
20 using std::endl; 
21 using std::ios; 
22 
23 #include <fstream> 
24 
25 using std::ofstream; 
26 
27 #include <cstdlib> 
28 
29 #include “clntdata.h” 
30 
31 int main() 
32 
33 ofstream outCredit( “credit.dat”, ios::binary ); 
34 
35 if ( outCredit ) { 
36 cerr « “Arquivo não pode ser aberto.” « endi; 
37 exit( 1 ); 
Fig. 14.12 Criando um arquivo de acesso aleatório seqüencialmente (parte 1 de 2). 
742 C++ COMO PROGRAMAR 
38 } 
39 
40 clientData blankClient = { 0, “‘, ““, 0.0 }; 
41 
42 for ( int i = 0; i < 100; i++ 
43 outCredit.write( 
44 reinterpretcast<const char *>( &blankClient ), 
45 sizeof( clientData ) ); 
46 return 0; 
Fig. 14.12 Criando um arquivo de acesso aleatório seqüencialmente (parte 2 de 2). 
Na Fig. 14.11,0 comando nas linhas 43 a45 
outCredit . write 
reinterpret_cast<const char *>( &blankClient ), 
sizeof( clientData ) ); 
faz com que a estrutura blankClient de tamanho sizeof ( clientData )seja gravada no arquivo credit. dat associado ao objeto outCredit de ofstream. Lembre-se de que o operador sizeof retoma o tamanho em bytes do objeto contido entre parênteses (ver Capítulo 5). Note que o primeiro argumento para a função write na linha 43 deve ser do tipo const char . Contudo, o tipo de dados de &blankClient é clientData 
*. Para converter &blankClient para o tipo de ponteiro apropriado, a expressão 
reinterpretcast<const char >( &blankClient ), 
usa o operador de coerção reinterpret_cast para converter o endereço de blankClient em um const char *, de modo que a chamada para write é compilada sem provocar um erro de sintaxe. 
14.9 Gravando dados aleatoriamente em um arquivo de acesso aleatório 
A Fig. 14.12 grava dados no arquivo “credit.dat”. Ele usa a combinação das funções seekp e write de 
ostream para armazenar dados em posições precisas no arquivo. A função seekp inicializa o ponteiro put de 
posição do arquivo para uma posição específica no arquivo, então write grava os dados. Um exemplo de execução 
é mostrado na Fig. 14.13. Note que o programa da Fig. 14.12 inclui (linha 16)0 arquivo de cabeçalho clntdata . h 
definido na Fig. 14.11. 
1 // Fig. 14.12: figl4l2.cpp 
2 II Gravando dados aleatoriamente em um arquivo de acesso aleatório 
3 #include <iostream> 
4 
5 using std: :cerr; 
6 using std::endl; 
7 using std::cout; 
8 using std::cin; 
9 using std::ios; 
10 
11 #include <fstream> 
12 
13 using std::ofstream; 
14 
15 #include <cstdlib> 
16 #include “clntdata.h” 
17 
Fig. 14.12 Criando um arquivo de acesso aleatório seqüencialmente (parte 1 de 2). 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 743 
18 int main() 
19 
20 ofstream outCredit( “credit.dat”, ios::binary ); 
21 
22 if ( !outCredit 
23 cerr « ‘Arquivo não pode ser aberto.” « endi; 
24 exit( 1 ); 
25 
26 
27 cout « “Digite o número da conta 
28 « “(1 a 100, O para terminar entrada)\n? “; 
29 
30 clientData client; 
31 cm » client.accountNuniber; 
32 
33 while ( client.accountNumber > O && 
34 client.accountNuinber <= 100 ) { 
35 cout « “Digite sobrenome, primeiro nome, saldo\n? “; 
36 cm » client.lastName » client.firstName 
37 » client.balance; 
38 
39 outCredit.seekp( ( client.accountNumber - 1 ) * 
40 sizeof( clientData ) ); 
41 outCredit.write 
42 reinterpret_cast<const char *>( &client ), 
43 sizeof( clientData ) ); 
44 
45 cout « “Digite número da conta\n? 
46 cm » client.accountNumber; 
47 } 
48 
49 return 0; 
50 
Fig. 14.12 Criando um arquivo de acesso aleatório seqüencialmente (parte 2 de 2). 
Digite o número da conta (1 a 100, O para terminar entrada) 
? 37 
Digite sobrenome, primeiro nome, saldo 
? Barker Doug 0.00 
Digite o número da conta 
? 29 
Digite sobrenome, primeiro nome, saldo 
? Brown Nancy -24.54 
Digite o número da conta 
? 96 
Digite sobrenome, primeiro nome, saldo 
? Stone San 34.98 
Digite o número da conta 
? 88 
Digite sobrenome, primeiro nome, saldo 
? Smith Dave 258.34 
Digite o número da conta 
? 33 
Digite sobrenome, primeiro nome, saldo 
? Dunn Stacey 314.33 
Digite o número da conta 
Fig. 14.13 Exemplo de execução do programa da Fig. 14.12. 
744 C++ COMO PROGRAMAR 
As linhas 39 e 40 26 
27 
outCredit.seekp( ( client.accountNuxnber - 1 ) * 28 
sizeof( clientData ) ); 29 
30 
posicionam o ponteiro de posição par do arquivo para o objeto outCredit na posição em bytes calculada por 31 
(client . accountNumber - 1) * sizeof ( clientData) Como o número da conta está entre 1 e 100, 
i é subtraido do número da conta quando se calcula a posição em bytes do registro. Desta forma, para o registro 1, 34 
o ponteiro de posição do arquivo é inicializado com o byte O do arquivo. Note que o objeto outCredit de ofstream 35 
é aberto com o modo de abertura de arquivo ios: :binary. 36 
14.10 Lendo dados seqüencialmente de um arquivo de acesso aleatório 
Nas seções anteriores, criamos um arquivo de acesso aleatório e gravamos dados nesse arquivo. Nesta seção, desen- 41 
volvemos um programa que lê todo o arquivo seqüencialmente e imprime somente aqueles registros que contém 42 
dados. Este programa fornece um benefício adicional. Veja se você pode determinar qual é ele; nós o revelaremos no 43 
fim desta seção. 
A função read de istream lê um número de bytes especificado da posição corrente no stream especificado 
para um um objeto. Por exemplo, as linhas 43 e 44 
inCredit.read(reinterpret_cast<char *>( &client ), 
sizeof(clientData) ); 50 
51 
da Fig 14.14 Icem o numero de bytes especificado por sizeof ( clientData ) do arquivo associado ao objeto 52 
inCredit de ifstream e armazenam os dados na estrutura client. Note que a função read exige um 53 
primeiro argumento do tipo char . Como &client é do tipo clientData , &client deve sofrer uma 54 
coerção para char* com o operador de coerção reinterpret cast. Note que o programa da Fig. 14.14 55 
inclui o arquivo de cabeçalho clntdata . h definido na Fig. 14.11. 56 57 
58 
1 II Fig. 14.14: figl4_14.cpp 
2 II Lendo seqüencialmente um arquivo de acesso aleatório 60 
3 #include <iostream> 61 
4 62 
5 using std::cout; 63 
6 using std::endl; 64 
7 using std: :ios; 65 
8 using std::cerr; 66 
10 #include <iomanip> Cor 
11 29 
12 using std::setprecision; 
13 using std::setiosflags; 1 
14 using std: :resetiosflags; 88 
15 using std::setw; 
16 
17 #include <fstream> Fini 
18 
19 using std::ifstream; 
20 using std::ostream; AFig 
21 ele co 
22 #include <cstdlib> 
23 #include “clntdata.h” 
24 
25 void outputLine( ostream&, const clientData & ); usaal 
Fig. 14.14 Lendo seqüencialmente um arquivo de acesso aleatório (parte 1 de 2). daest. 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 745 
26 
27 int main() 
28 
29 ifstream inCredit( “credit.dat”, ios::in ); 
30 
31 if ( 1inCredit ) { 
32 cerr « “Arquivo não pode ser aberto.” « endi; 
33 exit( 1 ); 
34 } 
35 
36 cout « setiosflags( ios::left ) « setw( 10 ) « “Conta” 
37 « setw( 16 ) « “Sobrenome” « setw( 11 
38 « “Primeiro nome” « resetiosflags( ios::left 
39 « setw( 10 ) « “Saldo” « endi; 
40 
41 clientData client; 
42 
43 inCredit.read( reinterpretcast<char*>( &client ), 
44 sizeof( clientData ) ); 
45 
46 while ( inCredit && !inCredit.eof O ) { 
47 
48 if ( client.accountNumber != O 
49 outputLine( cout, client ); 
50 
51 inCredit.read( reinterpret_cast<char *>( &client ), 
52 sizeof( clientData ) ); 
53 ) 
54 
55 return 0; 
56 
57 
58 void outputLine( ostream &output, const clientData &c 
59 { 
60 output « setiosflags( ios::left ) « setw( 10 
61 « c.accountNimiber « setw( 16 ) « c.lastName 
62 « setw( 11 ) « c.firstName « setw( 10 
63 « setprecision ( 2 ) « resetiosflags ( ios: : left 
64 « setiosflags( ios::fixed 1 ios::showpoint 
65 « c.balance « ‘\n’; 
66 } 
4 Fig. 14.14 Lendo seqüencialmente um arquivo de acesso aleatório (parte 2 de 2). 
A Fig. 14.14 lê seqüencialmente todos os registros no arquivo “credit . dat”, examina cada registro para ver se ele contém dados e exibe saídas formatadas para os registros que contêm dados. A condição na linha 46 
while ( inCredit && ‘inCredit.eof() ) { 
usa a função membro eof de ios para determinar quando o fim do arquivo é encontrado e faz com que a execução da estrutura while termine. Além disso, se há um erro de leitura do arquivo, o laço terminará porque inCredit 
Conta 
Sobrenome 
Primeiro nome Saldo 
29 
Brown 
Nancy 
-24.54 
33 
Dunn 
Stacey 
314.33 
37 
Barker 
Doug 
0.00 
88 
Smith 
Dave 
258.34 
96 
Stone 
Sam 
34.98 
746 C++ COMO PROGRAMAR 
assumirá o valor false. Os dados udos do arquivo são enviados para a saída por outputLine, que aceita dois argumentos - um objeto ostream e uma estrutura clientData para ser enviada para a saída. O tipo de parâmetro ostream é interessante porque qualquer objeto ostream (tal como cout) ou qualquer objeto de uma classe derivada de ostream (tal como um objeto do tipo ofstreain) pode ser fornecido como o argumento. Isso significa que a mesma função pode ser usada, por exemplo, para executar saída pelo stream de saída padrão e para um stream de arquivo, sem necessidade de escrever funções separadas. 
Mas qual o benefício adicionalque prometemos? Se você examinar a janela de saída, você notará que os registros são listados em ordem (classificados pelo número de conta)! Isto é uma simples conseqüência do modo como armazenamos estes registros no arquivo, usando as técnicas de acesso direto. Comparado ao bubble sort que vimos (Capítulo 4), classificar com técnicas de acesso direto é extremamente rápido. A velocidade é obtida fazendo- se os arquivos suficientemente grandes para manter quaisquer registros que possam ser criados. Isso, naturalmente, significa que o arquivo poderia ser, na maioria das vezes, esparsamente ocupado, o que é um desperdício de memória. Assim, aqui temos um outro exemplo da solução de compromisso de trocar espaço por tempo: por usarmos grandes volumes de espaço, somos capazes de desenvolver um algoritmo de classificação muito mais rápido. 
14.11 Exemplo: um programa de processamento de transações 
Agora, apresentamos um programa deporte para processamento de transações (Fig. 14.15), usando um arquivo de acesso aleatório para obter processamento de acesso “instantâneo”. O programa mantém informações de contas bancárias. O programa atualiza contas existentes, acrescenta novas contas, elimina contas e armazena uma lista formatada de todas as contas correntes em um arquivo-texto para impressão. Assumimos que o programa da Fig. 14. II foi executado para criarmos o arquivo credit . dat e que o programa da Fig. 14.12 foi executado para inserir os dados iniciais. 
O programa tem cinco opções (a opção é para terminar o programa). A opção 1 chama a função textFile para armazenar uma lista formatada de todas as informações das contas em um arquivo-texto chamado print. txt que pode ser impresso mais tarde. A função textFile aceita um objeto fstream como argumento para ser usado para a leitura dos dados do arquivo credit . dat. A função textFile usa a função membro read de istream e as técnicas de acesso a arquivos seqüenciais da Fig. 14.14 para ler dados de credito . dat. A função outputLine discutida na Seção 14.10 é usada para a saída dos dados para o arquivo print. txt. Note que textFile usa a função membro seekg de istream para garantir que o ponteiro de posição do arquivo está no começo do arquivo. Após a escolha da opção 1, o arquivo print. txt contém: 
A opção 2 chama a função updateRecord para atualizar uma conta. A função somente atualizará um registro já existente, assim a função primeiro verifica se o registro especificado está vazio. O registro é lido para a estrutura client com a função membro read de istream, então client. accountNumber é comparado com zero para determinar se o registro contém informações. Se client . accountNuniber é zero, uma mensagem é impressa dizendo que o registro está vazio e as opções do menu são exibidas. Se o registro contém informações, a função updateRecord exibe o registro na tela usando a função outputLine. lê o montante da transação, calcula o novo saldo e regrava o registro no arquivo. A saída típica para a opção 2 é: 
Conta Sobrenome Primeiro nome Saldo 
29 Brown Nancy -24.54 
33 Dunn Stacey 314.33 
37 Barker Doug 0.00 
88 Smith Dave 258.34 
96 Stone Sam 34.98 
Digite número da conta a ser atualizada (1 a 100): 37 
37 Barker Doug 0.00 
Digite débito (+) ou pagamento (-): +87.99 
37 Barker Doug 87.99 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 747 
using 
using 
using 
using 
using 
using 
using 
using 
using 
using 
using 
void 
void 
std: :cerr; std: :cin; std: :endl; std: :ios; 
std: :ofstream; std: :ostream; std: :fstream; 
std: : setiosflags; std: : resetiosflags; std: :setw; 
std: : setprecision; 
A opção 3 chama a função newRecord para adicionar um novo registro ao arquivo. Se o usuário fornecer um número de conta já existente, newRecord exibirá uma mensagem de erro informando que a conta já existe e as opções do menu serão exibidas novamente. Essa função usa o mesmo processo que o programa da Fig. 14.12 para adicionar uma nova conta. Uma saída típica da opção 3 é 
Digite o número da nova conta (1 a 100): 22 
Digite sobrenome, primeiro nome, saldo 
? Johnston Sarah 247.45 
A opção 4 chama a função deleteRecord para excluir um registro de um arquivo. A exclusão é realizada pedindo ao usuário um número de conta. Somente um registrojá existente pode ser excluído; assim, caso a conta especificada esteja vazia, é exibida uma mensagem de erro. Uma mensagem é exibida para informar ao usuário que o registro foi excluído. Uma saída típica da opção 4 é 
Digite número da conta a ser eliminada (1 a 100): 29 Conta N°29 eliminada. 
O arquivo “credit. dat” é aberto criando-se um objeto fstream para ler e gravar usando-se um “ou” dos modos ios: : in e ios: : out. 
II Fig. 14.15: figl4_15.cpp 
II Este programa lê um arquivo de acesso aleatório 
II seqüencialmente, atualiza dados já gravados no arquivo, 
// cria novos dados a serem colocados no arquivo 
// e elimina dados que já estão no arquivo. 
#include <iostream> 
using std::cout; 
#include <fstream> 
#include <iomanip> 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
Fig. 14.15 Programa de contas bancárias (parte 1 de 5). 
#include <cstdlib> 
#include ‘clntdata.h’ 
int enterChoiceO; 
void textFile( fstream& ); 
void updateRecord( fstream& ); 
void newRecord( fstream& ); 
deleteRecord( fstream& ); 
outputLine( ostream&, const clientData & ); 
1 
748 C++ COMO PROGRAMAR 
36 int getAccount( const char * const ); 
37 
38 enuzn Choices { TEXTFILE = 1, UPDATE, NEW, DELETE, END }; 
39 
40 int main() 
41 
42 fstream inOutCredit( “credit.dat”, ios::in 1 ios::out ); 
43 
44 if ( ‘inOutCredit 
45 cerr « “Arquivo não pode ser aberto.” « endi; 
46 exit ( 1 ); 
47 } 
48 
49 int choice; 
50 
51 while ( ( choice = enterChoice() ) != END ) { 
52 
53 switch ( choice 
54 case TEXTFILE: 
55 textFile( inOutCredit ); 
56 break; 
57 case UPDATE: 
58 updateRecord( inOutCredit ); 
59 break; 
60 case NEW: 
61 newRecord( inOutCredit ); 
62 break; 
63 case DELETE: 
64 deleteRecord( inOutCredit ); 
65 break; 
66 default: 
67 cerr « “Opção incorreta\n”; 
68 break; 
69 } 
70 
71 inOutCredit.clearO; II desliga indicador de fim de arquivo 
72 
73 
74 return 0; 
75 } 
76 
77 II Pede para selecionar uma opção do menu 
78 int enterChoice() 
79 { 
80 cout « “\nDigite sua opção” « endl 
81 « “1 - criar um arquivo de contas no formato de texto\n” 
82 « “ chamado \“print.txt\” para impressão\n” 
83 « “2 - atualizar uma conta\n” 
84 « “3 - acrescentar uma nova conta\n” 
85 « “4 - eliminar uma conta\n” 
86 « “5 - terminar o programa\n? 
87 
88 int menuChoice; 
89 cm » menuChoice; 
90 return menuChoice; 
91 } 
92 
93 // Cria arquivo no formato de texto para impressão 
Fig. 14.15 Programa de contas bancárias (parte 2 de 5). 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 749 
94 void textFile( fstream &readFromFile 
95 { 
96 ofstream outPrintFile( “print.txt”, ios::out ); 
97 
98 if ( ‘outPrintFile 
99 cerr « ‘Arquivo não pode ser aberto. « endi; 
100 exit( 1 ); 
101 
102 
103 outPrintFile « setiosflags( ios::left ) « setw( 10 
104 « “Conta” « setw( 16 ) « “Sobrenome” « setw( 11 
105 « “Primeiro nome” « resetiosflags( ios::left 
106 « setw( 10 ) « “Saldo” « endl; 
107 readFromFile.seekg( O ); 
108 
109 clientflata client; 
110 readFromFile.read( reinterpret_cast<char *>( &client ), 
111 sizeof( clientData ) ); 
112 
113 while ( ‘readFromFile.eof() ) { 
114 if ( client.accountNumber != O 
115 outputLine( outPrintFile, client ); 
116 
117 readFromFile.read( reinterpret_cast<char *>( &client ), 
118 sizeof( clientData ) ); 
119 } 
120 } 
121 
122 // Atualiza o saldo de uma conta 
123 void updateRecord( fstream &updateFile 
124 
125 int account = getAccount( “Digite número da conta a ser atualizada” ); 
126 
127 updateFile.seekg( ( account - 1 ) * sizeof( clientData ) ); 
128 
129 clientData client; 
130 updateFile.read( reinterpret_cast<char *>( &client ), 
131 sizeof( clientData ) ); 
132 
133 if ( client.accountNumber ! O ) { 
134 outputLine( cout, client ); 
135 cout « “\nDigite débito (+) ou pagamento(-): “; 
136 
137 double transaction; // débito ou pagamento 
138 cm » transaction; II deveria validar 
139 client.balance + transaction; 
140 outputLine( cout, client ); 
141 updateFile.seekp( ( account-1 ) * sizeof( clientData ) ); 
142 updateFile .write( 
143 reinterpret_cast<const char *>( &client ), 
144 sizeof( clientData ) ); 
145 } 
146 else 
147 cerr « “Conta N°” « account 
148 « “ não tem informação.” « endl; 
149 
150 
151 // Cria e insere um novo registro 
Fig. 14.15 Programa de contas bancárias (parte 3 de 5). 
1 
750 C++ COMO PROGRAMAR 
152 void newRecord( fstream &insertlnFile 
153 
154 int account = getAccount( “Digite o niunero da nova conta” ); 
155 
156 insertlnFile.seekg( ( account-1 ) * sizeof( clientData ) ); 
157 
158 clientData client; 
159 insertlnFile.read( reinterpret_cast<char *>( &client ), 
160 sizeof( clientData ) ); 
161 
162 if ( client.accountNumber == O ) { 
163 cout « “Digite sobrenome, primeiro nome, saldo\n? 
164 cm » client.lastName » client.firstName 
165 » client.balance; 
166 c1ient.accountNi.mber = account; 
167 insertlnFile.seekp( ( account - 1 ) * 
168 sizeof( clientData ) ); 
169 insertlnFile .write 
170 reinterpret_cast<const char *>( &client ), 
171 sizeof( clientData ) ); 
172 } 
173 else 
174 cerr « “Conta N’ « account 
175 « “ já contém informação.” « endi; 
176 
177 
178 1/ Elimina um registro já existente 
179 void deleteRecord( fstream &deleteFromFile 
180 { 
181 int account = getAccount( Digite número da conta a ser eliminada” ); 
182 
183 deleteFromFile.seekg( (account-1) * sizeof( clientData ) ); 
184 
185 clientData client; 
186 deleteFromFile.read( reinterpret_cast<char *>( &client ), 
187 sizeof( clientData ) ); 
188 
189 if ( client.accountNwnber != O ) { 
190 clientData blankClient = { 0, ‘‘, “, 0.0 }; 
191 
192 deleteFromFile.seekp( ( account - 1) * 
193 sizeof( clientData ) ); 
194 deleteFromFile .write 
195 reinterpretcast<const char *>( &blankClient ), 
196 sizeof( clientData ) ); 
197 cout « “Conta N°” « account « ‘ eliminada.” « endi; 
198 } 
199 else 
200 cerr « “Conta N° « account « “ está vazia.” « endl; 
201 } 
202 
203 /1 Envia uma linha com informações do cliente para a saída 
204 void outputLine( ostream &output, const clientData &c 
205 { 
206 output « setiosflags( ios::left ) « setw( 10 
207 « c.accountNumber « setw( 16 ) « c.lastName 
208 « setw( 11 ) « c.firstName « setw( 10 
209 « setprecision( 2 ) « resetiosflags( ios::left 
Fig. 14.15 Programa de contas bancárias (parte 4 de 5). 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 751 
210 « setiosflags( ios::fixed 1 ios::showpoint 
211 « c.balance « ‘\n’; 
212 } 
213 
214 // Lê um número de conta do teclado 
215 int getAccount( const char * const prompt 
216 { 
217 int account; 
218 
219 do 
220 cout « prompt « (1 a 100): 
221 cm » account; 
222 } while ( account < 1 1 1 account > 100 ); 
223 
224 return account; 
225 
Fig. 14.15 Programa de contas bancárias (parte 5 de 5). 
14.12 Entrada/saída de objetos 
Neste capítulo e no Capítulo 11, discutimos o estilo de entradaJsaída orientado a objetos de C++. Mas nossos exemplos se concentraram sobre a EIS de tipos de dados tradicionais e não sobre objetos de classes definidas pelo usuário. No Capítulo 8, mostramos como fazer entrada e saída de objetos de classes usando sobrecarga de operadores. Executamos a entrada de objetos sobrecarregando o operador de extração de stream » para as classes istream apropriadas. Executamos a saída de objetos sobrecarregando o operador de inserção em stream « para as classes ostream apropriadas. Em ambos os casos, somente os membros de dados de um objeto foram udos ou transferidos para a saída e, em cada caso, de uma forma com significado para objetos daquele tipo de dados abstrato particular. As funções membro de um objeto estão disponíveis internamente no computador e são combinadas com os valores de dados à medida que estes dados são lidos através do operador de inserção em stream sobrecarregado. 
Quando membros de dados de objetos são enviados na saída para um arquivo em disco, perdemos as informações sobre o tipo do objeto. Temos somente bytes de dados, não informações sobre seu tipo, em um disco. Se o programa que vai ler estes dados sabe a que tipos de objetos eles correspondem, então os dados são simplesmente lidos para objetos daquele tipo. 
Um problema interessante ocorre quando armazenamos objetos de diferentes tipos no mesmo arquivo. Como podemos distingui-los (ou suas coleções de membros de dados) quando os lemos para um programa? O problema, naturalmente, é que objetos tipicamente não têm campos de tipo (estudamos este problema cuidadosamente no Capítulo lo). 
Uma abordagem seria fazer cada operador de saída sobrecarregado colocar na saída um código de tipo precedendo cada coleção de membros de dados que representa um objeto. Então, a entrada de objetos sempre começaria pela leitura do campo de código de tipo e usaria um comando switch para invocar a função sobrecarregada apropriada. Embora esta solução não tenha a elegância da programação polimórfica, ela fornece um mecanismo prático para guardar objetos em um arquivo e recuperá-los conforme necessário. 
Resumo 
• Todos os itens de dados processados por um computador são reduzidos a combinações de zeros e uns. 
• O menor item dados em um computador pode assumir o valor O ou o valor 1. Tal item de dados é chamado de bit. 
• Dígitos, letras e símbolos especiais são conhecidos como caracteres. O conjunto de todos os caracteres que podem ser usados para escrever programas e representar itens de dados em um computador em particular é chamado de conjunto de caracteres daquele computador. Cada caractere no conjunto de caracteres do computador é representado como uma combinação de oito is e Os (chamada de byte). 
Um campo é um grupo de caracteres (ou bytes) que tem um significado. 
752 C++ COMO PROGRAMAR 
• Um registro é um grupo de campos relacionados. 
• Pelo menos um campo em um registro é escolhido como a chave do registro para identificar um registro como pertencente a uma pessoa ou entidade particular, que é diferente de todos os outros registros no arquivo. 
• O acesso seqüencial é o método mais popular para acessar dados em um arquivo. 
• Uma coleção de programas projetada para criar e gerenciar bancos de dados é chamada de sistema gerenciador de bancos de dados (SGBD). 
• C÷+ vê cada arquivo como um stream seqüencial de bytes. 
• Cada arquivo termina com alguma forma de marcador de fim de arquivo dependente da máquina. 
• Streams fornecem canais de comunicação entre arquivos e programas. 
• Os arquivos de cabeçalho <iostream> e <fstreaiu> devem ser incluídos em um programa para executar a EIS com arquivo ao estilo de C+÷. O arquivo de cabeçalho <fstreani> inclui as definições das classes para streams ifstream. ofstream efstream. 
• Arquivos são abertos instanciando-se objetos das classes para streams ifstream. ofstream e fstream. 
• C++ não impõe estruturas a um arquivo. Desta forma, conceitos como “registro” não existem em C++. O programador deve estruturar um arquivo para atender aos requisitos de uma aplicação particular. 
• Arquivos são abertos para saída criando-se um objeto da classe ofstream. Dois argumentos são passados ao objeto - o nome do arquivo e o modo de abertura do arquivo. Para um objeto ofstream. o modo de abertura do arquivo pode ser ios: : out, para saída de dados para um arquivo, ou ios: : app, para acrescentar dados ao fim de um arquivo. Arquivos existentes abertos com modo ios: : out são truncados. Se o arquivo não existe, ele é criado. 
• A função membro operador operator 1 de ios retorna um valor verdadeiro se o failbit ou o badbit foi ligado para um stream na operação open. 
• A função membro operador operator void* de ios converteco stream em um ponteiro para comparação com O (o ponteiro nulo). Se o failbit ou o badbit foi ligado para o stream, é retornado O (falso). 
• Programas podem não processar arquivos, processar somente um arquivo ou vários arquivos. Cada arquivo tem um nome que é único e está associadocom um objeto arquivo stream apropriado. Todas as funções de processamento de arquivo devem referenciar um arquivo com o objeto apropriado. 
• Um “ponteiro get” indica a posição do arquivo da qual a próxima leitura ocorrerá e um “ponteiro put” indica a posição no arquivo na qual a próxima saída será colocada. Tanto a classe istreaiu como a classe ostream fornecem funções membro para reposicionar o ponteiro de posição no arquivo. As funções são seekg (“seek get”) para a classe istream e seekp (“seekput”) para a classe ostream. 
• As funções membro tellp e tellg retornam as posições correntes dos ponteiros put e get. 
• Uma maneira conveniente de implementar arquivos de acesso aleatório é usar somente registros de comprimento fixo. Usando esta técnica, um programa pode rapidamente calcular a posição exata de um registro em relação ao começo do arquivo. 
• Dados podem ser inseridos em um arquivo de acesso aleatório sem destruir outros dados no arquivo. Dados podem ser atualizados ou excluídos sem regravar todo o arquivo. 
• A função membro write de ostream faz a saída para um stream especificado de algum número de bytes, começando em uma posição designada na memória. Quando o stream está associado com um arquivo, os dados são gravados na posição especificada pelo ponteiro put de posição no arquivo. 
• A função membro read de isream extrai um número especificado de bytes do stream especificado para uma área na memória começando em um endereço designado. Os bytes são extraídos começando pela posição especificada pelo ponteiro ge! de posição no arquivo. A função read requer um primeiro argumento do tipo char*. 
• A função write espera um primeiro argumento do tipo const char*. de modo que este argumento deve sofrer uma coerção para const char* se for de algum outro tipo de ponteiro. O segundo argumento é um inteiro que especifica o número de bytes a serem gravados. 
• O operador unário sizeof retorna, durante a compilação, o tamanho em bytes do objeto contido entre parênteses; sizeof retorna um uns igned integer. 
CAPÍTULO 14- PROCESSAMENTO DE ARQUIVOS 753 
• A função membro eof de ias informa se o indicador de fim de arquivo foi ligado no stream designado. O fim de arquivo é ligado após falhar uma tentativa de leitura. 
Terminologia 
abrir um arquivo 
arquivo 
arquivo de acesso aleatório 
arquivo de acesso seqüencial arquivo de cabeçalho <fstream> 
bit 
byte 
campo 
campo alfabético campo de caracteres campo numérico 
cerr (saída padrão para erros, sem buifer) 
chave de registro 
cm (entrada padrão) 
classe fstream classe ifstreain classe istream classe ofstrearn 
classe ostream 
clog (saída padrão para erros, com buifer) conjunto de caracteres 
cout (saída padrão) 
database / banco de dados 
database management system (DB MS) dígito binário 
dígito decimal 
EIS em memória 
EIS em núcleo fechar um arquivo 
fim de arquivo 
função membro dose função membro open 
função membro operator void* função membro operator função membro seekg de istream função membro seekp de ostream função membro tellg de istreaxn 
função membro tellp de ostreani hierarquia de dados 
manipulator de stream ends 
marcador de fim de arquivo modo de abertura de arquivo ios: : app modo de abertura de arquivo ios: : ate 
modo de abertura de arquivo ios: : binary modo de abertura de arquivo ias: : in modo de abertura de arquivo ias: : out modo de abertura de arquivo ias: : trunc nome de arquivo 
ponteiro de posição no arquivo ponto de início de seek ias: : beg ponto de início de seek ias: : cur ponto de início de seek ias: : end registro 
símbolo especial stream 
stream de entrada 
stream de saída truncar um arquivo existente 
Erros comuns de programação 
14.1 Abrir um arquivo existente para saída(ias: : aut) quando, na verdade, o usuário quer preservar o arquivo; o conteúdo do arquivo é descartado sem aviso. 
14.2 Usar um objeto afstream incorreto para se referir a um arquivo. 
14.3 Não abrir um arquivo antes de tentar referenciá-lo em um programa. 
Boa prática de programação 
14.1 Abrir um arquivo somente para entrada (usando ias: : in), se o conteúdo do arquivo não deve ser modificado. Isso evita a modificação não-intencional do conteúdo do arquivo. Esse é um exemplo do princípio do privilégio mínimo. 
Dica de desempenho 
14.1 Feche explicitamente cada arquivo tão logo se saiba que o programa não fará referência ao arquivo novamente. Isso pode reduzir o consumo de recursos no programa que vai continuar sendo executado após não mais necessitar de um arquivo 
particular. Essa prática também melhora a clareza do programa. 
754 C++ COMO PROGRAMAR 
Exercícios de auto-revisão 
14.1 Preencha os espaços em branco em cada um dos seguintes itens: 
a) Em última instância, todos os items de dados processados por um computador são reduzidos a combinações de 
_______ e _______ 
b) O menor item de dados que um computador pode processar é chamado de __________ 
c) Um é um grupo de registros relacionados. 
d) Dígitos, letras e símbolos especiais são chamados de ________________________ 
e) Um grupo de arquivos relacionados é chamado de______________ 
f) A função membro _______________ das classes stream de arquivo fstream. ifstream e ofstreazn fecha um arquivo. 
g) A função membro _________________ de istreani lê um caractere do stream especificado. 
h) As funções membro ________________ e ________________ de is tream lêem uma linha do stream especificado. 
i) A função membro ______________das classes streamde arquivo fstream. ifstream e ofstream abre um arquivo. 
j) A função membro _______________ de istreamé normalmente usada quando da leitura de dados de um arquivo em aplicações de acesso aleatório. 
k) As funções membro ____________ e _________________ das classes istream e ostream inicializam o ponteiro de posição para uma posição específica em um stream de entrada ou saída, respectivamente. 
14.2 Indique quais das seguintes afirmações são verdadeiras e quais são falsas. Se forem explique por quê: 
a) A função membro read não pode ser usada para ler dados do objeto de entrada cm. 
b) O programador deve criar explicitamente os objetos ci cout. cerr e clog. 
c) Um programa deve chamar explicitamente a função dose para fechar um arquivo associado a um objeto ifstream. ofstream ou fstream. 
d) Se o ponteiro de posição no arquivo aponta para uma posição em um arquivo seqüencial diferente do começo do arquivo, o arquivo deve ser fechado e reaberto para se ler desde o começo do arquivo. 
e) A função membro write de ostream pode escrever no stream padrão de saída cout. 
f) Dados em arquivos de acesso seqüencial são sempre atualizados sem gravar por cima dos dados vizinhos. 
g) Não é necessário pesquisar todos os registros em arquivos de acesso aleatório para encontrar um registro específico. 
h) Registros em um arquivo de acesso aleatório devem ser de comprimento uniforme. 
i) As funções membro seekp e seekg devem começar a pesquisa no início de um arquivo. 
14.3 Assuma que cada uma das seguintes afiramações se aplica ao mesmo programa. 
a) Escreva um comando que abre o arquivo “ oldmast . dat” para entrada; use o objeto inOidMaster de ifstreaxn. 
b) Escreva um comando que abre o arquivo “trans . dat para entrada; use o objeto inTransaction de ifstreain. 
c) Escreva um comando que abre o arquivo “ newmast. dat’ para saída (e criação); use o objeto outnewMaster de ofstream. 
d) Escreva um comando que lê um registro do arquivo ‘oldmast . dat”. O registro consiste no inteiro accountNum. do string name e do valor em ponto flutuante currentBalance: use o objeto inOidmaster de ifstrearn. 
e) Escreva um comando que lê um registro do arquivo “trans. dat’. O registro consiste no inteiro accouritNum e do valor em ponto flutuante dollarAmount: use o objeto inTransaction de ifstream. 
f) Escreva um comando que grava um registro no arquivo newmast. dat”. O registro consiste no inteiro accountNum, do string name e do valor em ponto flutuante currentBalance: use o objeto outNewMaster 
de ofstream. 
14.4 Encontre o(s) erro(s) e mostre como corrigi-lo(s) em cada um dosseguintes itens. 
a) O arquivo “payables . dat” referenciado pelo objeto outPayable de ofstream não foi aberto. 
outPayable « account « company « amount « endi; 
b) O seguinte comando deveria ler um registrn do arquivo “payables . dat”. O objeto inPayable de ifstream referencia este arquivo e o objeto inReceivable de istream referencia o arquivo “receivables . dat”. 
inReceivable » account » company » arnount; 
e) O arquivo “tools . dat” deveria ser aberto para adicionar dados ao arquivo sem descartar os dados correntes. 
ofstream outTools( “tools.dat”, ios::out ); 
CAPÍTULO 14 - PROCESSAMENTO DE ARQUIVOS 755 
Respostas aos exercícios de auto-revisão 
14.1 a) is, Os. b) Bit. c) Arquivo. d) Caracteres, e) Database/Banco de dados. 1) dose. g) get. h) get, getline. i) open. j) read. k) seekg. seekp. 
14.2 a) Falsa. A função read pode ler de qualquer objeto de entrada stream derivado de istreain. 
b) Falsa. Estes quatro streain.s são criados automaticamente para o programador. O arquivo de cabeçalho <iostreaan deve ser incluído em um arquivo para usá-los. Este cabeçalho inclui declarações de cada um destes objetos stream. 
c) Falsa. Os arquivos serão fechados quando destruidores para os objetos ifstreaxn. ofstream ou fstreain são executados, quando os objetos stream saem do escopo ou antes da execução do programa terminar, mas é uma boa 
prática de programação fechar explicitamente todos os arquivos com dose quando eles não são mais necessários. 
d) Falsa. A função membro seekp ou seekg pode ser usada para reposicionar o ponteiro put ou get de posição no arquivo no começo do arquivo. 
e) Verdadeira. 
f) Falsa. Na maioria dos casos, os registros de arquivos seqüenciais não são de comprimento uniforme. Portanto, é possível que a atualização de um registro faça com que dados de outros registros sejam sobrepostos. 
g) Verdadeira. 
h) Falsa. Os registros em arquivos de acesso aleatório são normalmente de comprimento uniforme, 
i) Falsa. E possível pesquisar do começo do arquivo, do fim do arquivo e da posição corrente no arquivo. 
14.3 a) ifstream inOidMaster( “oidmast.dat’, ios: :in 
b) ifstream inTransaction( “trans.dat”, ios::in ); 
c) ofstream outNewMaster( riewmast.dat”, ios::out ); 
d) iriOidmaster » accountNum » name » currentBaiance; 
e) inTransaction » accountNuin » doiiarPanount; 
outNewMaster « accountNum « name « currentBaiance; 
14.4 a) Erro: o arquivo “payables . dat” não foi aberto antes de se tentar fazer uma saída dados para o stream. 
Correção: use a função open de ostream para abrir “payabies . dat’ para saída. 
b) Erro: o objeto incorreto de istream está sendo usado para ler um registro do arquivo “payabies . dat”. Correção: use o objeto inPayabie de istream para referenciar “payabies dat”. 
c) Erro: o conteúdo do arquivo é descartado porque o arquivo está aberto para saída (ios: out). 
Correção: para adicionar dados ao arquivo, abra o arquivo para atualização (ios: : ate) ou abra o arquivo para acréscimo (ios: : app). 
Exercícios 
14.5 Preencha os espaços em cada um dos seguintes itens: 
a) Computadores armazenam grandes volumes de dados em dispositivos de memória secundária como________________ 
b) Um ________________ é composto de vários campos. 
c) Um campo que pode conter somente dígitos, letras e espaços é chamado de campo 
d) Para facilitar a recuperação de registros específicos de um arquivo, em cada registro é escolhido um campo como a 
e) A grande maioria das informações armazenadas em sistemas de computadores é armazenada em arquivos 
O Um grupo de caracteres relacionados com um significado é chamado de ________________ 
g) Os objetos stream padrão declarados pelo arquivo de cabeçalho <iostream> são 
_________ _________ e _________ 
h) A função membro ostreaxn ________________ faz a saída de um caractere para o stream especificado. 
i) A função membro ostream geralmente é usada para gravar dados em um arquivo acessado 
aletoriamente. 
j) A função membro istream reposiciona o ponteiro de posição do arquivo em um arquivo. 
14.6 Diga quais das seguintes sentenças são verdadeiras e quais são falsas. Se forem falsas, explique porquê: 
a) As impressionantes funções executadas por computadores envolvem essencialmente a manipulação de zeros e uns. 
b) As pessoas preferem manipular bits em vez de caracteres e campos porque bits são mais compactos. 
c) As pessoas especificam programas e items de dados como caracteres; os computadores então manipulam e processam estes caracteres como grupos de zeros e uns. 
d) O CEP de 5 dígitos de uma pessoa é um exemplo de um campo numérico. 
756 C÷+ COMO PROGRAMAR 
e) O endereço de uma pessoa é geralmente considerado um campo alfabético nas aplicações de computador. 
f) Os itens de dados representados em computadores formam uma hierarquia de dados na qual os items de dados se tornam maiores e mais complexos à medida que progredimos de campos para caracteres, para bits, etc. 
g) A chave do registro identifica um registro como pertencendo a um campo particular. 
h) A maioria das organizações armazenam toda a informação em um único arquivo para facilitar o processamento por computador. 
i) Cada comando que processa um arquivo em um programa C++ explicitamente faz referência àquele arquivo por nome. 
j) Quando um programa cria um arquivo, o arquivo é automaticamente conservado pelo computador para referência futura. 
14.7 O Exercício 14.3 pediu ao leitor para escrever uma série de comandos simples. Na verdade, esses comandos formam o núcleo de um importante tipo de programa de processamento de arquivos, ou seja, um programa de matching - comparação e correspondência de arquivos. No processamento comercial de dados, é comum ter-se vários arquivos em cada sistema de aplicação. Em um sistema de contas a receber, por exemplo, geralmente há um arquivo mestre contendo informações detalhadas sobre cada cliente, tais como o nome do cliente, o endereço, o número do telefone, o saldo pendente, o limite de crédito, as condições de desconto, aspectos contratuais e, possivelmente, um histórico condensado das compras recentes e pagamentos em dinheiro. 
A medida que ocorrem as transações (por exemplo, são feitas vendas e os pagamentos chegam), elas são digitadas em um arquivo. No fim de um determinado período (um mês para algumas empresas, uma semana para outras e um dia em alguns casos) o arquivo de transações (chamado “trans . dat” no Exercício 14.3) é aplicado ao arquivo mestre (chamado “oldmast . dat” no Exercício 14.3), atualizando desta forma o registro de compras e pagamentos para cada conta. Durante um processamento de atualização, o arquivo mestre é regravado como um novo arquivo (“newmast. dat), o qual então é usado, no fim do próximo período, para começar o processo de atualização novamente. 
Programas de matching de arquivos devem lidar com certos problemas que não existem em programas de um único arquivo. Por exemplo, nem sempre ocorre uma correspondência. Um cliente no arquivo mestre pode não ter feito quaisquer compras ou pagamentos no exercício corrente e, portanto, nenhum registro para este cliente aparecerá no arquivo de transação. Similarmente, um cliente que efetuou algumas compras ou pagamentos pode ter acabado de entrar para o grupo de clientes e a empresa pode não ter tido oportunidade de criar um registro mestre para este cliente. 
Use os comandos do Exercício 14.3 como base para escrever um programa completo de contas a receber que efetua 
matching de arquivos. Use o número da conta em cada arquivo como a chave do registro para fins de matching. Assuma que cada arquivo é um arquivo seqüencial, com registros armazenados em ordem crescente por número de conta. 
Quando ocorre uma correspondência (i.e., um registro com o mesmo número de conta aparece nos arquivos mestre e de 
transação), some a quantia em dinheiro no arquivo de transação ao saldo corrente no arquivo mestre e grave o registro em newmast. dat”. (Assuma que compras são indicadas por quantias positivas no arquivo de transação e pagamentos são indicados por quantias negativas.) Quando

Outros materiais