MC102 |
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?
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:
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.