Registos em Ficheiros
Pedro Barahona
DI/FCT/UNL
Maio 2004
18/19 de Maio de 2004
Registos em Ficheiros
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
18/19 de Maio de 2004
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
2
Leitura de Ficheiros
• A primeira questão a resolver consiste no tratamento dos
caracteres brancos em cadeias de caracteres. Isto porque há
duas formas típicas de armazenamento dessas cadeias:
– Comprimento Fixo: As cadeias têm sempre o mesmo
número de caracteres (sendo usados espaços se necessário);
– Comprimento Variável: As cadeias têm o número de
caracteres
necessários, sendo necessários caracteres
separadores, tipicamente tabs (horizontais).
cod
610
825
316
34
723
18/19 de Maio de 2004
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
3
Leitura de Ficheiros
• Por exemplo, “Pedro Vieira” pode ser codificado
– Comprimento Fixo: Com 5+1+6 = 12 caracteres
(incluindo o espaço), mais 13 espaços, para permitir
cadeias 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
horozontal (‘\t’).
cod
610
825
316
34
723
18/19 de Maio de 2004
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
4
Leitura de 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
cadeias. 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 lidos como quaisquer
outros caracteres, mas são impressos como espaços .
18/19 de Maio de 2004
Registos em Ficheiros
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;
18/19 de Maio de 2004
Registos em Ficheiros
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
Paulo Fernandes Lopes
18/19 de Maio de 2004
2341.36
Registos em Ficheiros
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
Paulo Fernandes Lopes
18/19 de Maio de 2004
2341.36
Registos em Ficheiros
15/04/1996
8
Escrita de Ficheiros
•
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
18/19 de Maio de 2004
2341.3615/04/1996
Registos em Ficheiros
9
Escrita de Ficheiros
•
Em comprimento variável, 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, 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.
610
Paulo Fernandes Lopes
18/19 de Maio de 2004
2341.36
Registos em Ficheiros
15/04/1996
10
Selecção de Registos
•
Podemos agora abordar o problema inicialmente colocado: ler um
ficheiro de empregados e 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.
18/19 de Maio de 2004
Registos em Ficheiros
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%c",
cod,nome,venc,data);
fprintf(f_out, "%c", "\n");
endif;
[cod,nome,venc,data,ct] =
fscanf(f_in,"%i%25c%f%s","C");
endwhile;
fclose(f_in); fclose(f_out);
18/19 de Maio de 2004
Registos em Ficheiros
12
Selecção de Registos
•
E eis 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);
endif;
[cod,nome, venc, data, count] =
fscanf(f_aux,"%i%s%f%s","C");
endwhile;
fclose(f_aux); fclose(f_out);
18/19 de Maio de 2004
Registos em Ficheiros
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. Em ambos
os casos apenas apresentamos a versão para formato variável.
•
De notar a instrução printf, que permite escrever no terminal
mensagens formatadas (com os formatos usados em ficheiros).
18/19 de Maio de 2004
Registos em Ficheiros
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 ao quociente total/i).
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");
i = 0; total = 0;
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);
18/19 de Maio de 2004
Registos em Ficheiros
15
Processamento de Registos
•
O tratamento de datas, implica reconhecer quando uma data é
anterior a outra, o que é feito com a função anterior 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");
[f_out, msg] = fopen("empresa_out_var.txt", "w");
[cod,nome, venc, data, count] = fscanf(f_aux,"%i%s%f%s","C");
data_menor =“01/01/2100”;
while !feof(f_aux)
if anterior(data,m_data)
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); fclose(f_out);
18/19 de Maio de 2004
Registos em Ficheiros
16
Processamento de Registos
•
A comparação de datas é feita através da comparação dos seus
anos, meses e dias, tendo o cuidado de converter as cadeias de
caracteres em números.
function d = anterior(data1,data2);% data no formato dd/mm/aaaa
ano1 = str2num(substr(data1,7,4));
ano2 = str2num(substr(data2,7,4));
if ano1<ano2
d = 1;
elseif ano1 > ano2 d = 0;
else
mes1 = str2num(substr(data1,4,2));
mes2 = str2num(substr(data2,4,2));
if mes1<mes2 d = 1;
elseif mes1 > mes2 d = 0;
else
dia1 = str2num(substr(data1,1,2));
dia2 = str2num(substr(data2,1,2));
if dia1 < dia2 d = 1;
else d = 0;
endif;
endif;
endif;
end function;
18/19 de Maio de 2004
Registos em Ficheiros
17
Processamento de Registos
•
Neste último caso, a informação transmitida não é muito
interessante. Provavelmente estaremos mais interessados em saber
quem é o empregado mais antigo, ou seja, qual o nome do
empregado com data de entrada mais antiga.
•
Este problema pode ser resolvido com uma pequena adaptação do
código, guardando não só a data mais antiga como o nome
while !feof(f_aux)
if anterior(data,m_data)
data_menor = data; antigo = nome
endif;
[cod,nome, venc, data, count] = ...
endwhile;
printf("o empregado mais antigo é %s \n", antigo);
printf("com data de entrada %s \n", data_menor);
18/19 de Maio de 2004
Registos em Ficheiros
18
Processamento de Registos
•
Todos estes programas obrigam a ler um ficheiro, cada vez que se
pretende responder a uma questão.
•
No entanto, a leitura 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.
18/19 de Maio de 2004
Registos em Ficheiros
19
Download

Registos em ficheiros