ARQUIVOS


  A memória do computador é volátil, e todos os dados contidos nela é perdido após o desligamento do computador.
  Se desejarmos manter os dados de uma aplicação vivos após o desligamento, como por exemplo, o resultado de um jogo ou os nomes de uma agenda telefônica, teremos que salvá-los em disco. 
  Existem dois formatos de arquivos: o formato texto e o formato binário.
  Em arquivos no formato texto, os códigos armazenados serão interpretados como valor de entrada na tabela ASCII, de maneira a formar um texto. Assim, por exemplo, o código 65 significa a letra 'A' maiúscula. Além disso, os arquivos no formato texto são divididos em linhas, onde um caracter com um código determinado indica o fim de uma linha.
  Em arquivos binários, os códigos são a representação direta dos dados, em seu formato original. Por exemplo, um inteiro será armazenado em disco como é armazenado na memória, ou seja, com 2 bytes por número. Já o double, em seu formato de 8 bits.
  Fica evidente que o programador deverá conhecer a disposição dos dados e seus respectivos tipos na hora de recuperar a informação no disco. Caso contrário, poderemos recuperar dados de um número double em um variável do tipo inteiro, por exemplo, causando inconsistência nos dados.

  Veremos a seguir, todas as funções de Pascal, relativas à arquivos.

Identificador Tipo Descrição
Append procedure Abre um arquivo texto já existente, posicionando o ponteiro de arquivo no final do arquivo.
Assign procedure Associa uma variável tipo arquivo com arquivo em disco.
BlockRead procedure Transferência de um arquivo sem tipo do disco para a memória.
BlockWrite procedure Transferência de um arquivo sem tipo da memória para o disco.
Chain procedure Carrega e executa um arquivo .CHN.
Close procedure Grava informações do buffer e fecha um arquivo externo.
Eof function TRUE se o ponteiro do arquivo estiver no fim do arquivo.
EoLn function TRUE se o ponteiro de arquivo texto estiver no fim da linha. 
Erase procedure Remove o arquivo do diretório do disco.
Execute procedure Carrega e executa um arquivo tipo .COM.
FilePos function Retorna a posição do ponteiro no arquivo não texto.
FileSize function Retorna o número de componentes de um arquivo não texto.
Flush procedure Força a saída dos dados contidos no buffer para o disco, sem fechar o arquivo.
Get procedure Entrada de dados do Pascal padrão - não usada no Turbo.
LongFilePos function Retorna a posição do ponteiro de um arquivo maior que 32 K - não é válido para arquivos texto.
LongFileSize function Retorna o número de componentes de um arquivo não- texto, maior que 32 K.
LongSeek procedure Posiciona o ponteiro de arquivo num dado registro, em arquivos maiores que 32 K - não é válido para arquivos texto.
Put procedure Saída de dados do Pascal padrão - não usada no Turbo.
Read procedure Entrada de dados do teclado ou de um arquivo externo de disco.
ReadLn procedure Entrada de dados de um dispositivo ou arquivo texto.
Rename procedure Muda o nome do arquivo em disco.
Reset procedure Abre o arquivo já existente em disco somente para leitura.
Rewrite procedure Cria um novo arquivo no disco e abre somente para gravação.
Seek procedure Posiciona ponteiro do arquivo no componente de número var - não é válido para arquivos texto.
SeekEof procedure Pula tabulação, espaço em branco e marcas de fim de linha (CR/LF) antes do teste de fim de arquivo (Eof). Retorna valor booleano.
SeekEoln procedure Pula tabulação e espaços em branco antes do teste de fim de linha (EoLn) e/ou fim de arquivo. Retorna valor booleano.
Truncate procedure Trunca um arquivo no registro indicado pelo ponteiro de um arquivo.
Write procedure Saída de dados para o dispositivo ou arquivo em disco.
WriteLn procedure Saída de dados para um dispositivo ou arquivo de texto com uma seqüência de caracteres CR e LF após a última variável.
(Tabela retirada do livro Turbo Pascal - Guia do Usuário, STEVE WOOD, ed Mc Graw-Hill, pgs 59, 60 e 61).




  Sintaxe básica

  A sintaxe básica para ambos os formatos de arquivo é:
var t_var : file of tipo_de_dado / text;

begin
  assign (t_var, nome_do_arquivo);   { Associa varável a arquivo }
  rewrite/reset/append (t_var);      { Tipo de operação em disco }

  read/write (t_var, var);           { Lê ou grava dados da variável }

  close (t_var);                     { Fecha arquivo aberto }
end.
  O primeiro passo é criar uma variável do tipo arquivo "t_var", que definirá o tipo de dado que será utilizado na leitura e gravação de dados. Em seguida, associamos a variável t_var a um arquivo físico no disco (designado pelo nome). A partir daí, todos os comandos de acesso ao disco que mencionam a variável t_var estarão acessando ao arquivo associado.



  Cursor de arquivo

  Imaginando o arquivo em disco como um grande vetor de dados, o cursor é um índice que irá apontar para uma determinada posição desse vetor.
  Para exemplificar, seja um arquivo no formato binário, composto de dados em valores inteiros (2 bytes ou 16 bits):
    0 |     1 |     2 |     3 |     4 |     5  - Índice do vetor.
------+-------+-------+-------+-------+-------
00 00 | 01 00 | 02 00 | 0A 00 | 0B 00 | 0C 00  - Dados em hexadecimal (arquivo).

  Ao comando de reset(), o cursor irá apontar para o início do arquivo. Assim, cursor := 0 e sua posição está assinalada em vermelho no diagrama abaixo.
    0 |     1 |     2 |     3 |     4 |     5  - Índice do vetor.
------+-------+-------+-------+-------+-------
00 00 | 01 00 | 02 00 | 0A 00 | 0B 00 | 0C 00  - Dados em hexadecimal (arquivo).
  Quando uma leitura no arquivo é feita através do comando read(), o conteúdo apontado pelo cursor (assinalado em azul) é copiado para a variável i. Nessa leitura, i recebe o valor igual 0.

  O código básico de leitura de dados no formato inteiro (integer) é esse:
var t_var : file of integer;
    i : integer;
...
read(t_var, i);
...

  Imediatamente após a leitura em disco, o cursor sobe uma posição. Assim, cursor := 1.
    0 |     1 |     2 |     3 |     4 |     5  - Índice do vetor.
------+-------+-------+-------+-------+-------
00 00 | 01 00 | 02 00 | 0A 00 | 0B 00 | 0C 00  - Dados em hexadecimal (arquivo).
  Na próxima leitura (read), i receberá o valor 1. O cursor sobe novamente uma posição (sempre quando o read/write é acionado).
    0 |     1 |     2 |     3 |     4 |     5  - Índice do vetor.
------+-------+-------+-------+-------+-------
00 00 | 01 00 | 02 00 | 0A 00 | 0B 00 | 0C 00  - Dados em hexadecimal (arquivo).

  É importante lembrar que a cada leitura, o conteúdo da variável i é modificado e a informação antiga é perdida. Assim, você deve imprimir o valor na tela ou armazená-los em algum lugar.
  Quando o cursor atinge a posição 6 desse exemplo, chega ao fim do arquivo. Ao tentar ler essa posição, o programa irá gerar um erro. Dessa forma, o comando EOF verifica se o cursor atingiu o fim do arquivo, antes de fazer a leitura de dados.

  A seguir, serão apresentados os códigos completos para a geração do arquivo binário utilizado, bem como a recuperação dos dados desse arquivo.

Código para gravar dados em um arquivo Código para ler dados de um arquivo
var t_var : file of integer;
    i : integer;
    v : array[0..5] of integer = ( $0, $1, $2, $A, $B, $C );

begin
  assign (t_var, 'dados.dat');
  rewrite (t_var);

  for i:= 0 to 5 do
  begin
    write(t_var, v[i]);
  end;

  close (t_var);
end.
var t_var : file of integer;
    x, i : integer;

