
Captulo 25 - arquivos

  Para comearmos a manipular arquivos, necessitamos lembrar alguns concei-
tos bsicos a respeito dos mesmos. Quando pensamos em arquivos, a primeira
coisa que nos vem  memria  a idia de um arquivo fsico, aquele de ao
normalmente encontrados em escritrios de diversas empresas. Pois bem, mas
qual o principal objetivo destes arquivos? De uma forma geral, um arquivo
se presta ao armazenamento de informaes atravs de fichas. Em informtica,
estes conceitos so perfeitamente vlidos, pois para o armazenamento de in-
formaes em um arquivo, temos que dar uma forma a arquivo, dar forma s
informaes que estaro contidas nele. Ento, como se fazem estas tarefas?
Veremos a seguir: 
  A primeira coisa que veremos  com relao ao contedo de um arquivo.
Em um arquivo fsico este contedo  feito normalmente atravs de fichas
que tm informaes de uma mesma forma. Para ns, esta ficha  um registro
e como j vimos anteriormente, o registro  uma estrutura que deve ser de-
finido, na rea de tipos, porm s a definio de um registro no implica 
na formao de um arquivo. Para fom-lo devemos ter um conjunto destas fi-
chas, no nosso caso, devemos declarar uma varivel do tipo arquivo e esta
varivel pode ser declarada de duas formas bsicas:
FILE - esta declarao define uma varivel como sendo arquivo. Sua sintaxe:      
identificador: FILE OF tipo;
                ou 
identificador: FILE;

  Vejamos um exemplo de definio de um tipo arquivo:
     .
     .
     .
TYPE
  registro = RECORD
    nome : STRING[30];
    cep : LONGINT;
    rg : STRING[8];
    cic : STRING[11];
       .
       .
       .
    END;
    arquivo_pessoal : FILE OF registro;
    arquivo_numrico : FILE OF BYTE;
    arquivo_sem_tipo : FILE;

  Porm, s a declaracao de uma varivel do tipo arquivo no quer dizer 
que j podemos manipular o arquivo, assim como no arquivo fsico, aquele de
ao, aqui teremos tarefas semelhantes, como por exemplo abrir uma gaveta,
retirar ou colocar uma ficha, fechar uma gaveta, identificar o arquivo atra-
vs de uma etiqueta qualquer, organizar as informaes, etc.
  Veremos a partir de agora, os comandos para fazermos tais tarefas: 

  O primeiro comando que veremos  o que nos permite associar a varivel do 
tipo arquivo ao nome externo deste arquivo, ou seja, o nome local que este
deve estar.

ASSIGN - este procedimento permite que associemos o nome externo de um arquivo
a uma varivel do tipo arquivo. O nome externo  aquele utilizado pelo sis-
tema operacional portanto deve ser vlido para o mesmo. So possveis todas
as formas de referncia usadas no path, e quando a unidade ou subdiretrios
forem omitidos, estes assumiro o default. Aps o uso do ASSIGN este con-
tinuar valendo at que seja dado um novo ASSIGN. O tamanho mximo do nome
do arquivo  de 79 bytes. Este procedimento nunca deve ser usado em um ar-
quivo j aberto, pois caso o nome do arquivo tenha tamanho 0, o arquivo ser
associado ao dispositivo padro de sada. Sua sintaxe:

ASSIGN (VAR <arq>, <nomearq> : STRING);
  arq, deve ser uma varivel do tipo arquivo. Veja o exemplo:
PROGRAM teste_assign;
CONST
  dv = B:;
  nome = TEST;
  ext = .DAT;
VAR
  arq1, arq2 : FILE OF BYTE;
BEGIN
  ASSIGN(arq2, dv + nome + ext);
  ASSIGN(ar1,  ); {o nome externo do arquivo}
                    {sendo omitido determina}                                          
                    {que o dispositivo de sada}
                    {ser o standard}
                .
                .
                .
END.

  Com a definio de uma varivel do tipo arquivo  e a sua associao a um
nome de arquivo externo, j temos um bom caminho andado, porm ainda nos 
faltam alguns comandos bsicos para, efetivamente, podermos manipular os
arquivos. Voltando  analogia com um arquivo de ao, veremos que a primeira
coisa que faremos para dar incio a manipulao deste arquivo  abr-lo. 
Pois bem, no nosso arquivo, tambm deveremos ter esta atividade, ou seja,
abrir o arquivo e para isto temos dois comandos bsicos, como vemos:

RESET - este procedimento permite-nos abrir um arquivo j existente. No caso
do uso deste, para a tentativa de abertura de um arquivo no existente, 
ocorrer um erro de execuo. Para que o procedimento tenha sucesso  neces-
srio que antes de execut-lo, tenhamos utilizado o procedimento ASSIGN.
Sua sintaxe:
RESET (VAR<arquivo>[:FILE;<tamanho>:WORD]);
  Vejamso um exemplo prtico:
