Upload
internet
View
105
Download
0
Embed Size (px)
Citation preview
4 Maio 2007 Registos em Ficheiros - Estruturas 1
Registos em Ficheiros - Estruturas
Pedro BarahonaDI/FCT/UNL
Introdução aos Computadores e à Programação2º Semestre 2006/2007
4 Maio 2007 Registos em Ficheiros - Estruturas 2
Registos em Ficheiros
• Muita informação alfanumérica está registada em ficheiros, na forma de
“registos”. Por exemplo, numa base de dados da empresa, são mantidos ficheiros
com informação sobre os empregados da empresa.
• Muitas aplicações (de gestão) consistem em ler ficheiros e criar outros com a
informação devidamente processada. Por exemplo: ler um ficheiro de empregados
e escrever outro, apenas com os empregados com vencimento superior a 1000 €.
cod nome vencimento data
610 Paul o Fer nandes Lopes 2341. 36 15/ 04/ 1996825 Pedr o Vi ei r a 989. 24 25/ 06/ 1999316 Mar t a Cost a Mar t i ns 1389. 17 05/ 01/ 199234 Rui Vasco Per ei r a 5310. 32 15/ 04/ 1996
723 J or ge Bar at a 767. 26 03/ 09/ 2002
4 Maio 2007 Registos em Ficheiros - Estruturas 3
Registos em Ficheiros
• A primeira questão a resolver consiste no tratamento dos caracteres brancos em
sequências de caracteres. Isto porque há duas formas típicas de armazenamento
dessas sequências:
– Comprimento Fixo: As sequências têm sempre o mesmo número de
caracteres (sendo usados espaços se necessário);
– Comprimento Variável: As sequências têm o número de caracteres
necessários, sendo necessários caracteres separadores, tipicamente tabs
(horizontais).
cod nome vencimento data
610 Paul o Fer nandes Lopes 2341. 36 15/ 04/ 1996825 Pedr o Vi ei r a 989. 24 25/ 06/ 1999316 Mar t a Cost a Mar t i ns 1389. 17 05/ 01/ 199234 Rui Vasco Per ei r a 5310. 32 15/ 04/ 1996723 J or ge Bar at a 767. 26 03/ 09/ 2002
4 Maio 2007 Registos em Ficheiros - Estruturas 4
Registos em Ficheiros
• Por exemplo, o nome “Pedro Vieira”, no ficheiro abaixo, pode ser codificado
– Comprimento Fixo: Com 5+1+6 = 12 caracteres (incluindo o espaço), mais
13 espaços, para permitir sequências de comprimento 25, que podem
armazenar nomes com até 25 caracteres (incluindo espaços);
– Comprimento Variável: Apenas com os 12 caracteres necessários, sendo
separado do vencimento por um tab horizontal (‘\t’).
cod nome vencimento data
610 Paul o Fer nandes Lopes 2341. 36 15/ 04/ 1996825 Pedr o Vi ei r a 989. 24 25/ 06/ 1999316 Mar t a Cost a Mar t i ns 1389. 17 05/ 01/ 199234 Rui Vasco Per ei r a 5310. 32 15/ 04/ 1996723 J or ge Bar at a 767. 26 03/ 09/ 2002
4 Maio 2007 Registos em Ficheiros - Estruturas 5
Registos em Ficheiros
• Em Octave (e em C) os dois tipos de codificação requerem instruções de leitura
padronizada (com templates) diferentes.
– Comprimento Fixo: Utiliza-se um template “%nc” em que n é o número de
caracteres a ler;
– Comprimento Variável: Utiliza-se um template “%s”;
• Neste último caso, levanta-se um problema: O template ‘%s’, não lê nem caracteres
brancos nem espaços. Assim, o nome “Pedro Vieira” seria lido não como 1, mas sim
como 2 sequências. Em geral, ter-se-ia de conhecer o número de nomes (próprios,
apelidos e partículas de ligação).
• Isto pode ser evitado com o uso de espaços especiais (“non break spaces” -
ASCII 160), que são considerados como quaisquer outros caracteres, mas
impressos como espaços.
4 Maio 2007 Registos em Ficheiros - Estruturas 6
Leitura de Ficheiros
• Caso seja necessário, pode converter-se um ficheiro noutro, convertendo-se todos
os espaços em espaços especiais, com a função abaixo:
function x = rem_sp(f_in_name, f_out_name); [f_in, msg] = fopen(f_in_name , "r"); [f_aux,msg] = fopen(f_out_name, "w"); [ch, count] = fscanf(f_in,"%1c","C"); while !feof(f_in) if ch == " " ch = setstr(160); endif; fprintf(f_aux, "%1c", ch); [ch, count] = fscanf(f_in,"%1c","C"); endwhile; fclose(f_in); fclose(f_aux); endfunction;
4 Maio 2007 Registos em Ficheiros - Estruturas 7
• Comprimento Fixo:
– Cada linha do ficheiro, assumindo-se que um nome é guardado com 25
caracteres, pode ser lida com as seguintes instruções de leitura padronizada,
fscanf
ou numa só instrução
Leitura de Ficheiros
610 Paulo Fernandes Lopes 2341.36 15/04/1996
[cod, count] = fscanf(fid,"%i","C"); [nome, count] = fscanf(fid,"%25c","C");[venc, count] = fscanf(fid,"%f","C");[data,count] = fscanf(fid,"%s","C");
[cod,nome,venc,data,count]=fscanf(fid,"%i%25c%f%s","C");
4 Maio 2007 Registos em Ficheiros - Estruturas 8
• Comprimento Variável:
– Neste caso, quer o nome quer a data podem ser lidos com o template “%s” da
instrução fscanf, podendo o registo de um empregado ser lido quer na forma :
quer numa só instrução
Leitura de Ficheiros
610 Paulo Fernandes Lopes 2341.36 15/04/1996
[cod, count] = fscanf(fid,"%i","C"); [nome, count] = fscanf(fid,"%s","C");[venc, count] = fscanf(fid,"%f","C");[data,count] = fscanf(fid,"%s","C");
[cod,nome,venc,data,count] = fscanf(fid,"%i%s%f%s","C");
4 Maio 2007 Registos em Ficheiros - Estruturas 9
• Comprimento variável:
– Neste caso o registo de um empregado pode ser escrito como
ou numa só instrução, como anteriormente. Notar agora que
1. Após cada campo, deve ser escrito o tab (‘\t’) de separação, excepto no último
campo, após o que se escreve o caracter (‘\n’)
2. Alguns templates podem ser fixos, (por exemplo, "%7.2f\t”) para evitar as
convenções por omissão do Octave.
Escrita de Ficheiros
fprintf(fid, "%i\t", cod);fprintf(fid, "%s\t", nome);fprintf(fid, "%7.2f\t", venc);fprintf(fid, "%s\n", data);
610 Paulo Fernandes Lopes 2341.36 15/04/1996
4 Maio 2007 Registos em Ficheiros - Estruturas 10
• Comprimento fixo:
– A escrita de ficheiros depende igualmente do formato utilizado para as strings.
Em comprimento fixo, o registo pode ser escrito como
ou numa só instrução, como anteriormente. Notar ainda que
1. O sinal – (em %-25s) justifica, à esquerda, o campo nome.
2. Após o último campo deve ser escrito um caracter (‘\n’), para mudança de linha
Escrita de Ficheiros
fprintf(fid, "%3i", cod); fprintf(fid, "%-25s", nome); fprintf(fid, "%7.2f", venc); fprintf(fid, "%10s", data);
fprintf(fid, "%c", “\n”);
610Paulo Fernandes Lopes 2341.3615/04/1996
4 Maio 2007 Registos em Ficheiros - Estruturas 11
• Podemos agora abordar o problema inicialmente colocado:
1. Ler um ficheiro de empregados; e
2. Escrever outro, apenas com os empregados com vencimento superior a
1000 euros.
• Naturalmente o programa dependerá de os ficheiros serem lidos e escritos em
formato fixo ou variável.
• Assumiremos que esta diferença apenas se reflectirá no campo “nome”, já que o
campo “data” não contem espaços e pode ser lido em formato cadeia (“%s”) sem
problema.
Selecção de Registos
4 Maio 2007 Registos em Ficheiros - Estruturas 12
• Eis a versão para formato fixo :
Selecção de Registos
[f_in, msg_in ] = fopen("empresa_in_fix.txt", "r");[f_out, msg_out] = fopen("empresa_out_fix.txt", "w");
[cod,nome,venc,data,ct] = fscanf(f_in,"%i%25c%f%s","C");while !feof(f_in) if venc > 1000 fprintf(f_out,"%3i%-25s%7.2f%12s\n", cod,nome,venc,data); printf( "%3i%-25s%7.2f%12s\n", cod,nome,venc,data); endif; [cod,nome,venc,data,ct] = fscanf(f_in,"%i%25c%f%s","C");endwhile;
fclose(f_in); fclose(f_out);
4 Maio 2007 Registos em Ficheiros - Estruturas 13
• E a versão para formato variável :
Selecção de Registos
rem_sp("empresa_in_var.txt", "empresa_aux_var.txt");
[f_aux, msg] = fopen("empresa_aux_var.txt", "r");[f_out, msg] = fopen("empresa_out_var.txt", "w");
[cod,nome, venc, data, count] = fscanf(f_aux,"%i%s%f%s","C");while !feof(f_aux) if venc > 1000 fprintf(f_out, "%i\t%s\t%7.2f\t%s\n", cod,nome,venc,data); printf( "%i\t%s\t%7.2f\t%s\n", cod,nome,venc,data); endif; [cod,nome, venc, data, count] = fscanf(f_aux,"%i%s%f%s","C");endwhile;
fclose(f_aux); fclose(f_out);
4 Maio 2007 Registos em Ficheiros - Estruturas 14
• Podemos agora considerar outro tipo de processamento de informação, que não
envolve necessariamente a escrita de novos ficheiros, correspondente a:
– Cálculo de totais e médias (de vencimentos, por exemplo)
– Determinação de máximos e mínimos (de vencimentos, ou antiguidades)
• Vamos ilustrar estes problemas com programas para determinação dos
vencimentos totais e médios dos empregados da empresa, bem como da
determinação do empregado mais antigo.
• Para ambos os problemas, apenas são apresentadas as versões para formato
variável.
• De notar ainda a instrução printf, que permite escrever no terminal mensagens
formatadas (com os formatos usados em ficheiros).
Processamento de Registos
4 Maio 2007 Registos em Ficheiros - Estruturas 15
• O tratamento de vencimentos utiliza um contador (variável i) de registos lidos e uma
variável (total) para determinação do total dos vencimentos (sendo a média igual à
razão entre total e i).
Processamento de Registos
rem_sp("empresa_in_var.txt", "empresa_aux_var.txt");
[f_aux, msg] = fopen("empresa_aux_var.txt", "r");
i = 0; total = 0;[cod,nome, venc, data, count] = fscanf(f_aux,"%i%s%f%s","C");while !feof(f_aux) i = i+1; total = total+venc; [cod,nome,venc,data,count] = fscanf(f_aux,"%i%s%f%s","C");endwhile; printf("o total dos vencimentos é de %7.2f \n", total);printf("a média dos vencimentos é de %7.2f \n",total/i);
fclose(f_aux); fclose(f_out);
4 Maio 2007 Registos em Ficheiros - Estruturas 16
• O tratamento de datas, implica reconhecer quando uma data é anterior a outra, o
que é feito com a função “data_comp” que compara duas datas, sendo armazenada
a data menor
Processamento de Registos
rem_sp("empresa_in_var.txt", "empresa_aux_var.txt"); [f_aux, msg] = fopen("empresa_aux_var.txt", "r");
data_menor =“01/01/2100”;[cod,nome, venc, data, count] = fscanf(f_aux,"%i%s%f%s","C");while !feof(f_aux) if data_comp(data,data_menor) = -1 data_menor = data; endif; [cod,nome, venc, data, count] = fscanf(f_aux,"%i%s%f%s","C");endwhile;
printf("a entrada mais antiga foi em %s\n",data_menor);
fclose(f_aux);
4 Maio 2007 Registos em Ficheiros - Estruturas 17
• A comparação de datas é feita através da comparação dos anos, meses e dias, das
duas datas
• Naturalmente, há que ter o cuidado de converter inicialmente as cadeias de
caracteres em números (com a função str2num).
Comparação de Datas
function d = data_comp(data1,data2);% data no formato dd/mm/aaaa
ano1 = str2num(substr(data1,7,4)); ano2 = str2num(substr(data2,7,4)); mes1 = str2num(substr(data1,4,2)); mes2 = str2num(substr(data2,4,2)); dia1 = str2num(substr(data1,1,2)); dia2 = str2num(substr(data2,1,2)); d = d_comp(ano1, mes1, dia1, ano2, mes2, dia2);endfunction
4 Maio 2007 Registos em Ficheiros - Estruturas 18
• Uma vez obtidos os anos, dias e meses as datas podem ser comparadas,
comparando-se sucessivamente os anos, e se necessário os meses e dias).
Comparação de Datas
function d = d_comp(ano1, mes1, dia1, ano2, mes2, dia2); if ano1 < ano2 d = -1; elseif ano1 > ano2 d = 1; elseif mes1 < mes2 d = -1; elseif mes1 > mes2 d = 1; elseif dia1 < dia2 d = -1; elseif dia1 > dia2 d = 1; else d = 0; endifendfunction
4 Maio 2007 Registos em Ficheiros - Estruturas 19
• No caso do empregado mais antigo, é interessante informar não só quando ele
entrou ao serviço (isto é , a data), mas também que é ele (isto é, o seu nome) .
• Este problema pode ser resolvido com uma pequena adaptação do código,
guardando não só a data mais antiga como o nome
Processamento de Registos
rem_sp("empresa_in_var.txt", "empresa_aux_var.txt");[f_aux, msg] = fopen("empresa_aux_var.txt", "r");data_menor =“01/01/2100”;[cod,nome, venc, data, count] = fscanf(f_aux,"%i%s%f%s","C");while !feof(f_aux) if data_comp(data,data_menor) == -1 data_menor = data; antigo = nome; endif; [cod,nome, venc, data, count] = fscanf(f_aux,"%i%s%f%s","C");endwhile;printf("o empregado mais antigo é %s \n", antigo);printf("com data de entrada %s \n", data_menor);fclose(f_aux);
4 Maio 2007 Registos em Ficheiros - Estruturas 20
• Todos estes programas obrigam, cada vez que se pretende responder a uma
questão :
1. A ler registos de um ficheiro;
2. A processar cada um dios registos, isoladamente; e, eventualmente
3. A escrever os registos num outro ficheiro
• No entanto, a leitura e escrita de ficheiros, mesmo em “discos rápidos” é
tipicamente milhares de vezes mais lenta que a leitura a partir de dados em
memória.
• Haverá pois vantagem em copiar um ficheiro de registos para memória e passar a
responder às questões a partir de aí.
• Veremos como responder a esta questão em geral e, no caso do Octave, como tal
poderá ser feitos com estruturas e listas.
Estruturas e Listas
4 Maio 2007 Registos em Ficheiros - Estruturas 21
• Embora Vectores e Matrizes sejam muito úteis quando os dados são todos do
mesmo tipo (no Octave, de qualquer tipo numérico), em muitos casos, a informação
que se pretende agrupar com um só identificador não é do mesmo tipo, como é o
caso dos registos considerados anteriormente.
• Por exemplo, um empregado duma empresa pode ser associado à seguinte
informação
– Um código (um número?)
– Um nome (uma cadeia de caracteres)
– Um vencimento (um decimal)
– Uma data de entrada (uma cadeia, ou 3 campos numéricos, para o dia, mês
e ano)
Estruturas
cod nome venc data610 Paulo Fernandes Lopes 2341.36 15/04/1996
4 Maio 2007 Registos em Ficheiros - Estruturas 22
• As várias linguagens de programação permitem o agrupamento destes dados
heterogéneos, com um mesmo identificador de uma forma variada (records no
Pascal, Struct em C, ...)
• O Octave adopta uma designação semelhante à do C, denominando estes
agrupamentos como estruturas.
• Consideremos pois o caso do empregado abaixo, em que gostaríamos de agregar
toda a informação numa única variável, do tipo estrutura, que denotaremos como
emp_610.
Estruturas
cod nome venc data610 Paulo Fernandes Lopes 2341.36 15/04/1996
4 Maio 2007 Registos em Ficheiros - Estruturas 23
• Uma vez definidos os nomes dos campos da estrutura, podemos atribuir-lhe os
valores pretendidos.
• O acesso a um campo da estrutura é feito fazendo suceder ao nome da estrutura o
nome do campo pretendido, separado por um ponto (‘.’).
• Por exemplo, a atribuição dos 4 valores dos campos pode ser feita pelas seguintes
atribuições:
Estruturas
cod nome venc data610 Paulo Fernandes Lopes 2341.36 15/04/1996
emp_610.cod = 610;emp_610.nome = “Paulo Fernandes Lopes”;emp_610.venc = 2341.36;emp_610.data=“15/04/1996”;
emp_610 =
4 Maio 2007 Registos em Ficheiros - Estruturas 24
• Uma vez agrupados os vários items de informação numa só variável do tipo estrutura,
podemos referir alguns campos depois de verificar outros.
• Por exemplo, dados vários empregados com o tipo referido, indicar qual o nome dos
que ganham mais de 1000 euros.
• Na sintaxe do Octave, tal poderia ser feito através da instrução condicional
• No entanto este tipo de processamento só é verdadeiramente útil se tivermos a
possibilidade de aceder a todos os empregados de uma forma genérica.
Estruturas
if emp_610.venc > 1000 then disp(emp_610.nome)endif
4 Maio 2007 Registos em Ficheiros - Estruturas 25
• Por exemplo, se tivessemos uma tabela com várias linhas, com códigos na primeira
coluna e vencimentos na 2ª coluna, poderíamos apresentar os códigos dos
empregados com vencimento superior a 1000 euros através da seguinte instrução
iterativa:
Por analogia, o que é necessário é poder aceder a uma sequência de (1 a n)
estruturas do tipo da do empregado.
• Na maioria das linguagens de programação, essa analogia é imediata, já que se
podem especificar vectores de estruturas.
Tabelas
for i = 1:n if tabela(i,2) > 1000
disp(tabela(i,1)) endif endfor;
1 2
1 610 2341.362 825 989.243 316 1389.174 34 5310.325 723 767.26... ... ...
4 Maio 2007 Registos em Ficheiros - Estruturas 26
• Nestas linguagens, poderíamos representar o conjunto de empregados através de
um vector, emps, em que cada elemento é uma estrutura (de empregado) com os
campos definidos como anteriormente.
• Agora, para obter os códigos dos empregados com vencimento superior a 1000 euros
bastaria usar uma seguinte instrução iterativa, análoga à anterior
Vectores de Estruturas
for i = 1:n if emps(i).vencimento > 1000
disp(emps(i)) endif endfor;
i nd cod nome venci ment o dat a1 610 Paul o Fer nandes Lopes 2341. 36 15/ 04/ 19962 825 Pedr o Vi ei r a 989. 24 25/ 06/ 19993 316 Mar t a Cost a Mar t i ns 1389. 17 05/ 01/ 19924 34 Rui Vasco Per ei r a 5310. 32 15/ 04/ 19965 723 J or ge Bar at a 767. 26 03/ 09/ 2002
for i = 1:n if tabela(i,2) > 1000
disp(tabela(i,1)) endif endfor;
4 Maio 2007 Registos em Ficheiros - Estruturas 27
• Em Octave, não existe a possibilidade de definir vectores de estruturas, já que os
vectores são numéricos.
• O Octave no entanto, disponibiliza uma estrutura de dados, a lista, que permite
organizar um conjunto de elementos em memória, podendo esses elelemntos ter um
tipo qualquer, numérico ou não.
• Antecipando, poderá ser usada, em Octave, o conjunto de empregados pode ser
representada por uma lista, que também designaremos por emps, e que permite o
acesso a um seu elemento de uma forma “parecida”, nomeadamente
Listas
for i = 1:n if nth(emps,i).vencimento > 1000
disp(nth(emps,i)) endif endfor;