begin
  assign (t_var, 'dados.dat');
  reset (t_var);

  for x:= 0 to 5 do
  begin
    read(t_var, i);
    writeln(i);
  end;

  close (t_var);
end.

Saída para o arquivo de leitura:
0
1
2
10
11
12

  O comando SEEK

  É possível posicionar diretamente o cursor para ler uma posição desejada do arquivo, em vez de percorrer todo o arquivo. O comando "seek" faz isso. Basta indicar o valor da posição do cursor, que varia de 0 a N-1.
  O programa a seguir mostra como ler diretamente a posição 4 do arquivo utilizado até aqui como exemplo.
var t_var : file of integer;
    i : integer;

begin
  assign (t_var, 'dados.dat');
  reset (t_var);
  seek (t_var, 4);
  read(t_var, i);
  writeln('Valor da posicao 4: ', i);
  close (t_var);
end.
  Saída:
Valor da posicao 4: 11

  Obs: O comando reset é necessário uma vez, para sinalizar que o arquivo será utilizado como leitura. Novos comandos seek podem ser acrescentados, seguidos do comando read para a leitura de outras posições do arquivo.



  Arquivos no formato texto

  Há duas maneiras de ler ou gravar arquivos de texto:
  1. Caractere a caractere
  2. Linha a linha
  A operação de acesso a disco é extremamente demorada, se comparada à velocidade de acesso à memória principal do computador. Assim, quanto menos operações de entrada e saída fizermos para uma mesma quantidade de dados, melhor. Por esse ângulo, percebemos que é muito mais vantajoso levar/trazer um bloco inteiro de dados para a o disco, do que de caractere a caractere. A opção 2 é a melhor.

  No primeiro exemplo, vamos escrever um texto linha a linha em um arquivo. Utilizamos a sintaxe básica, personalizando-a
var arq_texto : text;
    linha : string[255];

begin
  assign(arq_texto, 'texto.txt');
  rewrite(arq_texto);

  linha := 'O MSX foi trazido para o Brasil em 1985.';
  writeln(arq_texto, linha);

  linha := 'Foi fabricado pela Gradiente e Sharp.';
  writeln(arq_texto, linha);

  close(arq_texto);
end.
  Agora, vamos abrir o texto:
var arq_texto : text;
    linha : string[255];

begin
  assign(arq_texto, 'texto.txt');
  reset(arq_texto);

  while (not eof(arq_texto)) do
  begin
    readln(arq_texto, linha);
    writeln(linha);
  end;

  close(arq_texto);
end.
Saída:
O MSX foi trazido para o Brasil em 1985.
Foi fabricado pela Gradiente e Sharp.

  No exemplo a seguir, o programa escreve caractere a caractere em disco e depois abre o arquivo para leitura:
program Ilustra_Tipo_Text;

const RETURN = #13; 	{retorno do carro de impressao; retorno do cursor}
      LINE_FEED = #10;	{avanco do papel; mudanca de linha}

var carac	: Char;
    arq_texto	: text;
    str_leitura : String[255];

begin
  ClrScr;

  { Gravação }
  WriteLn('Digite um texto. Pressione  para finalizar.');
  WriteLn;
  Assign(arq_texto,'TEXTO.DAT');
  Rewrite(arq_texto);
  repeat
    read(Kbd,carac);
    if (carac <> ^Z) then
      Write(carac);
    if (carac = RETURN) then	{ Apague este comando se seu sistema pular }
      Write(LINE_FEED);		{ duas linhas ao ecoar o texto na tela. }
    Write(arq_texto,carac);
  until(carac = ^Z);
  Close(arq_texto);

  { Leitura }
  WriteLn; WriteLn;
  Write('Pressione qualquer tecla para rever o texto. ==> ');
  Read(Kbd,carac); WriteLn;
  Reset(arq_texto);
  WriteLn;
  While (not Eof(arq_texto)) do
  begin
    ReadLn(arq_texto,str_leitura);
    WriteLn(str_leitura);
  end;
  Close(arq_texto);