PROGRAM teste_reset;
VAR
  arquivo : FILE OF BYTE;
  nomearq : STRING[67];
BEGIN
  WRITE (entre com o nome do arquivo qu,quer abrir);
  READLN (nomearq);
  ASSIGN (arquivo, nomearq);
  RESET (arquivo);
END.

  Este comando apenas permite-nos abrir um arquivo j existente. Para que
possamos abrir um arquivo novo, temos um outro comando: 
REWRITE - este comando permite criar e abrir um novo arquivo. Caso o arqui-
vo j exista, ter seu contedo eliminado e ser gerado um novo arquivo.
Antes de executarmos este procedimento, devemos usar o ASSIGN, e caso a va-
rivel do tipo arquivo no seja tipada, o tamanho de cada registro ser de 
128 bytes, isto se no for especificado o tamanho do registro. Sua sintaxe:
REWRITE (VAR<arquivo>[:FILE;<tamanho>:WORD]);
Vejamos um exemplo, usando o comando REWRITE:
PROGRAM teste_rewrite;
VAR
  arquivo : FILE OF BYTE;
  nomearq : STRING[67];
BEGIN
  WRITE (entre com o nome do arquivo que quer,abrir);
  READLN (nomearq);
  ASDSIGN (arquivo, nomearq);
  REWRITE (arquivo); 
END.

  Em ambos os exemplos anteriores, vemos uma deficincia, enquanto um co-
mando apenas nos permite a abertura de arquivos j existentes, o outro 
apenas abre arquivos novos ou destri um possvel contedo anterior. Como
poderemos ento, resolver esta situao? Bem, existe um mtodo relativamente
simples, que consiste no uso de uma diretiva de compilao para checagem
de erros de entrada e/ou sada, {$I}. Esta diretiva retorna um cdigo de 
erro em uma funo do Turbo chamada IORESULT(veja DIRETIVAS DE COMPILAO).
Vejamos um exemplo prtico da utilizao da diretiva $I:
PROGRAM teste_abrearq;
USES CRT;
CONST
  mens : ARRAY[0..15] OF STRING[20] = (exist, ,no exist,
                                      em Path invlid,
                                      em excesso abertos,
                                      com Nome invlid,
                                      com HANDLE invlid,
                                       , , , , ,
                                      com acesso invlid,
                                       , ,com drive invlid);
VAR
  arquivo : FILE OF BYTE;
  nomearq : STRING[67];
  erro : WORD;
FUNCTION existe_arq(n_arq : STRING) : WORD;
BEGIN
  ASSIGN (arquivo,n_arq);
  {$I}
  RESET (arquivo);
  {$I+]
  existe_arq := IORESULT; {se IORESULT = 0}
                          {arquivo existe}
END;
PROCEDURE abre_novo(n_arq : STRING);
BEGIN
  ASSIGN (arquivo,n_arq);
  {$I-]
  REWRITE (arquivo);
  {$I+]
  IF IORESULT = 0
    THEN
      BEGIN
        GOTOXY (10,18);
        WRITE (arquivo criado sem problemas);
      END;
END;
        {programa principal}
BEGIN
  CLRSCR;
  GOTOXY (10,10);
  WRITE (entre com o nome do arquiv);
  READLN(nomearq);
  GOTOXY (10,14);
  erro := existe_arq(nomearq);
  WRITE (Arquiv,mens[erro]);
  IF erro = 2
      THEN
        abre_novo(nomearq);
END.    

  A segunda tarefa que deve nos preocupar  o fato do arquivo permanecer
aberto, ou abrimos o mesmo arquivo mais de uma vez. Para termos um uso ade-
quado de um arquivo, devemos abr-lo e depois de utilizado fech-lo. Para
esta tarefatemos um comando apropriado:
CLOSE - este procedimento permite que se feche um arquivo anteriormente aberto,
s sendo permitido o fechamento de um arquivo por vez. Sua sintaxe:
CLOSE (VAR<arq>);
Exemplo:
PROGRAM teste_close;
VAR
  arquivo : FILE OF BYTE;
  nomearq : STRING[67];
BEGIN
  WRITE (entre com o nome do arquivo qu,quer abrir);
  READLN (nomearq);
  ASSIGN (arquivo,nomearq);
  REWRITE (arquivo);
  CLOSE (arquivo);
END.

  Porm para manipularmos um arquivo no basta apenas abr-lo e fech-lo,
temos, na maioria das vezes, que ler uma informao contida nele, outras 
vezes registrar informaes novas, ou ainda, fazer manuteno em informaes
j existentes. Vejamos ento, como so os comandos que nos permitem tais
tarefas:

