




                                CAPTULO 9      
                                          
                                 ARQUIVOS   
                                          
                                          
            9.1. ENTRADA E SADA DE DADOS PARA UM DISCO 
            
                 Um  arquivo   (file)     qualquer  conjunto  de  bytes  
            armazenados  em  disco  (disquete,  disco  rgido  ou  disco
            virtual). As  regras para  a formao de um identificador de
            arquivo no disco, obedecem s regras do sistema operacional:
            nome com  no mnimo  1 e  no  mximo  8  caracteres,  e  uma
            extenso com  no mximo 3 caracteres, separados entre si por
            um ponto.  Em um  programa em  Pascal, um  arquivo deve  ser
            tratado como sendo uma varivel do tipo FILE.
                 A declarao  do tipo FILE  feita atravs das palavras
            chave FILE OF, seguidas do tipo dos elementos do arquivo:
            
                           TYPE
                             arquivo = FILE OF TipoBase;
            
            Exemplo: TYPE
                       agenda = FILE OF char;     (* Arquivo    de *)
                                                  (* caracteres    *)
                       ArqNumeros = FILE OF real; (* Arquivo    de *)
                                                  (* numeros reais *)
                       matriz = array[1..3, 1..3] of integer;
                       ArqMat = FILE OF matriz;   (* Arquivo    de *)
                                                  (* matrizes      *)
            
                 Para se manipular (ler ou escrever) dados de um arquivo
            em disco deve-se:
            
                 (1) Declarar uma varivel do tipo FILE:
            
                 TYPE                               ou   VAR
                   ArqInteiros = FILE OF integer;          arq: FILE of
                 VAR                                            integer;
                   arq: ArqInteiros;
            
                 (2) Associar  a varivel declarada em (1) com um string
            que corresponda ao nome do arquivo que ser criado ou que j
            exista no  disco. Isto   feito  com o  comando ASSIGN, cuja
            sintaxe :
                     ASSIGN(varivel, NomeDoArquivoNoDisco);
            Por exemplo,
                          ASSIGN(arq, 'TESTE.DAT');
            associa a varivel ARQ ao arquivo TESTE.DAT do disco.
            
                 (3) Aps  o uso  do ASSIGN,  deve-se "abrir"  o arquivo
            para gravao  ou para leitura dos dados. Se o arquivo ainda
            no existir  no disco, deve-se usar um REWRITE para cri-lo.
            Sua sintaxe   REWRITE(varivel).  Se o  arquivo j existir,

                                       - 129 -





            deve-se usar  um RESET,  cuja sintaxe  RESET(varivel). Por
            exemplo,
                      REWRITE(arq)  ---> Cria   no  disco  o  arquivo
                                         associado a ARQ (TESTE.DAT).
                      RESET(arq)    ---> "Abre"  para  leitura um ar-
                                         quivo j existente.
            
                 (4) Aps  a execuo  de (1), (2) e (3), o arquivo est
            pronto para  ser manipulado  pelo  programa.  Para  escrever
            dados em  um arquivo  usa-se um  WRITE,  escrevendo-se  como
            primeiro parmetro  a varivel  associada  ao  arquivo.  Por
            exemplo,
                      WRITE(arq, x) ---> Escreve em ARQ  o valor de  x.
                 Para se  ler dados  de  um  arquivo,  usa-se  um  READ,
            escrevendo-se a  varivel associada  ao arquivo como sendo o
            primeiro parmetro:
                      READ(arq, x) ---> L o valor de x em ARQ.
                 Deve haver  uma total  compatibilidade entre  o tipo do
            valor que  est sendo  gravado ou  lido e  o  tipo  base  do
            arquivo, declarado  direita de "FILE OF".
                 (5) Aps  a leitura  ou  gravao  de  todos  os  dados
            desejados no arquivo deve-se "fech-lo". Isto deve ser feito
            com o  comando CLOSE,  cuja sintaxe   CLOSE(varivel).  Por
            exemplo, CLOSE(arq).
            
            Exemplo:   Os    dados   so    gravados   em   um   arquivo
            seqencialmente. Um fragmento como
                                ...
                                x := 1;
                                Write(arq, x);
                                x := 2;
                                Write(arq, x);
                                x := 3;
                                Write(arq, x);
                                ...
            grava no arquivo ARQ os valores 1, 2 e 3, um aps o outro, e
            no um "por cima" do outro.  o mesmo que
                                ...
                                x := 1; y := 2; z := 3;
                                Write(arq, x, y, z);
                                ...
                 Para se recuperar posteriormente esses dados arquivados
            em ARQ, basta usar um ou vrios READ:
                                ...
                                Read(arq, x);     (* x := 1 *)
                                ...
                                Read(arq, x);     (* x := 2 *)
                                ...
                                Read(arq, x);     (* x := 3 *)
                                ...
            
            Exemplo: Neste  exemplo,   gerado aleatoriamente e salvo em
            disco um vetor de 10 elementos inteiros. Temos 2 opes para
            se declarar como sendo o tipo base do arquivo: ou um arquivo

                                       - 130 -





            de vetores  (onde um  vetor  gravado ou lido de uma s vez)
            ou um  arquivo de inteiros (onde as componentes do vetor so
            gravadas ou lidas uma a uma).
            
            PROGRAM Vetor_Aleatorio_Arquivado;
            
            TYPE
              vetor = array [1..10] of integer;
            
            VAR
              i: integer;
              v: vetor;
              arq: FILE OF vetor;
            
            BEGIN
              ASSIGN(arq, 'VETOR.DAT');
              REWRITE(arq);  (* Cria no disco o arquivo VETOR.DAT *)
            
              Randomize;
              for i := 1 to 10 do
                v[i] := Random(100);
            
              Write(arq, v); (* Grava v em ARQ *)
            
              CLOSE(arq);
            END.
            
                 Para recuperar  o vetor  v,  gravado  em  ARQ,  devemos
            acrescentar ao programa um trecho como:
                           ...
                           ASSIGN(arq, 'VETOR.DAT');
                           RESET(arq);
                           Read(arq, v);
                           CLOSE(arq);
                           ...
            
                 Tenha muito  cuidado ao  usar o comando REWRITE. Se ele
            for usado  em um  arquivo j existente, ento todos os dados
            do arquivo sero apagados.
            
            Exemplo: Este  programa-exemplo faz  a leitura  de quaisquer
            dados digitados  no teclado,  mas ao  invs de mostr-los na
            tela, grava-os  em disco  sob o nome de 'MENSAGEM.DOC'. Para
            ver o  contedo de  MENSAGEM.DO  use  um  TYPE  do  MS-DOS
            ([C:\] TYPE  MENSAGEM.DOC [ENTER])  ou  carregue  o  arquivo
            "para dentro"  do ambiente  do Pascal  (pressione F3, digite
            MENSAGEM.DOC e pressione ENTER). O caracter que sinalizar o
            fim da  mensagem digitada  ser o caracter #27, que  gerado
            quando se pressiona a tecla ESC.
            
            PROGRAM Mensagem;
            
            USES
              Crt;

                                       - 131 -





            
            TYPE
              ArquivoDeCaracteres = FILE OF char;
            
            VAR
              x: char;
              arq: ArquivoDeCaracteres;
            
            CONST
              TeclaESC = #27;
            
            BEGIN
              ASSIGN(arq, 'MENSAGEM.DOC');    (* Associa ARQ a *)
                                              (* MENSAGEM.DOC. *)
              REWRITE(arq);  (* "Abre" o arquivo para gravao *)
            
              repeat
                x := ReadKey;    (* L um caracter x do teclado, mas *)
                                 (* no mostra-o na tela.            *)
                Write(arq, x);   (* Grava x no arquivo do disco.     *)
              until x = TeclaESC;
            
              CLOSE(arq)   (* "Fecha" o arquivo *)
            END.
            
                 Para ver  a mensagem  na tela   medida  em que ela for
            sendo digitada,  deve-se colocar  um  "Write(x);"  antes  do
            "until".
            
            Exemplo: O programa a seguir, lista na tela o contedo de um
            arquivo, cujo nome deve ser fornecido pelo usurio durante a
            execuo do  programa. Ele  produz  o  mesmo  efeito  que  o
            comando TYPE  do MS-DOS.  Neste caso, declararemos o arquivo
            como um  arquivo de  caracteres (FILE  OF CHAR).  Usamos uma
            funo booleana  pr-definida EOF(arq) que ser TRUE somente
            quando o  fim do  arquivo ARQ  for  alcanado  (EOF  so  as
            iniciais de "end of file").
            
            PROGRAM Mostre;
            
            (* Mostra o contedo de um arquivo do disco na tela *)
            
            TYPE
              arquivo = FILE OF char;
            
            VAR
              x: char;
              arq: arquivo;
              nome: string;
            
            BEGIN
              Write('Nome do arquivo? ');
              Readln(nome);
              ASSIGN(arq, nome);       (* Associa ARQ a NOME *)

                                       - 132 -





              RESET(arq);   (* "Abre" o arquivo para leitura *)
            
              repeat
                Read(arq, x); (* L um caracter  X  em ARQ, *)
                Write(x);     (* mostra X na tela ...       *)
              until EOF(arq); (* ... at o fim do arquivo.  *)
            
              CLOSE(arq) (* "Fecha" o arquivo *)
            END.
            
            Exemplo: Um  parmetro para  um programa   um  dado  que  
            fornecido no  mesmo momento  em que  o programa  inicia  sua
            execuo. O  programa anterior  ficaria mais interessante se
            quando ele estivesse compilado em disco (com extenso .EXE),
            pudssemos digitar o nome do programa e o nome do arquivo de
            uma s vez no "prompt" do DOS:
                           [C:\] MOSTRE AUTOEXEC.BAT
            onde, "MOSTRE"  o nome que estamos atribuindo ao programa e
            "AUTOEXEC.BAT"  o nome do arquivo que queremos ver na tela.
            Neste  caso,  "AUTOEXEC.BAT"    um  parmetro  do  programa
            "MOSTRE.EXE". Para  um programa  admitir parmetros, devemos
            usar as funes PARAMCOUNT e PARAMSTR:
            
                 PARAMCOUNT  ---> Fornece a quantidade de parmetros
                                  fornecidos ao programa;
                 PARAMSTR(n) ---> n-simo parmetro (sempre conside-
                                  rado como sendo do tipo string).
            
                 Sempre que  se usa  parmetros em  um programa,  usual
            colocar um  IF no  incio do  programa prevendo  o  caso  do
            usurio no  digitar parmetros.  O IF  serve para dizer que
            tipos de parmetros o programa espera.
                 Se houver  necessidade  de  se  entrar  com  parmetros
            numricos, ento  deve-se usar uma funo pr-definida (VAL)
            que converte strings em valores numricos.
            
            PROGRAM Mostre; (* VERSO 2 *)
            
            (* Mostra o contedo de um arquivo do disco na tela *)
            
            TYPE
              arquivo = FILE OF char;
            
            VAR
              x: char;
              arq: arquivo;
              nome: string;
            
            BEGIN
            
              if (PARAMCOUNT = 0) then          (* Se a quantidade de *)
              begin              (* parmetros for nula, ento o pro- *)
                Writeln;         (* grama lembra para digitar um.     *)
                Writeln('Forma de usar: MOSTRE NomeDoArquivo');

                                       - 133 -





                Writeln;
                Halt;
              end
              else
                nome := PARAMSTR(1);
            
              ASSIGN(arq, nome);       (* Associa ARQ a NOME *)
              RESET(arq);   (* "Abre" o arquivo para leitura *)
            
              repeat
                Read(arq, x); (* L um caracter  X  em ARQ, *)
                Write(x);     (* mostra X na tela ...       *)
              until EOF(arq); (* ... at o fim do arquivo.  *)
            
              CLOSE(arq) (* "Fecha" o arquivo *)
            END.
            
                 Para se  executar um  programa que  exiga parmetros no
            ambiente do  Pascal, pressione ALT-O para ter acesso ao tem
            "Options"  do   menu  principal,   e  digite   um   "P"   de
            "Parameters". Digite  na janelinha  que aparecer  a lista de
            parmetros, pressionando ENTER e ESC no final.
            
            Exemplo: Neste  e nos  prximos 3 exemplos, vamos elaborar 4
            programas que  servem para  manusear uma  agenda  de  nomes,
            telefones e endereos gravada em AGENDA.DAT.
                 Antes vamos  criar um  arquivo de incluso DEF_TIPO.INC
            com a definio do tipo usado e do procedimento COR.
            
            (* ------------------------------------------------------ *)
            
            TYPE
              dado = record
                nome: string[30];
                endereco: string[80];
                telefone: longint;
              end;
            
            CONST
              NomeArq = 'AGENDA.DAT';
            
            PROCEDURE Cor(x, y: byte);
            
            BEGIN
              TextColor(x);      (* Define a cor do texto *)
              TextBackground(y)  (* e a cor de fundo.     *)
            END;
            
            (* ------------------------------------------------------ *)
            
                 Usaremos as  funes WHEREX e WHEREY da unidade CRT que
            servem para  guardar a  posio do cursor na tela e a funo
            pr-definida UPCASE(x) que transforma x em maisculas.
            

                                       - 134 -





            PROGRAM Inicia;
            
            USES
              Crt;
            
            {$I DEF_TIPO.INC}
            
            VAR
              arq: file of dado;
              x: dado;
              resposta: char;
              continua: boolean;
              PosX, PosY: byte;
            
            BEGIN
              Assign(arq, NomeArq);
              Rewrite(arq);   (* Cria no disco o arquivo "AGENDA.DAT" *)
            
              repeat
                ClrScr;
            
                Writeln; Cor(7, 0);
                Write('Nome ');
            
                (* (PosX, PosY) guardam a posio do cursor na tela: *)
                PosX := WhereX; PosY := WhereY;
            
                (*  mostrada uma barra luminosa de tamanho 30: *)
                Cor(0, 7); Write(' ':30);
            
                (* Volta ao inicio da barra luminosa: *)
                GoToXY(PosX, PosY);
                Readln(x.nome);
            
                Writeln; Cor(7, 0);
                Write('Endereco ');
                PosX := WhereX; PosY := WhereY;
                Cor(0, 7); Write(' ':60);
                GoToXY(PosX, PosY);
                Readln(x.endereco);
            
                Writeln; Cor(7, 0);
                Write('Telefone ');
                PosX := WhereX; PosY := WhereY;
                Cor(0, 7); Write(' ':7);
                GoToXY(PosX, PosY);
                Readln(x.telefone);
            
                Write(arq, x); (* Grava x no arquivo *)
            
                Writeln; Writeln; Cor(7, 0);
                Write('Continua? (s/n) ');
                Readln(resposta);
                Writeln;

                                       - 135 -





                continua := (UpCase(resposta) = 'S');
                (* CONTINUA ser TRUE quando RESPOSTA = 'S' *)
                (* ou RESPOSTA = 's'                         *)
              until not continua;
            
              Close(arq);
            END.
            
            Exemplo: Neste  exemplo,  feito um programa para mostrar na
            tela os  dados da agenda criada pelo programa anterior. Deve
            entrar como  parmetro um  1, um 2 ou um 3 que correspondem,
            respectivamente, a  mostrar os  nomes e endereos da agenda,
            os nomes e telefones, e os nomes, endereos e telefones.
            
            PROGRAM Listagem;
            
            USES
              Crt;
            
            {$I DEF_TIPO.INC}
            
            VAR
              arq: file of dado;
              x: dado;
              pausa: char;
              opcao: string[1];
            
            BEGIN
              if (ParamCount = 0) then     (* Se nao for fornecido um *)
              begin      (* parametro, o programa lembra como faze-lo *)
                Writeln;
                Writeln('Forma de usar: LISTAGEM opcao');
                Writeln;
                Writeln('onde opcao = 1, para a listagem dos',
                                                    ' nomes/enderecos');
                Writeln('     opcao = 2, para a listagem dos',
                                                    ' nomes/telefones');
                Writeln('     opcao = 3, para a listagem dos',
                                          ' nomes/enderecos/telefones');
                Writeln;
                Halt;
              end;
            
              opcao := ParamStr(1); (* OPCAO  o primeiro parametro *)
            
              Assign(arq, NomeArq);
              Reset(arq);  (* "Abre"  AGENDA.DAT  para leitura *)
            
              repeat
                Read(arq, x);
                Writeln;
                Writeln('Nome     : ', x.nome);
                if (opcao <> '2') then
                  Writeln('Endereco : ', x.endereco);

                                       - 136 -





                if (opcao <> '1') then
                  Writeln('Telefone : ', x.telefone);
                Writeln;
                Writeln('Para continuar, pressione qualquer tecla.');
                pausa := ReadKey;
              until Eof(arq);
            
              Close(arq);
            END.
            
            Exemplo: Com este exemplo, pode ser retirado ou acrescentado
            nomes   agenda. Para  isso   criado um arquivo auxiliar. O
            arquivo inicial  no final do programa  apagado do disco com
            o comando  ERASE(arquivo) e  o arquivo  auxiliar  renomeado
            com o  nome do  arquivo original  com o comando RENAME, cuja
            sintaxe  RENAME(Nome_velho, Nome_novo).
            
            PROGRAM Atualiza;
            
            USES
              Crt;
            
            {$I DEF_TIPO.INC}
            
            VAR
              arq, arq_aux: file of dado;
              x: dado;
              opcao, resposta: char;
              continua: boolean;
              PosX, PosY: byte;
            
            BEGIN
              Writeln;
              Write('Acrescenta ou retira dados do arquivo? (A/R) ');
              Readln(opcao);
            
              Assign(arq, NomeArq);
              Assign(arq_aux, 'ARQ_AUX.DAT');
              Reset(arq);        (* "Abre" para leitura AGENDA.DAT e *)
              Rewrite(arq_aux);  (* cria o ARQ_AUX.DAT               *)
            
              if (UpCase(opcao) = 'R') then
              begin    (* Caso em que sao retirados dados do arquivo *)
            
                repeat
                  Read(arq, x);
                  Writeln;
                  Writeln('Nome     : ', x.nome);
                  Writeln('Endereco : ', x.endereco);
                  Writeln('Telefone : ', x.telefone);
                  Writeln;
                  Writeln('---> Pressione "E" para eliminar ou "I" ',
                                                       'para ignorar.');
            

                                       - 137 -





                  (* Se for pressionado um "E" ou "e", ento o registro
                     no ser gravado no arquivo auxiliar, e conseqen-
                     temente, ser eliminado quando  o arquivo auxiliar
                     passar a ser o  AGENDA.DAT no final do programa *)
                  if (UpCase(ReadKey) <> 'E') then
                    Write(arq_aux, x);
                until Eof(arq);
              end
            
              else
            
              begin (* Caso em que dados sao acrescentados ao arquivo *)
                repeat
                  Read(arq, x);
                  Write(arq_aux, x);
                until Eof(arq);
            
                repeat
                  ClrScr;
                  Writeln; Cor(7, 0);
                  Write('Nome ');
                  PosX := WhereX; PosY := WhereY;
                  Cor(0, 7); Write(' ':30);
                  GoToXY(PosX, PosY);
                  Readln(x.nome);
            
                  Writeln; Cor(7, 0);
                  Write('Endereco ');
                  PosX := WhereX; PosY := WhereY;
                  Cor(0, 7); Write(' ':60);
                  GoToXY(PosX, PosY);
                  Readln(x.endereco);
            
                  Writeln; Cor(7, 0);
                  Write('Telefone ');
                  PosX := WhereX; PosY := WhereY;
                  Cor(0, 7); Write(' ':7);
                  GoToXY(PosX, PosY);
                  Readln(x.telefone);
            
                  Write(arq_aux, x);
            
                  Writeln; Writeln; Cor(7, 0);
                  Write('Continua? (s/n) ');
                  Readln(resposta);
                  Writeln;
                  continua := (UpCase(resposta) = 'S');
                until not continua;
              end;
            
              Close(arq);       (* "Fecha" os *)
              Close(arq_aux);   (* arquivos   *)
              Erase(arq);       (* Apaga o AGENDA.DAT *)
              Rename(arq_aux, NomeArq); (* Renomeia ARQ_AUX.DAT *)

                                       - 138 -





                                        (* de AGENDA.DAT        *)
            END.
            
            Exemplo: Neste  exemplo,   procurado em  AGENDA.DAT um nome
            fornecido  como   parmetro.  O  mtodo  utilizado  chama-se
            pesquisa seqencial  de dados  e no   um mtodo eficiente.
            Ser mostrado no final deste captulo um mtodo eficiente de
            se procurar dados em um arquivo.
            
            PROGRAM Procure;
            
            USES
              Crt;
            
            {$I DEF_TIPO.INC}
            
            VAR
              arq: file of dado;
              x: dado;
              pausa: char;
              NomeProcurado: string;
              i: byte;
            
            BEGIN
              Writeln;
              Writeln('PROCURANDO UM NOME NO ARQUIVO "AGENDA.DAT"');
              if (ParamCount = 0) then
              begin
                Writeln;
                Writeln('Forma de usar: PROCURE Nome_a_ser_procurado');
                Writeln; Halt;
              end;
            
              (* O nome  a ser procurado  um "somatorio" de strings,
                 pois  ele pode ser um nome composto, com espaos em
                 branco entre os nomes,  e neste caso cada nome ser
                 considerado como mais um parmetro. Por exemplo, se
                 for digitado no "prompt" do DOS
                           [C:\] PROCURE Jose da Silva
                 ento esto sendo fornecido 3 parmetros a PROCURE *)
            
              NomeProcurado := '';
              for i := 1 to ParamCount do
              begin
                NomeProcurado := NomeProcurado + ParamStr(i);
                if (i < ParamCount) then
                  NomeProcurado := NomeProcurado + ' ';
              end;
            
              Assign(arq, NomeArq);
              Reset(arq);
            
              repeat
                Read(arq, x);        (* L dados em ARQ at encontrar *)

                                       - 139 -





                if (x.nome = NomeProcurado) then    (* o nome  procu- *)
                begin                (* rado ou atingir o fim  do ar- *)
                  Writeln;           (* quivo.                        *)
                  Writeln('Nome     : ', x.nome);
                  Writeln('Endereco : ', x.endereco);
                  Writeln('Telefone : ', x.telefone);
                  Writeln;
                  Halt;
                end;
              until Eof(arq);
            
              Close(arq);
            END.
            
            9.2. ARQUIVOS TIPO TEXT 
            
                 O Pascal possui um tipo de arquivo pr-definido chamado
            TEXT.   um tipo  parecido com  um FILE  OF CHAR, arquivo de
            caracteres. Em  todo arquivo, os dados devem ser todos de um
            mesmo tipo.  No caso  de se  precisar  gravar  em  um  mesmo
            arquivo nmeros e strings, por exemplo, temos duas solues:
            ou usamos  o tipo  TEXT para  o arquivo  ou usamos registros
            para unificar  todos os dados e declaramos o tipo do arquivo
            como sendo um arquivo de registros.
                 O Pascal  tem vrios  procedimentos ou funes para uso
            exclusivo dos arquivos tipo TEXT:
            
                 WRITELN(arquivo, var1, var2, ...) ---> Grava os valores
                                 de var1, var2, ... no arquivo e  acres-
                                 centa no  final um sinal de nova linha.
                 READLN(arquivo, var1, var2, ...) ---> Recupera os dados
                                 gravados por um WRITELN.
                 APPEND(arquivo) ---> Abre o arquivo para serem acres-
                                      tados dados no final.
            
            Exemplo: Neste  exemplo, criaremos  um arquivo tipo TEXT com
            vrias mensagens  a respeito da soma de alguns termos de uma
            srie.
                 A unidade padro DOS possui um procedimento GETTIME que
            l a  hora interna  do computador, e devolve o resultado nos
            seus 4 parmetros:
            
                 GetTime(h, min, s, cents)  ---> Retorna a  hora interna
                                                 h:min:s:cents.
            
                 Neste exemplo,  calcularemos a  hora do incio e do fim
            dos clculos.  No final ser calculado o tempo total gasto e
            o resultado gravado no arquivo SOMA.TXT. Isto pode ser usado
            em vrios computadores para comparar suas velocidades.
            
            PROGRAM TestandoVelocidade;
            
            USES
              Crt, Dos;

                                       - 140 -





            
            CONST
              total = 100000; (* Total de termos somados *)
            
            VAR
              arq: TEXT;
              h, min, s, cents: word;
              i: longint;
              inicio, fim, soma: real;
            
            BEGIN
              Assign(arq, 'SOMA.TXT');
              Rewrite(arq);
              ClrScr;
            
              (* Mostra uma mensagem em vdeo reverso: *)
              TextBackground(white); TextColor(black);
              Writeln('Calculando a soma de ', total,
                                          ' termos da serie harmonic);
              TextBackground(black); TextColor(white);
              Writeln;
            
              GetTime(h, min, s, cents); (* Verifica qual a hora do *)
                                         (* inicio da execuo.     *)
            
              (* Mostra a hora inicial na tela e grava-a em disco *)
              Writeln('Inicio ''as ', h:2, ':', min:2, ':', s:2,
                                                          ':', cents:2);
              Writeln(arq, 'Inicio ''as ', h:2, ':', min:2, ':', s:2,
                                                          ':', cents:2);
            
              (* Transforma a hora inicial em segundos: *)
              inicio := h*3600 + min*60 + s + cents/100;
            
              soma := 0;           (* Calculo do somatorio *)
              for i := 1 to total do
              begin
                if i mod 1000 = 0 then     (* Mostra uma estatstica *)
                begin                      (* a respeito do andamen- *)
                  GoToXY(10, 6);           (* to da execuo.        *)
                  Write(100*i/total:5:1, '% completo');
                end;
                soma := soma + 1/i;
              end;
              Writeln;
              Writeln; Writeln('Soma = ', soma:8:3);
            
              GetTime(h, min, s, cents);  (* Verifica qual a hora *)
                                          (* do trmino.          *)
              Writeln;
            
              (* Mostra a hora final na tela e grava-a em disco *)
              Writeln('Final ''as ', h:2, ':', min:2, ':',
                                                     s:2, ':', cents:2);

                                       - 141 -





            
              Writeln(arq, 'Final ''as ', h:2, ':', min:2, ':',
                                                     s:2, ':', cents:2);
            
              (* Transforma a hora final em segundos: *)
              fim := h*3600 + min*60 + s + cents/100;
            
              Writeln;
              Writeln('Tempo total gasto = ', (fim - inicio):5:2,
                                                           ' segundos');
              Writeln;
            
              (* Grava o resultado do somatrio e o tempo gasto *)
              Writeln(arq, 'Soma dos ', total, ' termos = ', soma:8:3);
              Writeln(arq, 'Tempo total gasto = ', (fim - inicio):5:2,
                                                           ' segundos');
              Writeln;
              Close(arq);
            END.
            
                 Executando-se este programa, obtemos algo como:
            
            Calculando a soma de 100000 termos da serie harmonica
            
            Inicio 'as  7:15:30:19
            
                     100.0% completo
            
            Soma =   12.090
            
            Final 'as  7:17: 5:16
            
            Tempo total gasto = 94.97 segundos
            
                 Temos tambm  um  arquivo  SOMA.TXT  no  disco  com  as
            seguintes linhas:
            
            Inicio 'as  7:15:30:19
            Final 'as  7:17: 5:16
            Soma dos 100000 termos =   12.090
            Tempo total gasto = 94.97 segundos
            
                 Gravando  em  disco  o  resultado  da  execuo  de  um
            programa, temos  a vantagem de poder guardar indefinidamente
            os resultados.
            
            9.3. OUTROS COMANDOS PARA MANIPULAO DE ARQUIVOS    
            
                 Alm   dos   comandos   bsicos   de   leitura/gravao
            apresentados anteriormente,  o Pascal  possui muitos  outros
            comandos para  manipulao dos dados de um arquivo do disco.
            Alguns deles so:
            
            SEEK(arq, n) ---> Ajusta a leitura/gravao de dados de ARQ

                                       - 142 -





                              para  a posio do  (n + 1)-simo dado do
                              arquivo. Por exemplo, SEEK(arq, 5) ajusta
                              para a sexta posio  a  leitura/gravao
                              de dados.
            
            FILESIZE(arq) ---> Funo que retorna o tamanho de ARQ.
            
            FILEPOS(arq)  ---> Funo que retorna a  posio  atual  de
                               leitura/gravao no arquivo.
            
                 Os 3 comandos acima no podem ser usados em arquivos do
            tipo TEXT.
            
            Exemplo: O  fragmento a seguir, l os dados do arquivo ARQ_X
            gravados entre  1/2 e  3/4 do  arquivo (logo,  se o  arquivo
            contiver  100   dados,  o   fragmento  l   apenas   aqueles
            compreendidos nas posies 50, 51, ..., 75).
                 ...
                 TamanhoDoArquivo := FileSize(Arq_X);
                 Seek(Arq_X, TamanhoDoArquivo div 2 - 1);
                 repeat
                   Read(Arq_X, dado);
                   ...
                 until FilePos(Arq_X) = 3*(TamanhoDoArquivo div 4);
                 ...
            
                 A  unidade   padro  DOS   contm  muitas   funes   e
            procedimentos para manipular arquivos. Alguns deles so:
            
            DISKFREE(NumeroDoDrive) ---> Funo que retorna a quantidade
                                         de bytes livres no drive  asso-
                                         ciado a NUMERODODRIVE.
            DISKSIZE(NumeroDoDrive) ---> Funo que retorna o tamanho do
                                         disco associado a NUMERODODRIVE
            
                 onde NUMERODODRIVE = 0 para o drive atual,
                      NUMERODODRIVE = 1 para o drive A,
                      NUMERODODRIVE = 2 para o drive B, etc.
            
            GETFATTR(arq, atributo) ---> Procedimento que  retorna o
                                         ATRIBUTO do arquivo ARQ.
            SETFATTR(arq, atributo) ---> Ajusta  ARQ  com o ATRIBUTO
                                         fornecido.
            
                 Nos procedimentos  acima, ATRIBUTO   uma  varivel  do
            tipo byte,  que deve  ser a  soma dos  valores  da  seguinte
            tabela (Obs.: listamos apenas alguns atributos).
            
                            Atributo      Especificao
                           --------------------------------
                               1     Arquivo s de leitura
                               2     Arquivo escondido
                               4     Arquivo de sistema
                           --------------------------------

                                       - 143 -





            
            Exemplo: O presente exemplo, modifica para "s de leitura" o
            atributo dos  arquivos cujos  nomes  forem  fornecidos  como
            parmetros .  No final,   mostrado  o tamanho  do disco e a
            quantidade de bytes livres do drive atual.
            
            PROGRAM Atributo;
            
            USES
              Dos;
            
            VAR
              i: byte;
              arquivo: file of byte;
            
            BEGIN
              for i := 1 to ParamCount do
              begin
                Assign(arquivo, ParamStr(i));
                SetFAttr(arquivo, 1); (* Ajusta o atributo  do *)
                              (* arquivo para "s de leitura". *)
              end;
            
              Writeln;
              Writeln('Tamanho do disco = ', DiskSize(0));
              Writeln('Quantidade de bytes livres = ', DiskFree(0))
            END.
            
                 Os arquivos  listados como  parmetro do programa acima
            no podero  ser apagados com um DEL ou ERASE do DOS.  como
            se tivesse  sido usado um [C:\] ATTRIB +R ARQUIVO do sistema
            operacional ou  algum utilitrio  especfico como  o  FA  do
            Norton Utilities  ([C:\] FA  ARQUIVO /R+). Para "desligar" o
            atributo  "s   de  leitura",   troque  no   programa  acima
            "SetFAttr(arquivo, 1);" por "SetFAttr(arquivo, 0);".
                 No podem ser usados os caracteres * ou ? nos nomes dos
            arquivos especificados como parmetros.
            
            9.4. CLASSIFICAO DE DADOS E PESQUISA BINRIA    
            
                 Existem muitos  algoritmos  para  se  ordenar  vetores,
            colocando-se em  uma ordem desejada (crescente, decrescente,
            alfabtica, etc.).  Nesta seo vamos descrever um algoritmo
            bastante  simples,   til  para   se  classificar  conjuntos
            pequenos. No  captulo 11  descreveremos um  algoritmo  mais
            eficiente, ou  seja, mais  rpido, para  classificar  dados.
            Descreveremos como colocar em ordem crescente os primeiros n
            elementos  de  um  vetor  de  elementos  reais.  Vetores  de
            caracteres  ou   strings  podem   ser  ordenados   de  forma
            completamente anloga.
            
            (1) Usamos  uma "chave",  que neste  caso ser  uma varivel
            booleana que  chamaremos EstarOrdenado,  para  testar  se  o

                                       - 144 -





            conjunto j  est na ordem desejada. Atribumos inicialmente
            o valor TRUE a essa varivel.
            (2) Fazemos  um i variar de 1 at (n - 1) e, para cada valor
            de i, comparamos o elemento de ordem i com o de ordem (i+1).
            Se eles  estivem fora de ordem, ento trocamos suas posies
            e atribumos a EstarOrdenado o valor FALSE.
            (3) Se  EstarOrdenado for  FALSE, ento executamos (1) e (2)
            novamente. Se  EstarOrdenado for TRUE, ento o conjunto est
            na ordem desejada.
            
                 Note que  a nica  opo para  a varivel EstarOrdenado
            ser FALSE   que,  na  execuo  do  passo  (2),  existam  2
            elementos consecutivos fora da ordem desejada.
            
            Exemplo:
            
            PROGRAM Classifica;
            
            CONST
              MaxElementos = 100;
            
            TYPE
              vetor = array [1..MaxElementos] of real;
            
            VAR
              teste, EstarOrdenado: boolean;
              i, n: byte;
              v: vetor;
            
            PROCEDURE Troque(VAR x, y: real);
            
            VAR
              aux: real;
            
            BEGIN
              aux := x; x := y; y := aux
            END;
            
            BEGIN
              Writeln;
              Writeln('COLOCANDO EM ORDEM CRESCENTE n NUMEROS');
              Writeln;
              Write('Valor de n? '); Readln(n);
              Writeln;
              Writeln('Digite os nmeros a serem ordenados: ');
              for i := 1 to n do Read(v[i]);
            
              repeat
                EstarOrdenado := true;
                for i := 1 to n - 1 do
                begin
                  teste := (v[i] <= v[i + 1]); (* TESTE ser TRUE se *)
                  if not teste then            (* v[i] e v[i + 1] es- *)
                  begin                        (* tiverem  na   ordem *)

                                       - 145 -





                    EstarOrdenado := false;    (* desejada.           *)
                    Troque(v[i], v[i + 1]);
                  end;
                end;
              until EstarOrdenado;
            
              Writeln;
              Writeln('Numeros fornecidos em ordem crescente:');
              for i := 1 to n do Write(v[i]:8:2);
            END.
            
                 Para ordenar  os nmeros  em ordem  decrescente,  basta
            trocar o "<=" por ">=" na atribuio do valor de TESTE.
            
            Exemplo: Neste exemplo, construmos vrios procedimentos que
            cria, classifica  e mostra um arquivo formado por uma matriz
            de 50 registros do tipo ALUNO.
            
            CONST
              MaxAlunos = 50;
            
            TYPE
              aluno = record
                nome: string[30];
                matricula: string[8];
                nota: real
              end;
            
              turma = array [1..MaxAlunos] of aluno;
            
            (* ------------------------------------------------------ *)
            
            PROCEDURE ClassificaNomes(VAR v: turma);
            
            (* Classifica os registros de v colocando os nomes em ordem
               alfabetica *)
            
            
            PROCEDURE Troque(VAR v1, v2: aluno);
            
            (* Troca os valores dos registros v1 e v2 *)
            
            VAR
              aux: aluno;
            
            BEGIN
              aux := v1;
              v1 := v2;
              v2 := aux
            END; (* fim do procedimento Troque *)
            
            
            VAR
              teste, EstarOrdenado: boolean;

                                       - 146 -





              i: byte;
            
            BEGIN (* Inicio de ClassificaNomes *)
              repeat
                EstarOrdenado := true;
            
                for i := 1 to MaxAlunos - 1 do
                begin
                  teste := (v[i].nome < v[i + 1].nome);
            
               (*     Para classificar as notas em ordem crescente,
                  deveriamos ter:
                  teste := (v[i].nota < v[i + 1].nota)
                      Para classificar as notas em ordem decrescente:
                  teste := (v[i].nota > v[i + 1].nota)
                      Para classificar as matriculas em ordem crescente:
                  teste := (v[i].matricula < v[i + 1].matricula)      *)
            
                  if not teste then
                  begin
                    EstarOrdenado := false;
                    Troque(v[i], v[i + 1]);
                  end;
            
                end;
              until EstarOrdenado;
            
            END; (* Fim de ClassificaNomes *)
            
            (* ------------------------------------------------------ *)
            
            PROCEDURE CriaArquivo(NomeDoArquivo: string);
            
            (* Cria o arquivo cujo nome  fornecido como parmetro *)
            
            VAR
              t: turma;
              i: byte;
              TArq: file of turma;
            
            BEGIN
              Assign(TArq, NomeDoArquivo);
              Rewrite(TArq);
            
              (* O WITH  a seguir  permite  que  sejam usados *)
              (* "nome", "matricula"  e  "nota",  ao inves de *)
              (* "t[i].nome", "t[i].matricula" e "t[i].nota"  *)
              for i := 1 to MaxAlunos do
                with t[i] do
                begin
                  Write('Nome do aluno ', i, ' : '); Readln(nome);
                  Write('Matricula : '); Readln(matricula);
                  Write('Nota : '); Readln(nota);
                  Writeln;

                                       - 147 -





                end;
            
              Writeln;
              Writeln('Gravando os dados no arquivo "', NomeDoArquivo,
                                                               '" ...');
              Write(TArq, t);
              Close(TArq);
            
            END; (* Fim de CriaArquivo *)
            
            (* ------------------------------------------------------ *)
            
            PROCEDURE MostraArquivo(NomeDoArquivo: string);
            
            (* Lista na tela os registros que formam um arquivo *)
            
            VAR
              t: turma;
              i, j: byte;
              TArq: file of turma;
            
            BEGIN
              Assign(TArq, NomeDoArquivo);
              Reset(TArq);
              Read(TArq, t);
              Close(TArq);
            
              Writeln;
              for i := 1 to MaxAlunos do
                with t[i] do
                begin
                  Write(' ':10, nome);
                  for j := 1 to 30 - Length(nome) do
                    Write('.');
                  Writeln(nota:6:2);
                end;
            
            END; (* Fim de MostraArquivo *)
            
            (* ------------------------------------------------------ *)
            
            PROCEDURE ClassificaArquivo(NomeDoArquivo: string);
            
            (* Ordena os registros do arquivo em ordem alfabtica *)
            
            VAR
              TArq: file of turma;
              t: turma;
            
            BEGIN
              Assign(TArq, NomeDoArquivo);
              Reset(TArq); (* "Abre" TARQ para leitura *)
              Read(TArq, t);
              ClassificaNomes(t);

                                       - 148 -





            
              Rewrite(TArq);   (* "Abre" TARQ para gravacao, apagando *)
                               (* todos os dados que existiam.        *)
              Write(TArq, t);  (* Grava os novos dados em TARQ.       *)
              Close(TArq)
            END; (* fim de ClassificaArquivo *)
            
            (* ------------------------------------------------------ *)
            
                 Se os  procedimentos anteriores  estiverem gravados  em
            CLAS_ARQ.IN ento podemos executar o seguinte:
            
            PROGRAM ClassificandoArquivo;
            
            {$I CLAS_ARQ.INC}
            
            BEGIN
              CriaArquivo('NOTAS.DAT');
              ClassificaArquivo('NOTAS.DAT');
              MostraArquivo('NOTAS.DAT')
            END.
            
            Exemplo:  Para  encerrar  este  captulo,  descreveremos  um
            mtodo simples  e eficiente  de se  procurar um  dado em  um
            vetor ordenado  em ordem  crescente  (ou  alfabtica).  Esse
            mtodo   conhecido como  pesquisa binria.  Suponhamos  que 
            queremos encontrar  um  elemento  X  em  um  vetor  ordenado
            (V[1], ..., V[n]).
            
            (1) Consideramos  dois ndices  INICIO e  FIM cujos  valores
            iniciais definiremos como INICIO := 1 e FIM := n.
            (2) Calculamos  o  ponto  mdio  do  intervalo  INICIO..FIM,
            MEDIO := (INICIO + FIM) div 2.
            (3) Se  o elemento procurado X preceder V[meio], ou seja, se
            X <  V[meio], ento  concentraremos as atenes no intervalo
            de ndices 1..(MEIO - 1). Fazemos isso, redefinindo FIM como
            sendo igual a (MEIO - 1).
                 Se X  suceder V[meio],  isto ,  se X  > V[meio], ento
            ficamos s  com o  intervalo de  ndices  (MEIO  +  1)..FIM,
            redefinindo INICIO := MEIO + 1.
                 Se X  no preceder  e  nem  suceder  V[meio],  ento  X
            coincide com V[meio] e a procura est terminada.
            (4) Cada  vez que  o passo  (3)   executado, o valor de FIM
            diminui e o valor de INICIO aumenta. Se o elemento procurado
            no fizer  parte do  vetor V,  ento chegar um ponto em que
            INICIO ser  maior do que FIM, uma situao que servir para
            indicar que  no adianta mais procurar X em V e que chegou a
            hora de  encerrar. Logo,  para continuar a procura de X em V
            deve-se executar  os passos  (2) e (3) sucessivas vezes, at
            que INICIO > FIM.
            
                 Codificaremos o  algoritmo acima  sob a forma da funo
            PESQBIN a  seguir, que retorna a posio no vetor V onde o X

                                       - 149 -





            se encontra.  Convencionaremos que  se a  funo retornar um
            valor negativo, ento X no foi encontrado em V.
            
            CONST
              max = 20;
            
            TYPE
              vetor = array[1..max] of real;
            
            (* ------------------------------------------------------ *)
            
            FUNCTION PesqBin(v: vetor; x: real; n: byte): shortint;
            
            VAR
              inicio, meio, fim: byte;
            
            BEGIN
              inicio := 1;
              fim := n;
            
              repeat
                meio := (inicio + fim) div 2;
                if (x < v[meio]) then
                  fim := meio - 1
                else
                  if (x > v[meio]) then
                    inicio := meio + 1
                  else
                  begin
                    PesqBin := meio; (* Neste caso, X = V[meio] *)
                    Exit
                  end;
              until (inicio > fim);
            
              PesqBin := -1; (* Caso em que X nao faz parte de V *)
            END;
            
            (* ------------------------------------------------------ *)
            
                 A pesquisa  binria s  deve ser aplicada em vetores em
            ordem crescente.  Quando ela  pode ser aplicada, ela  muito
            mais  eficiente  do  que  a  pesquisa  seqencial  usada  no
            programa-exemplo PROCURE do incio deste captulo.
            
            PROGRAM Testando_a_pesquisa_binria;
            
            {$I PESQ_BIN.INC} (* Supondo que o tipo e funcao anteri- *)
                              (* res esto gravados nesse arquivo.   *)
            
            CONST
              V: vetor = (-50, -37, -20, -10, -5, -3, -2, -1,  0,    1,
                            2,   3,   5,   7,  8,  9, 10, 13, 92, 101);
            
            VAR

                                       - 150 -





              x: real;
              indice: shortint;
            
            BEGIN
              Write('Valor de x? '); Readln(x);
            
              indice := PesqBin(v, x, 20);
            
              if (indice < 0) then
                Writeln('Valor nao encontrado no vetor V.')
              else
                Writeln('Valor encontrado em V na posicao ', indice);
            END.
            
            9.5. EXERCCIOS  
            
            1) Conte  quantas letras  "A", minsculas ou maisculas, tem
            um texto, cujo nome  fornecido como parmetro do programa.
            
            2) Elabore  um progama  ELIMVIRG.PAS que  elimine  todas  as
            vrgulas de um texto. Ele deve funcionar da seguinte forma:
                    C:\> ELIMVIRG TextoFonte TextoSemVirgulas
            
            3) Determine  quais so os caracteres que o Turbo Pascal usa
            para sinalizar um fim de linha em um arquivo .PAS do disco.
            
            4) Em  um disco  esto gravados  dois arquivos:  um  com  os
            nmeros de  matrcula e  os salrios dos funcionrios de uma
            empresa, e  o outro  com as  matrculas e  os nomes  de cada
            funcionrio. Imprima  uma listagem  em ordem  alfabtica dos
            nomes  dos   funcionrios   acompanhados   dos   respectivos
            salrios.
            
            5) Envie  cada caracter  de um  arquivo para uma impressora,
            com exceo  da seqncia  de caracteres \xy, onde x  ', `,
            ^, ", ~ ou uma vrgula e y  caracter qualquer, que deve ser
            impressa como sendo xzy, onde z  o caracter #8.
                 O caracter  #8 sinaliza para a impressora um retrocesso
            de uma  letra. Com isso, podemos acentuar de forma eficiente
            todas as  letras: basta enviar para a impressora o acento, o
            #8 e  a letra.  A crase  pode ser  gerada com acento_grave +
            retrocesso + A, um '' com acento_agudo + retrocesso + E, um
            '' com  til + retrocesso + A, um '' com acento_circunflexo
            + retrocesso  + O,  o cedilha  pode ser gerado com vrgula +
            retrocesso + C e o trema com aspas + retrocesso + U.
                 Voc deve, ento, elaborar um programa que:
            
                    leia no arquivo ...          ... e imprima
                  -----------------------------------------------
                      Op\,c\~ao                        Opo
                      Matem\'atica                     Matemtica
                      seq\"u\^encia                    seqncia
                      C:\UTILIT\SK                     C:\UTILIT\SK
                      \`as vezes                       s vezes

                                       - 151 -