end.
(Retirado do livro Turbo Pascal - Guia do Usuário, STEVE WOOD, ed Mc Graw-Hill, pg 62 e 63)



  Arquivos no formato binário

  Há quatro maneiras de ler ou gravar arquivos de texto:
  1. Byte a Byte
  2. Dado a dado (definido pelo tipo de dado)
  3. Bloco de bytes (buffer)
  4. Bloco de dados
  Conforme dito na seção de arquivos de texto, a operação de acesso a disco é extremamente demorada. Assim, optamos sempre por ler um bloco de informações.

  No primeiro exemplo, veremos um programa que utilizará um bloco de bytes (opção 3) para ler arquivos binários e trazer seus dados à tela.
type buf = array[1..1023] of byte;

var arq_buff : file of buf;
    buffer : buf;
    x : integer;

begin
  Assign(arq_buff,'agenda.dat');
  Reset(arq_buff);
  while (not Eof(arq_buff)) do
  begin
    writeln;
    Read(arq_buff, buffer);  { Lê dados do disco no vetor }
    for x:=0 to 1023 do
      write(buffer[x],' ');  { Imprime dados na tela }
  end;
  close(arq_buff);
end.
  Obs: A diferença entre o arquivo de texto e o arquivo binário está no tipo de variável associada (assign) com o arquivo. Aqui, a sintaxe é:
var nome : file of tipo_de_dado;
  Quando o programa lê dados do disco, armazena exatamente 1024 bytes no vetor do tipo "buf". O cursor do arquivo (posição na área de dados) inicialmente está na posição 0. Após ler 1024 bytes, irá para a posição 1024 no arquivo. A leitura de dados é sequencial e limitada ao tamanho do buffer. Portanto, se o arquivo tiver tamanho igual a 2000 bytes, terá que fazer 2 leituras no disco com copia no buffer para ler todo o arquivo, no exemplo acima.

  No segundo exemplo, será utilizada a leitura/gravação de dado a dado (opção 2). Para isso, vamos utilizar um tipo de dado composto no programa que irá gravar informações em disco.
type
  ficha = record
      nome : string[40];
      idade : integer;
      peso : real;
    end;

var ficha_arq : file of ficha;
    ficha_1 : ficha;

begin
  assign(ficha_arq, 'ficha.dat');
  rewrite(ficha_arq);

  ficha_1.nome := 'Kleber';
  ficha_1.idade := 20;
  ficha_1.peso := 65.0;

  write(ficha_arq, ficha_1);
  close(ficha_arq);
end. 
  Se abrirmos o arquivo 'ficha.dat' em um editor hexadecimal, temos:
End. | Dados em Hexadecimal                             | ASCII
-----+--------------------------------------------------+--------------------
0000 | 06 4B 6C 65 62 65 72 00  00 00 00 00 00 00 00 00 | .Kleber.........
0010 | 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 | ................
0020 | 00 00 00 00 00 00 00 00  00 00 14 00 00 00 00 00 | ................
0030 | 00 00 00 00 00 40 50 40                          | .............@P@
  Separando os dados, temos:
End. | Dados em Hexadecimal                             | ASCII
-----+--------------------------------------------------+--------------------
0000 | 06 4B 6C 65 62 65 72 00  00 00 00 00 00 00 00 00 | .Kleber.........
0010 | 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 | ................
0020 | 00 00 00 00 00 00 00 00  00 00 14 00 00 00 00 00 | ................
0030 | 00 00 00 00 00 40 50 40                          | .............@P@
  A string de tamanho igual a 40 é assinalada em verde (o primeiro byte é o comprimento da sring armazenada).
  A variável inteira (tem dois bytes) está assinalada em azul. Note que o valor decimal 20 em hexadecimal vale 14.
  A variável real (tem oito bytes - no Free Pascal para PC, onde foi testado) está assinalada em vermelho.

  O programa a seguir recupera os dados do arquivo "ficha.dat" e os imprime na tela.
type
  ficha = record
      nome : string[40];
      idade : integer;
      peso : real;
    end;