WRITE - este procedimento alm de ser usado para exibir mensagens e vari
veis no dispositivo padro de sada (vdeo), pode ser usado para gravar 
informaes em outros dispositivos, como um arquivo. Sua sintaxe:
WRITE (<arq>,<reg1>[,<reg2>,...,<regn>]);
Vejamos um simples exemplo:
PROGRAM teste_write_arq;
CONST
  nomearq = TESTE.DAT;
  max = 10;
TYPE
  registro = RECORD
    nome : STRING[30];
    ender : STRING[35];
    END;
VAR
  arquivo : FILE OF registro;
  reg : registro;
  ind : BYTE;
BEGIN
  ASSIGN (arquivo,nomearq);
  REWRITE (arquivo);
  FOR ind := 1 TO max DO
    BEGIN
      WRITE(Digite ,ind,nom);
      READLN (reg.nome);
      WRITE (digite ,ind,Endere);
      READLN (reg.ender);
      WRITE (arquivo,reg);
    END;
  CLOSE (arquivo);
END.

READ
    Como j vimos anteriormente, este procedimento permite que atribuamos a
uma varivel, um valor obtido por um dispositivo associado. Este dispositivo
pode ser tambm um arquivo, e se no caso da leitura em arquivo, for
detectado o fim do mesmo, ser gerado um erro. Sua sintaxe:
            READ(<arq>,<reg>);
Vejamos um exemplo simples de uma leitura em arquivos:

PROGRAM teste_read_arqu;
CONST
  nomearq = TESTE.DAT;
  max  = 10;
TYPE
  registro  = RECORD
    nome  : STRING [30];
    ender :  STRING [25];
  END;
VAR
  arquivo : FILE OF registro;
  reg  : registro;
  ind  : BYTE;
BEGIN
  ASSIGN(arquivo, nomear) ;
  RESET (arquivo);
FOR ind := 1 TO MAX DO
  BEGIN
    READ(arquivo, reg);
    WRITElN (Este e ,ind, o NOm, reg.nome);
    WRITELN(Este  , ind,o Endere, reg.ender);
  END;
  CLOSE(arquivo);
END.

  No exemplo anterior, no temos problema algum ao executar a leitura
no arquivo, pois este havia sido gravado anteriormente por ns mesmos
e com uma quantidade de registros predefinidos. Porm, na maioria das
vezes, no temos idia da quantidade de registros contidos em um ar-/
quivo e para esta situao, devemos saber quando chegamos ao final de
um arquivo. O Turbo nos fornece tal funo:

EOF  -  esta funo nos retorna o valor TRUE quando for encontradas a
marca de fim de arquivo. Sua sintaxe:
EIF(VAR <arq>): BOOLEAN;
  Utilizando o exemplo anterior, com uma pequena variao:   

PROGRAM teste_eof;
CONST
   nomearq = TESTE.DAT;
TYPE
  registro  =  RECORD
    nome  :  STRING[30];
    ender :  STRING[25];
  END;
VAR
  arquivo   :  FILE OF registro;
  reg   :  registro;
  ind  :  BYTE;
BEGIN
  ASSIGN (arquivo, nomearq); 
  RESET(arquivo);
  ind  := 1;
  WHILE NOT EOF (arquivo) DO
    BEGIN
      READ (arquivo, reg);
  WRITELN(Este  , ind,o Nom, reg.nome);
  WRITELN(Este  , ind, o Endere, reg ender);
  inde  := ind + 1;
  END;
  CLOSE( arquivo);
END.
  
  O lao  repetido at que seja encontrada marca de fim de arquivo.
  Porm, para a manuteno de alguns registros de um determinado ar-
quivo, temos que saber qual a posio deste registro no arquivo e  -
tambm qual a posio do ltimo registro.
  Quando estamos manipulando um arquivo, existe a todo momento um -
ponteiro que fica deslocando-se de acordo com as leituras e grava-
es, ou seja, quando abrimos um arquivo, este ponteiro indica a 
posio zero, ou que  o primeiro registro fsico do arquivo. A cada
leitura ou gravao este ponteiro avana uma posio, mas existem  -
algumas funes e procedimentos que permitem a manipulao deste pon- 
teiro. Vejamos ento estas:

SEEK  -  Este procedimento permite que movamos o ponteiro do arquivo
para uma posio preestabelecida , podendo ser usado em arquivos de
qualquer tipo exceto os de tipos TEXTO. S pode sser usado em arqui-
vos previamente abertos. Sua sintaxe:
  SEEK (VAR<arq>;<posio>:LONGINT);

