MC102
Algoritmos e Programação de Computadores
Parte XXIV



Norton Trevisan Roman




ARQUIVOS DE ACESSO SEQÜENCIAL

Até agora, todos os dados lidos por nossos programas vinham do teclado, e todas as saídas íam para a tela.

Mas, e se quiséssemos guardar alguns resultados para reaproveitar mais tarde? Como faríamos? Simples, basta guardarmso esses dados em arquivos no computador.

Quando digitamos um texto, para guardá-lo, não salvamos em um arquivo? E quando queremos usar esse texto novamente não lemos o que salvamos anteriormente no arquivo? O princípio é o mesmo. Então, como usamos arquivos em Pascal?

Antes de mais nada, devemos declarar uma variável de arquivo. Como fazemos isso?



Atenção!!! Os programas escritos até a metade desta lição não devem ser executados. A razão é que eles estão incompletos. Mais adiante veremos o que falta neles.



  VAR nome_da_variável : FILE OF tipo_do_dado;
	

Assim, para definirmos uma variável de arquivo, temos que saber o tipo de dado que colocaremos lá. Então, suponha que nosso arquivo é texte, então teremos:

  VAR arq : FILE OF char;
	

Mas, afinal, para que precisamos de uma variável de arquivo? Mais adiante veremos que é através dela que teremos acesso a nosso arquivo.

Esse tipo de arquivo texto já é pré-definido no pascal, ou seja, o Pascal tem um tipo pré-definido para arquivos texto: o text. Então, as duas declarações abaixo são equivalentes

  VAR arq : FILE OF char;

  VAR arq : text;
	

pois o pascal já fez essa declaração para você:

  TYPE text = FILE OF char;
	

Naturalmente, se você quiser arquivos de algum outro tipo que não char, você deve fazer, por exemplo:

  VAR arq1 : FILE OF integer;
      arq2 : FILE OF real;

  etc
	

só que, nesse caso, os arquivos são tratados de modo um pouco diferente pelo Pascal. Confira a lição de arquivos de acesso aleatório para ver como o Pascal trata esses arquivos.

Nesse momento, trataremos somente de arquivos texto.

Bom, agora já sabemos como declarar uma variável de arquivo. Agora, como a usamos?

Antes de querer lidar com o arquivo, precisaremos associar essa variável a um arquivo real no disco. Ou seja, precisamos associá-la ao nome de um arquivo que queiramos criar ou ao nome de um já existente que queiramos abrir. Como fazemos isso?

  Assign(variável_de_arquivo, nome_do_arquivo);
	

Por exemplo, o programa:

  VAR arq : text;

  BEGIN
    Assign(arq,'oba.txt');
  END.
	

associa à variável "arq" o arquivo de nome "oba.txt". Lembre-se que tal arquivo não necessariamente precisa existir no disco.

Após associar o nome ao arquivo, qual o próximo passo? Aí depende: queremos criar um novo arquivo ou abrir um existente?

Suponha que queremos criar um arquivo novo:

  VAR arq : text;

  BEGIN
    Assign(arq,'oba.txt');

    Rewrite(arq);
  END.
	

Pronto! O comando Rewrite cria um novo arquivo e o abre para gravação (escrita). E se o arquivo "oba.txt" já existir no disco? Nesse caso o Rewrite o apaga, escrevendo o novo conteúdo por cima dele. Ou seja, cuidado! Pois se o arquivo a ser criado já existir no disco, tudo que havia nele antes do Rewrite será perdido. Assim, a forma geral do Rewrite é:

  Rewrite(variável_de_arquivo);
	

Agora que criamos o arquivo, como fazemos para guardar algo nele? Como fazemos para escrever nele? De um modo bem semelhante a como escrevíamos na tela:

  VAR arq : text;

  BEGIN
    Assign(arq,'oba.txt');

    Rewrite(arq);

    write(arq, 'mensagem');
    writeln(arq, 'para');
    write(arq,'o arquivo')
  END.
	

Isso irá gravar

  mensagempara
  o arquivo
	

no arquivo. Ou seja, do mesmo modo que o write e o writeln agem na tela, agem no arquivo.