var ficha_arq : file of ficha;
    ficha_1 : ficha;

begin
  assign(ficha_arq, 'ficha.dat');
  reset(ficha_arq);

  read(ficha_arq, ficha_1);

  writeln('Nome: ', ficha_1.nome);
  writeln('Idade: ', ficha_1.idade);
  writeln('Peso: ', ficha_1.peso);

  close(ficha_arq);
end. 
Saída:
Nome: Kleber
Idade: 20
Peso: 65.0

  Agora, será apresentado um programa que lê três fichas em um vetor e depois salva em disco.
type
  ficha = record
      nome : string[40];
      idade : integer;
      peso : real;
    end;

var ficha_arq : file of ficha;
    fichario : array[1..3] of ficha;
    i : integer;

procedure preenche_ficha(pos : integer);
begin
  writeln('Ficha ', pos);
  write('Nome: ');
  readln(fichario[pos].nome);    { No MSX utilizar read(kbd, fichario[pos].nome) }
  write('Idade: ');
  readln(fichario[pos].idade);
  write('Peso: ');
  readln(fichario[pos].peso);
end;

procedure salva_arquivo;
var i : integer;
begin
  assign(ficha_arq, 'ficha_2.dat');
  rewrite(ficha_arq);

  for i:=1 to 3 do
  begin
    write(ficha_arq, fichario[i]);
  end;

  close(ficha_arq);
end;

begin

  for i:=1 to 3 do
  begin
    preenche_ficha(i);
  end;

  salva_arquivo;
end.
Saída (dados que eu coloquei):
Ficha 1
Nome: Beatriz
Idade: 26
Peso: 58
Ficha 2
Nome: Carla
Idade: 18
Peso: 55
Ficha 3
Nome: Fabiana
Idade: 22
Peso: 56

  Ao abrirmos o arquivo "ficha_2.dat" em um editor hexadecimal, o resultado é o seguinte:
0000:0000 | 07 42 65 61  74 72 69 7A  00 00 00 00  00 00 00 00 | .Beatriz........
0000:0010 | 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00 | ................
0000:0020 | 00 00 00 00  00 00 00 00  00 00 1A 00  00 00 00 00 | ................
0000:0030 | 00 00 00 00  00 00 4D 40  05 43 61 72  6C 61 00 00 | ......M@.Carla..
0000:0040 | 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00 | ................
0000:0050 | 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00 | ................
0000:0060 | 00 00 12 00  00 00 00 00  00 00 00 00  00 80 4B 40 | ..............K@
0000:0070 | 07 46 61 62  69 61 6E 61  00 00 00 00  00 00 00 00 | .Fabiana........
0000:0080 | 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00 | ................
0000:0090 | 00 00 00 00  00 00 00 00  00 00 16 00  00 00 00 00 | ................
0000:00A0 | 00 00 00 00  00 00 4C 40                           | ......L@  
  O programa que irá ler a "ficha_2.dat" e imprimir os dados na tela é o seguinte:
type
  ficha = record
      nome : string[40];
      idade : integer;
      peso : real;
    end;

var ficha_arq : file of ficha;
    fichario : array[1..3] of ficha;
    i : integer;

procedure imprime_ficha(pos : integer);
begin
  writeln('Ficha ', pos);
  writeln('Nome: ', fichario[pos].nome);
  writeln('Idade: ', fichario[pos].idade);
  writeln('Peso: ', fichario[pos].peso);
  writeln;
end;

procedure le_arquivo;
var i : integer;
begin
  assign(ficha_arq, 'ficha_2.dat');
  reset(ficha_arq);

  for i:=1 to 3 do
  begin
    read(ficha_arq, fichario[i]);
  end;

  close(ficha_arq);
end;

begin

  le_arquivo;

  for i:=1 to 3 do
  begin
    imprime_ficha(i);
  end;

end.
Saída:
Ficha 1
Nome: Beatriz
Idade: 26
Peso: 5.80000000000000E+001

Ficha 2
Nome: Carla
Idade: 18
Peso: 5.50000000000000E+001

