Métodos de Programação I (2005/2006) 1 Ficheiro (file) • O tipo estruturado file representa, em Pascal, uma estrutura de dados capaz de armazenar dados ou resultados de qualquer outro tipo • É uma estrutura externa e, em geral, uma estrutura permanente • Permite a comunicação com o utilizador ou entre programas • Estruturalmente é semelhante a uma tabela (array) • Tamanho dinâmico • Acesso sequencial Diagrama sintático para o tipo file: → file of → tipo → Ficheiros standard: Ficheiros com caracterı́sticas especiais, pré-declarados no sistema ◦ input: habitualmente especifica o terminal (stdin); ◦ output: habitualmente especifica o monitor (stdout); Existem sistemas onde é possı́vel redeclarar estes ficheiros, evitando assim a associação obrigatória ao terminal ou monitor. Métodos de Programação I (2005/2006) 2 O Pascal permite usar o nome (identificador) de qualquer ficheiro no cabeçalho do programa, indicando assim ao sistema operativo que este programa vai usar estes ficheiros: program Ex_ficheiros(input, dados, resultados, output); • Os ficheiros podem existir previamente ou podem ser criados aquando da execução do programa. • Um ficheiro (não–standard) é uma variável logo tem de ser declarado num cabeçalho. var entrada: file of integer; resultados: file of char; (mais) Exemplos: type FichInt = file of integer; var entrada: FichInt; resultados: ERRO: text; ficheiros: ⇐⇒ file of char; file of file of real; Não existem ficheiros de ficheiros! Métodos de Programação I (2005/2006) • Os ficheiros input e output são do tipo text • A instrução assign liga a variável do tipo ficheiro ao seu nome externo: por exemplo, assign(entrada,"dados"); diz ao S.O. que o identificador de ficheiro de inteiros entrada (declarado no acetato anterior) fica associado ao ficheiro externo de nomedados (que tem de ser do mesmo tipo, ou seja, um ficheiro só com números inteiros). • É sempre necessário preparar o ficheiro para leitura ou para escrita: ◦ reset: a instrução reset(fich), onde fich é um identificador de uma variável do tipo ficheiro, coloca o ficheiro de nome fich no modo de leitura ou entrada (Pode ser inspeccionada (lida) mas não pode ser alterada.) ◦ rewrite: a instrução rewrite(fich), onde fich é um identificador de uma variável do tipo ficheiro, coloca o ficheiro de nome fich no modo de escrita ou saı́da (Pode ser alterada mas não pode ser inspeccionada) 3 Métodos de Programação I (2005/2006) 4 Estrutura e acessos a ficheiros: • Um ficheiro é uma sequência de elementos (componentes) do mesmo tipo (a noção de linhas só existe nos ficheiros de texto) • Cada componente é uma identidade indivisı́vel: var Fstring10: file of array[1..10] of char; – Cada componente é uma tabela de 10 elementos do tipo caracter; – Não é possı́vel aceder a um caracter individual; • Conceptualmente, um ficheiro é uma tabela de elementos seguidos do mesmo tipo com acesso sequencial: var f: file of integer -45 -11 -4 0 1 7 18 27 35 40 ↑ 18 variável tampão (buffer) 43 50 / eof • Quando da declaração da variável ficheiro, é–lhe associada uma variável especial, dita variável janela ou tampão (buffer), que é do mesmo tipo dos elementos do ficheiro. • A variável janela permite o acesso sequencial a cada componente do ficheiro (da primeira para a última), ficando com uma cópia do valor da componente actualmente apontada pela janela: fˆ = 18 Métodos de Programação I (2005/2006) 5 Preparar o ficheiro para o modo de leitura: reset(f ) – coloca a janela na primeira componente do ficheiro f, atribui à variável fˆ uma cópia do valor desse elemento e, caso o ficheiro seja não vazio, atribui o valor false à função lógica eof(f ) get(f ) – avança a janela para a próxima componente do ficheiro f, modifica o valor da variável fˆ para a cópia do valor deste elemento; caso o ficheiro tenha chegado ao fim, atribui o valor true à função lógica eof(f ) Exemplo: var f: file of integer; num: integer; . . . reset(f); -45 ↑ -11 -45 fˆ -4 0 1 7 18 27 35 40 43 50 / eof 18 27 35 40 43 50 / eof eof(f ) = false num:=f^; get(f); -45 -11 ↑ -11 fˆ get(f); -4 0 1 7 num= -45 eof(f ) = false Métodos de Programação I (2005/2006) -45 -11 -4 ↑ 0 -4 fˆ 1 7 6 18 num= -45 27 35 40 43 50 / eof 50 / ↑ eof(f ) = false . . . get(f); -45 -11 -4 0 1 7 18 27 35 40 43 ? eof(f ) = true fˆ get(f); Error: attempt to read past end of file Problema: Carregar num vector de inteiros a informação contida num ficheiro de inteiros: reset(f); n := 0; while not eof(f) do begin n := n+1; v[n] := f^; get(f); end; Métodos de Programação I (2005/2006) 7 Preparar o ficheiro para o modo de escrita: rewrite(f ) – (ficheiro f considera-se vazio) coloca a janela no “fim” do ficheiro f; a variável fˆ está indefinida; atribui o valor true à função lógica eof(f ) put(f ) – coloca o valor actual de fˆ como componente no ficheiro f; avança a janela e a variável fˆ fica indefinida; atribui o valor true à função lógica eof(f ). Exemplo: var f: file of char; num: integer; . . . rewrite(f); / ↑ ? fˆ eof(f ) = true f^:=’e’; put(f); e / ↑ ? fˆ eof(f ) = true Métodos de Programação I (2005/2006) f^:=’x’; put(f); f^:=’p’; put(f); e x e m 8 f^:=’e’; put(f); f^:=’l’; put(f); p l o f^:=’m’; put(f); f^:=’o’; put(f); / ↑ ? fˆ eof(f ) = true Notas: • Um mesmo ficheiro não pode ser utilizado para escrever e ler simultaneamente • É possı́vel usar o mesmo ficheiro para ler e escrever desde que não seja problemático perder a informação anterior • É possı́vel abrir o mesmo ficheiro várias vezes para leitura ou para escrita • Para actualizar um ficheiro existente é necessário criar um novo ficheiro (ver próximo acetato) • As funções read e write são mais simples de utilizar mas a janela (buffer) permite ver (aceder) a componente que está actualmente na janela sem de facto a ler (i.e., sem avançar a janela para a próxima componente) Métodos de Programação I (2005/2006) 9 Problema: Actualizar um ficheiro do tipo real com mais n valores do mesmo tipo, contidos num dado vector. var i : integer; ... reset(f); { prepara f para leitura } rewrite(res); { prepara res para escrita } while not eof(f) do begin { copia f para res } res^ := f^; put(res); get(f); { proximo elemento de f } end; for i:=1 to n do begin { coloca novos valores em res } res^ := v[i]; put(res); end; ... Métodos de Programação I (2005/2006) 10 Ficheiros Formatados do tipo text • Ficheiros do tipo caracter (text ⇐⇒ file of char) • Facilitam a comunicação com utilizadores • Fáceis de manipular • Ineficientes Sendo ficheiros onde cada componente é um caracter, é ainda possı́vel ler e escrever números reais ou inteiros: var fich: text; x: real; num: integer; c: char; . . . read(fich, c); • lê o caracter actual de fich para c write(fich, c); • escreve o valor de c (um caracter) no final de fich read(fich, num); ◦ avança espaços em branco ◦ lê a próxima sequência de caracteres até que fichˆ 6∈ {0 00 , . . . ,0 90 } ◦ converte no respectivo inteiro Se a componente (caracter) actual da janela 6∈ {0 +0 ,0 −0 ,0 00 , . . . ,0 90 } ocorre um erro Métodos de Programação I (2005/2006) 11 write(fich, num); ◦ converte o número na respectiva string de dı́gitos ◦ escreve a string no ficheiro ◦ avança a janela • Num ficheiro de texto existe um sı́mbolo especial, o caracter newline que permite usar a instrução writeln(): writeln(fich, ’exemplo’); m write(fich, ’exemplo’); write(fich, ’\n’); • Num ficheiro de texto existe uma outra função lógica eoln() que tem o valor true se a janela apontar um caracter newline e false caso contrário p p o o r \n c á \n / ↑ r fˆ eoln(f ) = false r \n ↑ fˆ c á \n eof(f ) = false / eoln(f ) = true eof(f ) = false Métodos de Programação I (2005/2006) p p o o r r \n \n 12 c á \n / ↑ c fˆ eoln(f ) = false c á \n ↑ eof(f ) = false / eoln(f ) = true fˆ eof(f ) = false p o r \n c á \n / ↑ ? eoln(f ) = ? fˆ eof(f ) = true • O Pascal standard acaba todos os ficheiros de texto com um newline • Também o readln() só pode ser usado em ficheiros de texto: readln(fich,car); ◦ copia o valor de fich^ para car ◦ avança até ao encontrar o próximo ’\n’ ◦ coloca a janela a apontar o caracter seguinte Métodos de Programação I (2005/2006) 13 Considere o seguinte exemplo: ... \n 3 4 1 \n 6 7 \n / var sum, n: integer; ... reset(f); sum := 0; while not eof(f) do begin read(f, n); sum := sum + n; end; ... Error: attempt to read past end of file • A instrução read(f, n) avança caracteres “brancos”, à procura de um número inteiro e ’\n’ é um caracter “branco”! Neste caso é conveniente usar um procedimento especializado que ajuda a preparar o próximo input: um procedimento que ”salte brancos” e deixe a janela a apontar o próximo caracter “não branco” Métodos de Programação I (2005/2006) 14 Que acontecerá na situação seguinte? ... \n 3 4 \n T 7 \n / Para ler reais e inteiros de ficheiros de texto deverão criar-se procedimentos para validar as entradas e fazer tratamento de erros ! • Funções read e write com múltiplos argumentos nos ficheiros de texto é possı́vel usar mais do que um argumento na mesma chamada: read(fich,x1); read(fich,x1, x2, x3); ⇐⇒ read(fich,x2); read(fich,x3); read(fich,x1, x2, x3); readln(fich,x1, x2, x3); ⇐⇒ readln(fich); Nota: quando as funções são chamadas sem argumentos do tipo ficheiro, são usados, por defeito, os ficheiros standard Métodos de Programação I (2005/2006) 15 Problema: Dado um texto, determinar a percentagem de frequência de cada uma das letras desse texto. Estratégia: Sendo, ◦ n o número total de letras que aparece no texto dado ◦ nx o número de vezes que aparece a letra x nesse texto então, a frequência da letra x em percentagem é dada por, f req(x) = nx × 100 n Dado um ficheiro de texto, ler todos os seus caracteres e, para cada um deles, caso seja letra do alfabeto, contar mais uma letra destas e contar mais uma letra no total. Para cada uma das diferentes letras encontradas, calcular a respectiva frequência. Algoritmo genérico: • Abrir fich, ficheiro de texto, para leitura; • Enquanto fich for não vazio: – ler caracter; – caso caracter seja letra do alfabeto: ∗ incrementar contador respectivo ∗ incrementar contador total de letras • Calcular, para cada letra encontrada, a respectiva frequência e escrever no ficheiro de saı́da. Métodos de Programação I (2005/2006) Notas: ◦ Não se deve diferenciar entre maiúsculas e minúsculas ◦ Atenção às mudanças de linha! • Uma vez que se terá que usar um contador para cada letra possı́vel do alfabeto, considerar-se-á uma tabela de contadores, ou seja, array[’a’..’z’] of IntNNeg • Serão, também, usados conjuntos do tipo letra para distinguir entre caracteres letra e outros caracteres que possam aparecer no texto program Frequencia(texto,tabela,output); type IntNNeg = 0..32000; Contador = array[’a’..’z’] of IntNNeg; var texto, tabela: text; conta: Contador; total, xminusc: IntNNeg; {xminusc "transforma" Maiuscs em minuscs} car: char; 16 Métodos de Programação I (2005/2006) begin { programa principal } { inicia contadores conta } for car:= ’a’ to ’z’ do conta[car]:=0; { le e conta totais } assign(texto, ’texto.txt’); reset(texto); { abre ficheiro modo leitura } n := 0; xminusc := ord(’a’)-ord(’A’); { leitura e contabilizacao } while not eof(texto) do begin while not eoln(texto) do { "saltar" newline } begin read(texto, car); if car in [’a’..’z’,’A’..’Z’] then begin if car in [’A’..’Z’] then car := chr(ord(car)+xminusc); conta[car] := conta[car]+1; { + esta letra } total := total+1; { + uma letra } end {if } end; { while not eoln } readln(texto); { avanca para prox. car. <> eoln } end; { while not eof } close(texto); { prepara ficheiro para escrita } assign(tabela, ’tab.txt’); rewrite(tabela); { calcula frequencia e escreve } writeln(tabela,’***** Tabela de Frequencias *****’); 17 Métodos de Programação I (2005/2006) writeln(tabela); for car := ’a’ to ’z’ do begin write(tabela,’ ’, car:4,’:’); writeln(conta[car]/total*100:5:2,’%’); end; close(tabela); end. 18 Métodos de Programação I (2005/2006) Aplicação deste programa sobre o próprio texto do programa (ficheiro .p): ***** Tabela de Frequencias ***** a:13.28% b: 1.99% c: 6.77% d: 2.79% e:10.89% f: 2.52% g: 1.59% h: 1.46% i: 6.51% j: 0.00% k: 0.00% l: 4.52% m: 1.46% n: 8.76% o: 7.44% p: 1.59% q: 0.40% r: 9.43% s: 1.99% t: 9.56% u: 2.39% v: 0.93% w: 1.06% x: 1.46% y: 0.27% z: 0.93% 19