O write e o writeln também podem ser usados para escrever outros tipos de dados. Eles convertem automanticamente os valores de dados numéricos para strings de dígitos antes de armazenar no arquivo texto. Assim, posso escrever:

  VAR arq   : text;
      idade : integer;
      str   : string;

  BEGIN
    Assign(arq,'oba.txt');

    Rewrite(arq);

    idade := 25;
    writeln(arq,'João',idade);

    str := 'Pedro Souza';
    idade := 12;
    writeln(arq,str,idade)
  END.
	

E se, em vez de cirarmos um arquivo, quisermos escrever em um já existente, sem apagar o conteúdo prévio deste? Neste caso, em vez de Rewrite usamos Append:

  VAR arq   : text;
      idade : integer;
      str   : string;

  BEGIN
    Assign(arq,'oba.txt');

    Append(arq);

    idade := 25;
    writeln(arq,'Jnova linha')
  END.
	

Nesse exemplo, se o arquivo continha:

  linha 1
  escrevo linha 2
	

agora terá:

  linha 1
  escrevo linha 2
  nova linha
	

se "escrevo linha 2" foi gravada com writeln, ou:

  linha 1
  escrevo linha 2nova linha
	

se "escrevo linha 2" foi gravada com write.

Assim, Append abre um arquivo já existente, de modo que possamos adicionar texto ao FINAL deste. Note que só adiciono ao fim do arquivo.

A forma geral do comando é:

  Append(variável_de_arquivo);
	

Como o Append assume que o arquivo existe, se este não existir, um erro em tempo de execução será gerado.

Agora, digamos que queremos apenas ler o conteúdo de um arquivo que está no disco, como fazemos? Com Reset:

  Reset(variável_de_arquivo);
	

Por exemplo, o seguinte programa

  VAR arq : text;
      s   : string[5];
      s1  : string;

  BEGIN
    Assign(arq,'oba.txt');

    Reset(arq);

    read(arq,s);
    readln(arq,s1);
  END.
	

lê 2 strings de "oba.txt". O read lê até o tamanho máximo de s (5). Já o readln lê uma linha inteira do arquivo, armazenando-a em s1 (lê a linha ou até 255 caracteres, que é o máximo de string). Assim, se o arquivo contiver

  este texto é grande
  eu o escrevi
	

s terá "este " (repare que o espaço foi junto) e s1 terá o resto da linha, ou seja, "texto é grande".

Então, Reset abre um arquivo existente somente para leitura.

da mesma forma que o write, o read pode transformar o texto lido em valores numéricos. Por exemplo, lembra nosso arquivo anterior onde gravamos "João 25"? Como lemos isso?

  VAR arq : text;
      s   : string;
      id  : integer;

  BEGIN
    Assign(arq,'oba.txt');

    Reset(arq);

    readln(arq,s,id);
  END.
	

Aqui, s terá "João" e id terá 25.

assim, tanto real quanto integer podem ser lidos, lembrando que, em um arquivo texto, quando lemos várias variáveis, como no exemplo acima, os caracteres delimitadores são os espaços, tabulação e CR (enter).

Se tentarmos ler uma seqüência não numérica com read e guardarmos em uma variável numérica, um erro ocorre (da mesma forma como quando recebemos do teclado).

Assim, é melhor escrever informações diferentes em linhas diferentes, ou seja, se estou guardando nomes e idades, gravo numa linha o nome e na outra a idade. É bem verdade que precisarei de dois writeln para gravar e dois readln para ler, mas fica mais organizado.

Suponha, então, que temos um arquivo chamado "idades.txt" com nomes e diades:

  Rolando Caio da Rocha
  23
  Jacinto Dores
  12
  Armando Machado
  34
	

Como fazemos para imprimir na tela o conteúdo desse arquivo?

  VAR arq : text;
      s   : string;
      id  : integer;

  BEGIN
    Assign(arq,'oba.txt');

    Reset(arq);

    WHILE NOT eof(arq) DO
    BEGIN
      readln(arq,s);
      readln(arq,id);
      writeln(s,' - ',id)
    END
  END.
	

O que eof faz? Sua sintaxe é:

  Eof(variável_de_arquivo) : boolean;
	

e ele retorna True se o arquivo chegou ao fim (EOF = End Of File). É útil quando não sabemos quantas linhas tem o arquivo.

