Registos em Ficheiros - Estruturas
Pedro Barahona
DI/FCT/UNL
Introdução aos Computadores e à Programação
2º Semestre 2006/2007
4 Maio 2007
Registos em Ficheiros - Estruturas
1
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
610
825
316
34
723
4 Maio 2007
nome
Paulo Fernandes Lopes
Pedro Vieira
Marta Costa Martins
Rui Vasco Pereira
Jorge Barata
vencimento
data
2341.36
989.24
1389.17
5310.32
767.26
15/04/1996
25/06/1999
05/01/1992
15/04/1996
03/09/2002
Registos em Ficheiros - Estruturas
2
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
610
825
316
34
723
4 Maio 2007
nome
Paulo Fernandes Lopes
Pedro Vieira
Marta Costa Martins
Rui Vasco Pereira
Jorge Barata
vencimento
data
2341.36
989.24
1389.17
5310.32
767.26
15/04/1996
25/06/1999
05/01/1992
15/04/1996
03/09/2002
Registos em Ficheiros - Estruturas
3
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
610
825
316
34
723
4 Maio 2007
nome
Paulo Fernandes Lopes
Pedro Vieira
Marta Costa Martins
Rui Vasco Pereira
Jorge Barata
vencimento
data
2341.36
989.24
1389.17
5310.32
767.26
15/04/1996
25/06/1999
05/01/1992
15/04/1996
03/09/2002
Registos em Ficheiros - Estruturas
4
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
5
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
6
Leitura de Ficheiros
•
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
[cod, count]
[nome, count]
[venc, count]
[data,count]
=
=
=
=
fscanf(fid,"%i","C");
fscanf(fid,"%25c","C");
fscanf(fid,"%f","C");
fscanf(fid,"%s","C");
ou numa só instrução
[cod,nome,venc,data,count]=fscanf(fid,"%i%25c%f%s","C");
610
4 Maio 2007
Paulo Fernandes Lopes
2341.36
Registos em Ficheiros - Estruturas
15/04/1996
7
Leitura de Ficheiros
•
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 :
[cod, count]
[nome, count]
[venc, count]
[data,count]
=
=
=
=
fscanf(fid,"%i","C");
fscanf(fid,"%s","C");
fscanf(fid,"%f","C");
fscanf(fid,"%s","C");
quer numa só instrução
[cod,nome,venc,data,count] = fscanf(fid,"%i%s%f%s","C");
610
4 Maio 2007
Paulo Fernandes Lopes
2341.36
Registos em Ficheiros - Estruturas
15/04/1996
8
Escrita de Ficheiros
•
Comprimento variável:
–
Neste caso o registo de um empregado pode ser escrito como
fprintf(fid,
fprintf(fid,
fprintf(fid,
fprintf(fid,
"%i\t", cod);
"%s\t", nome);
"%7.2f\t", venc);
"%s\n", data);
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.
610
4 Maio 2007
Alguns templates podem ser fixos, (por exemplo, "%7.2f\t”) para evitar as
convenções por omissão do Octave.
Paulo Fernandes Lopes
2341.36
Registos em Ficheiros - Estruturas
15/04/1996
9
Escrita de Ficheiros
• Comprimento fixo:
–
A escrita de ficheiros depende igualmente do formato utilizado para as strings.
Em comprimento fixo, o registo pode ser escrito como
fprintf(fid,
fprintf(fid,
fprintf(fid,
fprintf(fid,
fprintf(fid,
"%3i", cod);
"%-25s", nome);
"%7.2f", venc);
"%10s", data);
"%c", “\n”);
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
610Paulo Fernandes Lopes
4 Maio 2007
2341.3615/04/1996
Registos em Ficheiros - Estruturas
10
Selecção de Registos
•
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.
4 Maio 2007
Registos em Ficheiros - Estruturas
11
Selecção de Registos
•
Eis a versão para formato fixo :
[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
12
Selecção de Registos
•
E a versão para formato variável :
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
13
Processamento de Registos
•
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).
4 Maio 2007
Registos em Ficheiros - Estruturas
14
Processamento de Registos
•
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).
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
15
Processamento de Registos
•
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
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
16
Comparação de Datas
•
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).
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
17
Comparação de Datas
•
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).
function d = d_comp(ano1,
if
ano1 < ano2
d
elseif ano1 > ano2
d
elseif mes1 < mes2
d
elseif mes1 > mes2
d
elseif dia1 < dia2
d
elseif dia1 > dia2
d
else
d
endif
endfunction
4 Maio 2007
mes1, dia1, ano2, mes2, dia2);
= -1;
= 1;
= -1;
= 1;
= -1;
= 1;
= 0;
Registos em Ficheiros - Estruturas
18
Processamento de Registos
•
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
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
19
Estruturas e Listas
•
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.
4 Maio 2007
Registos em Ficheiros - Estruturas
20
Estruturas
•
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)
cod
nome
610 Paulo Fernandes Lopes
4 Maio 2007
venc
2341.36
data
15/04/1996
Registos em Ficheiros - Estruturas
21
Estruturas
•
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.
cod
nome
610 Paulo Fernandes Lopes
4 Maio 2007
venc
2341.36
data
15/04/1996
Registos em Ficheiros - Estruturas
22
Estruturas
•
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:
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
cod
nome
610 Paulo Fernandes Lopes
venc
2341.36
Registos em Ficheiros - Estruturas
data
15/04/1996
23
Estruturas
•
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
if emp_610.venc > 1000
then disp(emp_610.nome)
endif
•
No entanto este tipo de processamento só é verdadeiramente útil se tivermos a
possibilidade de aceder a todos os empregados de uma forma genérica.
4 Maio 2007
Registos em Ficheiros - Estruturas
24
Tabelas
•
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:
1
2
1
610
2341.36
for i = 1:n
2
825
989.24
if tabela(i,2) > 1000
disp(tabela(i,1))
3
316
1389.17
endif
4
34
5310.32
endfor;
5
723
767.26
...
...
...
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.
4 Maio 2007
Registos em Ficheiros - Estruturas
25
Vectores de Estruturas
•
•
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.
ind
cod
1
2
3
4
5
610
825
316
34
723
nome
Paulo Fernandes Lopes
Pedro Vieira
Marta Costa Martins
Rui Vasco Pereira
Jorge Barata
vencimento
data
2341.36
989.24
1389.17
5310.32
767.26
15/04/1996
25/06/1999
05/01/1992
15/04/1996
03/09/2002
Agora, para obter os códigos dos empregados com vencimento superior a 1000
euros bastaria usar uma seguinte instrução iterativa, análoga à anterior
for i = 1:n
if emps(i).vencimento > 1000
disp(emps(i))
endif
endfor;
4 Maio 2007
for i = 1:n
if tabela(i,2) > 1000
disp(tabela(i,1))
endif
endfor;
Registos em Ficheiros - Estruturas
26
Listas
•
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
for i = 1:n
if nth(emps,i).vencimento > 1000
disp(nth(emps,i))
endif
endfor;
4 Maio 2007
Registos em Ficheiros - Estruturas
27
Download

Document