Ficha 3
Nome: Fabiana
Idade: 22
Peso: 5.60000000000000E+001

  Você também pode ir direto para um registro do arquivo através da instrução seek(arq_var, n), onde n é o número do registro. Entretanto, n varia de 0 a total_registros - 1.
type
  ficha = record
      nome : string[40];
      idade : integer;
      peso : real;
    end;

var ficha_arq : file of ficha;
    fichario : array[1..3] of ficha;
    i : integer;

procedure imprime_ficha(pos : integer);
begin
  writeln('Ficha ', pos);
  writeln('Nome: ', fichario[pos].nome);
  writeln('Idade: ', fichario[pos].idade);
  writeln('Peso: ', fichario[pos].peso);
  writeln;
end;

procedure le_e_imprime_ficha(pos : integer);
begin
  assign(ficha_arq, 'ficha_2.dat');
  reset(ficha_arq);
  seek(ficha_arq, pos-1);          { Varia de 0 a n-1 }
  read(ficha_arq, fichario[pos]);  { Varia de 1 a n }
  close(ficha_arq);

  imprime_ficha(pos);
end;

begin
  le_e_imprime_ficha(2);
end.
Saída:
Ficha 2
Nome: Carla
Idade: 18
Peso: 5.50000000000000E+001

  Podemos também ler/gravar um vetor de tipo, todo de uma vez (opção 4).
  O que fizemos nos exemplos anteriores, foi criar uma variável "ficha_arq" do modelo "arquivo de tipo ficha". Dessa forma, cada leitura/escrita no disco irá gravar dados do tipo ficha (no caso, é apenas uma ficha).
  Agora, iremos criar um tipo novo de dado, chamado "ficha_vet", que será um vetor de tamanho igual a três, do tipo ficha (modela o tipo que vamos precisar).
  Iremos modificar a variável "ficha_arq" para ser do tipo "arquivo de ficha_vet". Assim, a cada leitura/gravação, todo o vetor é gravado de uma só vez.
 
  Modificamos os programas anteriores:

Gravação Leitura
type
  ficha = record
      nome : string[40];
      idade : integer;
      peso : real;
    end;

  ficha_vet = array[1..3] of ficha;

var ficha_arq : file of ficha_vet;
    fichario : array[1..3] of ficha;
    i : integer;

procedure preenche_ficha(pos : integer);
begin
  writeln('Ficha ', pos);
  write('Nome: ');
  readln(fichario[pos].nome); 
  write('Idade: ');
  readln(fichario[pos].idade);
  write('Peso: ');
  readln(fichario[pos].peso);
end;

procedure salva_arquivo;
var i : integer;
begin
  assign(ficha_arq, 'ficha_2.dat');
  rewrite(ficha_arq);
  write(ficha_arq, fichario);
  close(ficha_arq);
end;

begin

  for i:=1 to 3 do
  begin
    preenche_ficha(i);
  end;

  salva_arquivo;
end.
type
  ficha = record
      nome : string[40];
      idade : integer;
      peso : real;
    end;

  ficha_vet = array[1..3] of ficha;

var ficha_arq : file of ficha_vet;
    fichario : array[1..3] of ficha;
    i : integer;

procedure imprime_ficha(pos : integer);
begin
  writeln('Ficha ', pos);
  writeln('Nome: ', fichario[pos].nome);
  writeln('Idade: ', fichario[pos].idade);
  writeln('Peso: ', fichario[pos].peso);
  writeln;
end;

procedure le_arquivo;
var i : integer;
begin
  assign(ficha_arq, 'ficha_2.dat');
  reset(ficha_arq);
  read(ficha_arq, fichario);
  close(ficha_arq);
end;

begin

  le_arquivo;

  for i:=1 to 3 do
  begin
    imprime_ficha(i);
  end;

end.

  Obs: Os comandos read/write não fazem mais o acesso a uma posição do vetor (fichario[pos]), mas sim a todo o vetor (fichario).


/MARMSX/CURSOS/PASCAL