Por fim, já que com Reset, Rewrite e Append abrimos um arquivo, sempre devemos fechá-lo. Como?

  Close(variável_de_arquivo);
	

Então, nosso programa acima fica:

  VAR arq : text;
      s   : string;
      id  : integer;

  BEGIN
    Assign(arq,'oba.txt');

    Reset(arq);

    WHILE NOT eof(arq) DO
    BEGIN
      readln(arq,s);
      readln(arq,id);
      writeln(s,' - ',id)
    END;

    Close(arq)
  END.
	

Sempre feche seus arquivos assim que terminar de usá-los.

Vale notar que, apesar de fechar o arquivo, close não termina com a associação entre a variável de arquivo e o nome deste. Ou seja, podemos jsar Reset, Rewrite e Append novamente para tratar com o mesmo arquivo sem precisarmos usar Assign.

Assim, valem as seguintes observações:

Então, em suma, para ler ou escrever em um arquivo texto temos que: Como um arquivo texto só pode ser aberto para inclusão em seu final, como podemos fazer para modificar um dado no meio deste? E apagar algum dado do meio?

Considere um sistema onde guardamos o nome da pessoa e sua data de nascimento. Então, vamos criar as estruturas para tal:

  TYPE data   = RECORD
                  dia : word;
            	  mes : word;
	  	  ano : word
                END;
       pessoa = RECORD
                  nome : string[25];
		  nasc : data
		END;
	

Agora, vamos criar o arquivo e abaster os dados:

  PROGRAM PROG;

  CONST NOMEARQ = 'niver.txt';

  TYPE data   = RECORD
                  dia : word;
            	  mes : word;
	  	  ano : word
                END;
       pessoa = RECORD
                  nome : string[25];
		  nasc : data
		END;

  VAR sai : boolean;
      arq : text;
      p   : pessoa;

  BEGIN
    sai := false;

    Assign(arq,NOMEARQ);
    Rewrite(arq);

    WHILE NOT sai DO
    BEGIN
      write('nome: ');
      readln(p.nome);

      IF p.nome='sai' THEN sai:=true
      ELSE BEGIN
        write('nascimento(dd mm aa):');
	readln(p.nasc.dia,p.nasc.mes,p.nasc.ano);
	writeln(arq,p.nome);
	writeln(arq,p.nasc.dia:2,' ',p.nasc.mes:2,' ',p.nasc.ano:2)
	{o :2 é para escrever com 2 dígitos}
      END
    END;

    Close(arq)
  END.
	

Esse programa fica executando até que o usuário digite 'sai'. Note que, se o arquivo existir, o que havia nele será apagado. Se você não quiser que isso ocorra use Append em ves do Rewrite.

Então, nosso arquivo será:

  Rolando Caio da Rocha
  12 05 98
  Jacinto Dores
  01 01 87
  Armando Machado
  15 06 76
  Paula Aguiar Cavalo
  23 02 88
	

Agora suponha que queremos apagar do arquivo o Armando Machado, como fazemos? Parece estranho, mas como esse tipo de arquivo é seqüencial, ou seja, não consigo modificar o meio dele, devemos usar o seguinte algoritmo:

  Abro "niver.txt" para leitura
  Crio "temp.txt"
  Enquanto "niver.txt" não chegar ao fim
    Leio nome e data do "niver.txt"
    Se nome ≠ "Armando Machado" gravo nome em "temp.txt"
    Senão ignoro e não copio nada
	

Após rodarmos o algoritmo no arquivo da esquerda, teremos o da direita:

       NIVER.TXT

  Rolando Caio da Rocha
  12 05 98
  Jacinto Dores
  01 01 87
  Armando Machado
  15 06 76
  Paula Aguiar Cavalo
  23 02 88
	
       TEMP.TXT

  Rolando Caio da Rocha
  12 05 98
  Jacinto Dores
  01 01 87
  Paula Aguiar Cavalo
  23 02 88
	

Note que "temp.txt" é o "niver.txt" que queríamos. Então completo meu algoritmo:

  Abro "niver.txt" para leitura
  Crio "temp.txt"
  Enquanto "niver.txt" não chegar ao fim
    Leio nome e data do "niver.txt"
    Se nome ≠ "Armando Machado" gravo nome em "temp.txt"
    Senão ignoro e não copio nada

  Apago "niver.txt"
  Troco o nome de "temp.txt" para "niver.txt"
	