FILEPOS  -  esta funo nos retorna a posio atual do ponteiro do -
arquivo. Assim, que abrimos um arquivo, com RESET ou  REWRITE, a po-
sio do ponteiro  zero, ou seja, o primeiro registro, no pode ser
usado em c=arquivos do tipo TEXTO. Sua sintaxe:
FILEPOS(VAR<arq>):LONGINT;

FILESIZE - esta funo retorna o tamanho de um arquivo em numero de regis-
tros. Caso o arquivo esteja vazio, a funo retornar 0. No pode ser usada,
em arquivos do tipo TEXTO. Sua sintaxe:
FILESIZE (VAR<arq>) : LONGINT;
  Vejamos agora, um exemplo ejglobando estes ltimos comandos:
PROGRAM teste_ponteiro;
VAR
  nomearq : STRING[67];
  arquivo : FILE OF BYTE;
 tamanho : LONGINT;
BEGIN
  WRITE (entre com o nome do arquiv);
  READLN (nomearq);
  ASSIGN (arquivo, nomearq);
  RESET (arquivo);
  tamanho := FILESIZE (arquivo);
  WRITELN (o tamanho do arquiv,nomearq,em bytes, tamanho);
  WRITELN (posicionando no meio do arquivo...);
  SEEK (arquivo, tamanho DIV 2);
  WRITELN (a posio atual  , FILEPOS(arquivo));
  CLOSE (arquivo);
  READLN;
END.

WITH - este comando permite que a referncia a uma varivel do tipo regis-
tro seja simplificada. Podemos encadear at sete registros em um comando
WITH. A referncia aos campos dos registros pode ser efetuada com o uso 
deste comando sem a utilizao do ponto, ligando o nome do registro ao 
nome do campo. Sua sintaxe:
WITH <registro1>, [<registron>...] DO BLOCO;
exemplo:
PROGRAM teste_with;
TYPE
  data = RECORD
    dia, mes, ano : WORD;
      END;
  registro = RECORD
    campo1 : BYTE;
    campo2 : STRING;
    campo3 : BOOLEAN;
    campo4 : data;
  END;
VAR
  reg : registro;
  d : data;
BEGIN  
  WITH d DO
  BEGIN
    dia := 18;
    mes := 11;
    ano := 1989;
  END;
  WITH reg,campo4 DO
    BEGIN
      campo1 := 3;
      campo2 := teste de with;
      campo3 := TRUE;
      dia := 1;
      mes := 1;
      ano := 1980;
    END;
END.
  Vejamos agora, um exemplo onde a manipulao de um arquivo  completa:
PROGRAM teste_arquivo;
USES CRT;
CONST 
  nomearq = alunos.dat;
TYPE
  registro = RECORD
    aluno : STRING[30];
    matria : ARRAY[1..4,1..10] of byte;
    classe : STRING[2] ;
  END;
VAR
  arq : FILE OF registro;
  reg : REGISTRO;
  fim : BOOLEAN;
  (*rotina de abertura de arquivos, se arquivo no *)
  (*existe cria um novo deixando-o aberto    *)
PROCEDURE abre_arq (n_arq : STRING);
BEGIN
  ASSIGN (arq,n_arq);
  {$I-}
  RESET (arq);
  {$i+}
  IF IORESULT <> 0
    THEN
      REWRITE (arq);
END;
  (*rotina de incio do programa, nesta fazemos a*)
  (*inicializao das variveis do programa   *)
PROCEDURE incio;
BEGIN
  CLRSCR;
  fim := FALSE;
  abre_arq (nomearq);
END;
  (* funo que retorna apenas S ou N *)
FUNCTION s_n : CHAR;  
VAR
  ch : CHAR;
BEGIN  
  REPEAT
    ch := UPCASE (READKEY);
  UNTIL ch IN [S,N];
  s_n := ch;
END;
  (*rotina de exibio das matrias *);
PROCEDURE matrias(bimestre : BYTE);
CONST
  m : ARRAY[1..10] OF STRING[10] = (Matemtic,
                                     Portugus,
                                     Fsic,
                                     Qumic,
                                     Biologi,
                                     Ingles,
                                     Geografi,
                                     Histri,
                                     Ed Fsic,
                                     O.S.P.B);
VAR
  ind : BYTE;
  aux : STRING[3];
  erro : INTEGER;
  N : BYTE;
BEGIN
  FOR ind := 1 TO 10 DO
    BEGIN
      GOTOXY (5,11 + ind);
      WRITE (m[ind]);
    END;
  IF reg.matria [bimestre,1] <= 10
    THEN
      FOR ind := 1 TO 10 DO
        BEGIN
          GOTOXY (18,11 + ind);
          WRITE (reg.matria[bimestre,ind]);
        END;
      FOR ind := 1 TO 10 DO 
        BEGIN
    