Agora podemos passar ao programa que faz isso:

  PROGRAM PROG;

  CONST NOMEARQ = 'niver.txt';
        NOMEAUX = 'temp.txt';

  TYPE data   = RECORD
                  dia : word;
            	  mes : word;
	  	  ano : word
                END;
       pessoa = RECORD
                  nome : string[25];
		  nasc : data
		END;

  VAR arq1 : text;
      arq2 : text;
      p    : pessoa;
      proc : string[25];

  BEGIN
    sai := false;
    proc := 'Armando Machado';

    {abro o arquivo de dados para leitura}
    Assign(arq1,NOMEARQ);
    Reset(arq1);

    {crio o arquivo temporário}
    Assign(arq2,NOMEAUX);
    Rewrite(arq2);

    WHILE NOT Eof(arq1) DO
    BEGIN
      readln(arq1,p.nome);
      readln(arq1,p.nasc.dia,p.nasc.mes,p.nasc.ano);

      IF p.nome<>proc THEN
      BEGIN
        writeln(arq2,p.nome);
	writeln(arq2,p.nasc.dia:2,' ',p.nasc.mes:2,' ',p.nasc.ano:2)
      END
    END;

    {fecho os arquivos}
    Close(arq1);
    Close(arq2);

    {apago niver.txt}
    Erase(arq1);

    {renomeio temp.txt para niver.txt}
    Rename(arq2,'niver.txt')
  END.
	

Viu como apagamos e renomeamos um arquivo?

  Erase(variável_de_arquivo); {apaga o arquivo do disco}

  Rename(variável_de_arquivo,'novo_nome'); {renomeia o arquivo, dando 'novo_nome' a ele}
	

Agora suponha que, nesse arquivo de onde "Armando Machado" foi removido, queremos mudar a data de nascimento de "Jacinto Dores" para 01/02/87, como fazemos? O algoritmo é muito semelhante:

  Abro "niver.txt" para leitura
  Crio "temp.txt"
  Enquanto "niver.txt" não chegar ao fim
    Leio nome e data do "niver.txt"
    Se nome ≠ "Jacinto Dores" gravo nome em "temp.txt"
    Senão gravo os dados modificados

  Apago "niver.txt"
  Troco o nome de "temp.txt" para "niver.txt"
	

ou seja:

  PROGRAM PROG;

  CONST NOMEARQ = 'niver.txt';
        NOMEAUX = 'temp.txt';

  TYPE data   = RECORD
                  dia : word;
            	  mes : word;
	  	  ano : word
                END;
       pessoa = RECORD
                  nome : string[25];
		  nasc : data
		END;

  VAR arq1 : text;
      arq2 : text;
      p    : pessoa;
      proc : string[25];
      nova : data;

  BEGIN
    sai := false;
    proc := 'Jacinto Dores';

    nova.dia := 1;
    nova.mes := 2;
    nova.ano := 87;

    {abro o arquivo de dados para leitura}
    Assign(arq1,NOMEARQ);
    Reset(arq1);

    {crio o arquivo temporário}
    Assign(arq2,NOMEAUX);
    Rewrite(arq2);

    WHILE NOT Eof(arq1) DO
    BEGIN
      readln(arq1,p.nome);
      readln(arq1,p.nasc.dia,p.nasc.mes,p.nasc.ano);

      writeln(arq2,p.nome);
      IF p.nome<>proc THEN
	writeln(arq2,p.nasc.dia:2,' ',p.nasc.mes:2,' ',p.nasc.ano:2)
      ELSE {substituo a data}
	writeln(arq2,nova.dia:2,' ',nova.mes:2,' ',nova.ano:2)
      END
    END;

    {fecho os arquivos}
    Close(arq1);
    Close(arq2);

    {apago niver.txt}
    Erase(arq1);

    {renomeio temp.txt para niver.txt}
    Rename(arq2,'niver.txt')
  END.
	

Pronto, modificamos.





‹— Parte XXIIIPágina da disciplinaParte XXV —›