Вы находитесь на странице: 1из 24

15/10/2015

HolaMundo.pascal(online):Captulo6

Ms Siguienteblog

Crearblog Acceder

Publicaciones del Autor


.pas

.c

.java

7 DE AG O STO DE 2 0 0 7

Captulo 6
.

Tipos de Datos definidos por el Programador


Los lenguajes de programacin brindan la posibilidad al programador de definir sus propios
tipos de datos.
En Pascal tenemos la seccin type donde podemos definir nuevos tipos. Un ejemplo de esto es
el siguiente programa en el que se definen los tipos de datos: Entero y Cadena, y luego se
definen dos variables (e y s) de esos tipos y se las usa.
testType.pas
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:

type
Entero=integer;
Cadena=string[20];

>> Click para Ms Informacin <<

Recursos / Download
var
//definounavariabledetipoEntero
e:Entero;

SZDelphi (por JDownloader)

//definounavariabledetipoCadena
s:Cadena;

Plantilla de Diagramas p/Visio

begin
e:=10;
s:='Prueba';
writeln(e,'',s);
end.

Instalacin y uso de Delphi

Contenidos

1 Introduccin
2 Operadores
3 Procedimientos y Funciones
4 Corte de Control

En este captulo nos interesa definir tipos de datos compuestos, es decir: tipos de datos que
agrupen dos o ms datos simples.

5 Arrays
6 Archivos de Registros

Registros
Llamamos registro o estructura a un tipo de datos definido por el programador que agrupa dos
o ms datos relacionados entre s, a los que llamaremos campos.
Veamos un programa que define un registro RAlumno que contiene 3 campos: legajo, nombre
y fecNac.

7 Estructuras Dinmicas
8 Arboles y Recursividad
9 Teora de Objetos (intro)
EjemplosResueltos

testRecord.pas
1:
2:
3:
4:
5:
6:

Ahora en Video!
type
//definimosunnuevotipodedatos
//un"registro"
RAlumno=record

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

1/24

15/10/2015

HolaMundo.pascal(online):Captulo6
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:

legajo:integer;
nombre:string[20];
fecNac:longint;//aaaammdd
end;
varalumno:RAlumno;
begin
//leemosellegajo
write('IngreseLegajo:');
readln(alumno.legajo);
//leemoselnombre
write('IngreseNombre:');
readln(alumno.nombre);
//leemoslafecha
write('IngreseFecha(aaaammdd):');
readln(alumno.fecNac);
//imprimimoslosdatos
writeln('Legajo:',alumno.legajo);
writeln('Nombre:',alumno.nombre);
writeln('Fecha:',alumno.fecNac);
end.

Acerca del Autor


Ing. Pablo A. Sznajdleder
Agradecimientos

No al blog de Java

Por convensin utilizaremos el prefijo R para indicar que el tipo de datos corresponde a un
registro. Por ejemplo: REmpleado, RAlumno, etc.

Super Baterista

Analicemos nuevamente el registro RAlumno:


type RAlumno = record
...legajo: integer;
...nombe: string[20];
...fecNac: longint; // aaaammdd
end;
El campo fecNac a su vez podra ser un registro con tres campos: dia, mes y anio. Entonces la
seccin type quedara as:
Sitios Relacionados
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:

type
//definimosunregistroRFecha
RFecha=record
dia:integer;
mes:integer;
anio:integer;//nousamosla"enie"
end;

PascAlgo (por Lic. Hugo Cuello)


Algoritmia.net
Java Algorithm Framework

RAlumno=record
legajo:integer;
nombre:string[20];
fecNac:RFecha;//estecampoestipoRFecha
end;

Entonces para asignar la fecha de nacimiento de un alumno (fecNac) ser:


1:
2:
3:
4:
5:
6:
7:
8:

varalumno:RAlumno;
begin
alumno.fecNac.dia:=2;
alumno.fecNac.mes:=10;
alumno.fecNac.anio:=1970;
end.

Archivos
http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

2/24

15/10/2015

HolaMundo.pascal(online):Captulo6

Llamamos archivo a todo conjunto de datos almacenado en un dispositivo de alacenamiento


secundario (no en memoria principal).
La informacin contenida en un archivo puede ser homogenea o no. Es decir: un archivo puede
contener un conjunto de valores integer, un conjunto de valores string, un conjunto de
registros de alumnos (RAlumno), etc. Pero tamben podra contener una combinacin
arbitraria de diferentes tipos de datos: por ejemplo un integer con un valor n, luego n
registros de tipo RAlumno, Luego un longint conteniedo otro valor y as.
En este captulo estudiaremos archivos homogneos, principalmente archivos de registros.

Grabar un Archivo
Veamos el cdigo de un programa que lee valores numricos por teclado y los graba en un
archivo llamado SALIDA.dat.
grabarArchivo.pas
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:

type
FInteger=fileofinteger;
var
arch:FInteger;//variabledearchivo
v:integer;
begin
//relacionamoslavariabledearchivocon
//elarchivofisicoendisco:SALIDA.dat
assign(arch,'SALIDA.dat');
//sielarchivonoexisteentonceslocrea
//sielarchivoexisteentonceslovacia
rewrite(arch);
//leemosunvalorporteclado(tipointeger)
read(v);
//finalizamoslacargadedatosconcero
while(v<>0)dobegin
//escribimosenelarchivoelvalorleido
write(arch,v);
//leemoselsiguientevalor(porteclado)
read(v);
end;
//cierraelarchivo
close(arch);
end.

Anlisis
Analizando el cdigo anterior podremos ver los puntos fundamentales para poder manejar
archivos dentro de un programa.
Comenzamos definiendo un tipo de datos propio para el archivo. El tipo FInteger. Por
convensin utilizaremos el prefijo F para identificar los tipos de datos que se refieren a
archivos.
Recordemos que el archivo contendr valores numricos ingresados por teclado. As que un
archivo de integer estar bien (siempre que no se ingresen valores con decimales o valores
mayores que 32767). Tambin definimos una variable del tipo FInteger. La variable arch.
1:
2:
3:
4:
5:
6:
7:
8:

type
FInteger=fileofinteger;
var
arch:FInteger;//variabledearchivo
v:integer;

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

3/24

15/10/2015

HolaMundo.pascal(online):Captulo6

Teniendo definido el tipo del archivo y una variable de ese tipo para manejarlo tenemos que
relacionar la variable de archivo (arch) con un archivo fsico en el disco. En el ejemplo el
archivo fsico ser SALIDA.dat. Con la funcin assign establecemos esta relacin y luego con
la funcin rewrite creamos el archivo. Es decir: si no exista lo crea y si exista lo deja vacio
(con 0 bytes).
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:

begin
//relacionamoslavariabledearchivocon
//elarchivofisicoendisco:SALIDA.dat
assign(arch,'SALIDA.dat');
//sielarchivonoexisteentonceslocrea
//sielarchivoexisteentonceslovacia
rewrite(arch);

El prximo paso es leer valores por teclado (finalizando con cero) y grabarlos en el archivo.
Para esto utilizamos la funcin write. Esta funcin recibe como parmetro la variable de
archivo (arch) y el valor que queremos grabar (v). Obviamente el valor tiene que ser del
mismo tipo del archivo. Como este archivo es de integer entonces lo que vayamos a grabar en
l tiene que ser de tipo integer tambin.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:

//leemosunvalorporteclado(tipointeger)
read(v);
//finalizamoslacargadedatosconcero
while(v<>0)dobegin
//escribimosenelarchivoelvalorleido
write(arch,v);
//leemoselsiguientevalor(porteclado)
read(v);
end;

Por ltimo es necesario cerrar el archivo. El archivo se abre y se cierra. Para cerrarlo
utilizamos la funcin close.
1:
2: //cierraelarchivo
3: close(arch);
4: end.
5:

Leer un Archivo
Veamos ahora el cdigo de un programa que lee el archivo SALIDA.dat (generado en el
problema anterior) y muestra por pantalla su contenido.
leerArchivo.pas
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:

type
FInteger=fileofinteger;
var
arch:FInteger;
v:integer;
begin
//relacionalavariabledearchivo
//conelarchivofisicoSALIDA.dat
assign(arch,'SALIDA.dat');
//abreelarchivo
reset(arch);

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

4/24

15/10/2015

HolaMundo.pascal(online):Captulo6
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:

//mientrasnollegueelfindearchivo(eof)
while(NOTeof(arch))dobegin
//leeelarchivoyasignaelvalorleido
//alavariablev(queesdelmismotipo
//delarchivo:integer)
read(arch,v);
//muestroelvalorleido
writeln(v);
end;
//cierraelarchivo
close(arch);
end.

Anlisis
Analizando el cdigo anterior vemos lo siguiente. Para abrir el archivo (luego del assign)
utilizamos la funcion reset. Esta funcin abre para lectura/escritura el archivo pero no borra
su contenido.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:

type
FInteger=fileofinteger;
var
arch:FInteger;
v:integer;
begin
//relacionalavariabledearchivo
//conelarchivofisicoSALIDA.dat
assign(arch,'SALIDA.dat');
//abreelarchivo
reset(arch);

A continuacin iteramos mientras no llegue el fin del archivo. Para esto utilizamos la funcin
eof ("end of file") que retorna true al momento de leer el ltimo registro. Dentro de while
leemos con la funcin read. Esta funcin recibe la variable de archivo (arch) y una variable
del mismo tipo del archivo en la cual va a asignar el valor leido (v).
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:

//mientrasnollegueelfindearchivo(eof)
while(NOTeof(arch))dobegin
//leeelarchivoyasignaelvalorleido
//alavariablev(queesdelmismotipo
//delarchivo:integer)
read(arch,v);
//muestroelvalorleido
writeln(v);
end;
//cierraelarchivo
close(arch);
end.

Veamos los diagramas para los programas grabarArchivo (izquierda) y leerArchivo (derecha).

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

5/24

15/10/2015

HolaMundo.pascal(online):Captulo6

El Registro Actual y el Fin de Archivo


Cada vez que leemos desde un archivo, la funcin read leer (retornar) el prximo registro.
Esto es posible porque internamente se maneja un puntero que apunta al siguiente registro
que se debe leer.
Cuando hacemos reset el puntero se posiciona en el primer registro. Cuando hacemos read la
funcin retorna el registro apuntado y avanza el puntero al siguiente registro.
Cuando el puntero llega a final del archivo (uno despues del ltimo) la funcin eof retorna
true.
Supongamos que tenemos el archivo NOMBRES.dat (de strings), el siguiente grfico muestra
como se va desplazando el puntero hasta llegar al end of file. En azul se destaca el valor
retornado por la funcin read.

Siguiendo el grfico paso a paso vemos que al hacer reset el puntero apunta al primer
registro. Luego, el primer read retorna 'Analia' y el puntero queda en el segundo registro. El
prximo read retorna 'Pablo' y el puntero queda en el tercer registro. El prximo read retorna
'Silvia' y el puntero pasa al fin de archivo (es decir: la funcin eof da true en ese mismo
momento).
Tenemos que tener mucho cuidado al momento de "barrer" un archivo (leerlo integramente
desde el primer registro hasta el ltimo) porque la funcin eof da true al momento de leer el
ltimo registro, no luego de leerlo.

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

6/24

15/10/2015

HolaMundo.pascal(online):Captulo6

Veamos como barrer el archivo NOMBRES.dat sin perder ningn registro.


En el siguiente grfico el programa de la izquierda est mal, no muestra el ltimo registro. El
programa de la derecha es correcto, procesa todos los registros del archivo.

El cdigo Pascal ser:


Incorrecto:
1:
2:
3:
4:
5:
6:
7:
8:
9:

//:
read(arch,nom);//leoantesdeentrar
while(NOTeof(arch))dobegin
writeln(nom);
read(arch,nom);//leoantesdecerrar
end;
//:

Leer afuera del while y volver a leer antes de cerrarlo hace que el ltimo registro no se
procese. Esto es porque al leer el ltimo registro la funcin eof retorna true y entonces el
while no vuelve a iterar.
Correcto:
11:
12:
13:
14:
15:
16:
17:
18:
19:

//:
while(NOTeof(arch))dobegin
read(arch,nom);//leonibienentra
writeln(nom);
end;
//:

Leer una nica vez adentro, al inicio del while, soluciona el problema y permite procesar
todos los registros.

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

7/24

15/10/2015

HolaMundo.pascal(online):Captulo6

El problema del eof puede resultar un poco molesto. En realidad, lo intuitivo (al menos para
mi) es que eof de true luego de leerse el ltimo registro, no al mismo momento de leerlo.
Para solucionar esto y poder leer antes de entrar y antes de finalizar el while podemos
programar una funcin (muy simple) que retorne true si lleg el eof y false si no lleg (y en
este caso que lea el registro).

El siguiente programa recorre y muestra un archivo de enteros utilizando la funcin


leerArchivo.
leerArchivoConFuncion.pas
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:

type
FInteger=fileofinteger;
//funcionleerArchivo:
//sieseofretornatrue,sinoentonces
//leeelarchivoyretornafalse
functionleerArchivo(vara:FInteger
;varbuff:integer):boolean;
begin
if(eof(a))thenbegin
leerArchivo:=true;
endelsebegin
read(a,buff);
leerArchivo:=false;
end;
end;
//programaprincipal
var
arch:FInteger;
v:integer;
fin:boolean;
begin
assign(arch,'SALIDA.dat');
reset(arch);
//leemosafueradelwhileutilizando
//lafuncionleerArchivo
fin:=leerArchivo(arch,v);
while(NOTfin)dobegin
writeln(v);
//volvemosaleerantesdecerrar
//elwhileutilizandolafuncion
fin:=leerArchivo(arch,v);
end;
close(arch);
end.

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

8/24

15/10/2015

HolaMundo.pascal(online):Captulo6

Acceso Directo a Registros


Como ya vimos, al abrir el archivo se maneja un puntero interno que apunta al primer
registro. Luego, si leemos ese registro el puntero avanza al prximo registro y as hasta llegar
al fin de archivo (eof).
Se puede identificar a los registros por la posicin que ocupan dentro del archivo y luego
posicionar el puntero para acceder y leer directamente el registro ubicado en dicha posicin.
Para esto disponemos de las siguientes funciones:
fileSize(arch) retorna la cantidad de registros que tiene el archivo
seek(arch,n) posiciona el puntero en el registro nmero n

El siguiente programa utiliza la funcionalidad de acceso directo para leer arbitrariamente los
registros del archivo PERSONAS.dat. El usuario ingresa el nmero de registro que quiere ver y
el programa lo muestra. As hasta que se ingrese un nmero de registro negativo.
testAccesoDirecto.pas
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:

type
RPersona=record
nombre:string[20];
edad:integer;
end;
FPersona=fileofRPersona;
//programaprincipal
var
arch:FPersona;
reg:RPersona;
cantReg,pos:integer;
begin
//abroelarchivoparaleerlo
assign(arch,'PERSONAS.dat');
reset(arch);
//obtengolacantidadderegistrosque
//tieneelarchivo
cantReg:=filesize(arch);
writeln('Elarchivotiene:'
,cantReg,'registros.');
//elusuarioingresaelnro.deregistro
//quequiereleer
write('IngreseNro.Registro(0,'
,cantReg1,'):');
readln(pos);
while(pos>=0)dobegin
//posicionoelpuntero
seek(arch,pos);
//leoelregistroapuntadoporelpuntero
read(arch,reg);
//muestrolainformacion
writeln(reg.nombre,',',reg.edad);
//vuelvoapediralusuarioque
//ingreseelproximonro.deregistro
write('IngreseNro.Registro(0,'
,cantReg1,'):');
readln(pos);
end;
close(arch);
end.

Notemos que la posicin de los registros se cuenta a partir de 0 (cero). Por este motivo la

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

9/24

15/10/2015

HolaMundo.pascal(online):Captulo6

funcin seek puede posicionarse entre los registros 0 y n1 (siendo n la cantidad de registros
del archivo).
Es decir: si filesize(arch) = n entonces seek(arch,0) posiciona el puntero en el primer
registro, seek(arch, 1) posiciona el puntero en el segundo registro y... seek(arch,n1)
posiciona el puntero en el ltimo registro.
Veamos el diagrama:

Ordenamiento de Archivos
Lamentablemente no hay forma de ordenar los registros de un archivo sin que esto implique
reescribir nuevamente el archivo. Es decir: ordenar un archivo implica escribirlo de nuevo.
Por esto (sobre todo para archivos grandes) el proceso de ordenamiento suele ser costoso (en
trminos de procesamiento). Debemos ser prudentes al momento de considerarlo como una
opcin vlida.

Ordenar Archivos va Arrays


Esta opcin solo es vlida para archivos pequeos y (dado que la longitud del array debe ser
definida con anticipacin) con una cantidad de registros acotada.
La idea es: definir un array de registros del mismo tipo que los registros del archivo, "subir" el
archivo al array, ordenar el array y "bajar" el array al archivo, reescribindolo.
El trmino "subir" describe la accin de cargar (en este caso) en el array los registros leidos
del archivo. El trmino "bajar" indica la accin de grabar los registros del array en el archivo.

Supongamos que el archivo PERSONAS.dat tiene a lo sumo 100 registros, entonces el


siguiente programa nos permite ordenarlo ascendentemente por el campo nombre.
Comencemos por analizar la seccin type en la que definiremos el tipo de registro, el tipo del
archivo y un tipo ARPersona como un array de registros RPersona, de 100 posiciones.

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

10/24

15/10/2015

HolaMundo.pascal(online):Captulo6
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:

type
RPersona=record
nombre:string[20];
edad:integer;
end;
FPersona=fileofRPersona;
//unarrayde100registrosRPersona
ARPersona=array[1..100]ofRPersona;

Con la seccin type definida, veamos el diagrama del algoritmo que resuelve el problema y
luego continuaremos con el resto del cdigo.

Para resolver el problema abrimos el archivo, lo subimos al array con la funcin subirArchivo,
ordenamos el array con el procedimiento ordenar, vaciamos el archivo con rewrite y bajamos
al archivo el contenido del array (con el procedimiento bajarArchivo).
Notemos que la funcin subirArchivo retorna un entero (len) que indica la cantidad de
elementos tiles que se insertaron en el array.
Que el array sea de 100 elementos no significa que los 100 elementos estn siendo utilizados.
Lo definimos de 100 porque ese es el nmero mximo de registros que puede tener el archivo
(segn el enunciado). Pero si el archivo tiene 20 registros entonces len (el valor de retorno de
la funcin) ser 20.

Ahora si, continuemos con el cdigo del programa.


function subirArchivo
13:
14:
15:
16:
17:
18:
19:

functionsubirArchivo(vara:FPersona
;vararr:ARPersona):integer;
varreg:RPersona;i:integer;
begin
i:=0;
while(NOTeof(a))dobegin

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

11/24

15/10/2015

HolaMundo.pascal(online):Captulo6
20:
21:
22:
23:
24:
25:
26:
27:

read(a,reg);
i:=i+1;
arr[i]:=reg;
end;
subirArchivo:=i;
end;

La funcin recorre el archivo (lo recibe abierto), asigna al array en la posicin i el registro
leido (reg) e incrementa el valor de i. Es decir que al finalizar el while, en i tendremos la
cantidad de registros que se insertaron en el array. Por eso el valor de retorno ser i.
procedure ordenar
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:

procedureordenar(vararr:ARPersona;len:integer);
varordenado:boolean;i:integer;aux:RPersona;
begin
ordenado:=false;
while(NOTordenado)dobegin
ordenado:=true;
fori:=1tolen1dobegin
if(arr[i+1].nombre<arr[i].nombre)thenbegin
aux:=arr[i];
arr[i]:=arr[i+1];
arr[i+1]:=aux;
ordenado:=false;
end;
end;
end;
end;

Simplemente es el procedimiento de la burbuja adaptado para el tipo de datos de los


elementos del array (RPersona).
procedure bajarArchivo
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:

procedurebajarArchivo(vara:FPersona
;vararr:ARPersona
;len:integer);
varreg:RPersona;i:integer;
begin
fori:=1tolendobegin
write(a,arr[i]);
end;
end;

Recorre el array y graba en el archivo (abierto y vacio) cada uno de los registros del array.
Recordemos que tanto el array como el archivo contienen registros del mismo tipo: RPersona.
programa principal
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:

//programaprincipal
vararch:FPersona;arr:ARPersona;len:integer;
begin
//abroelarchivo
assign(arch,'PERSONAS.dat');
reset(arch);
//losuboaunarrayderegistros
len:=subirArchivo(arch,arr);
//ordenoelarray
ordenar(arr,len);
//reescriboelarchivo

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

12/24

15/10/2015

HolaMundo.pascal(online):Captulo6
75:
76:
77:
78:
79:
80:
81:
82:

rewrite(arch);
//graboenelarchivolosregistrosdelarray
bajarArchivo(arch,arr,len);
close(arch);
end.

Bsqueda Binaria sobre Archivos


Si el archivo se encuentra ordenado por el campo por el cual queremos buscar, entonces
podemos aplicar el algoritmo de la bsqueda binaria exactamente de la misma manera que lo
utilizamos para realizar bsquedas sobre arrays.

Supongamos que tenemos el archivo EMPLEADOS.dat ordenado por el campo legajo con el
siguiente formato de registro:
REmpleado = record
..legajo: integer;
..nombre: string[20];
end;
Entonces podemos analizar la funcin buscarBinArch que realiza una bsqueda binaria sobre
este archivo y retorna (en un valor entero) la posicin del registro que contiene el valor
(legajo) que estamos buscando (si existe) o un valor negativo si no existe lo que buscamos.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:

functionbuscarBinArch(vararch:FEmpleado
;v:integer
;varreg:REmpleado):integer;
vari,j,k:integer;encontrado:boolean;
begin
encontrado:=false;
//icomeinzaencero(primerposicion)
i:=0;
//japuntaalultimoregistro(recordemosque
//senumerandesdecero)
j:=filesize(arch)1;
while((i<=j)AND(NOTencontrado))dobegin
//calculolaposicionpromedio
k:=(i+j)div2;
//posicionoelpunteroyleo
seek(arch,k);
read(arch,reg);
if(reg.legajo=v)thenbegin
encontrado:=true;
endelsebegin
if(reg.legajo<v)thenbegin
i:=k+1;
endelsebegin
j:=k1;
end;
end;
end;
if(i>j)thenbegin
buscarBinArch:=1;
endelsebegin
buscarBinArch:=k;
end;
end;

Comparando con la bsqueda binaria sobre arrays las nicas diferencias son:

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

13/24

15/10/2015

HolaMundo.pascal(online):Captulo6

1 j se inicializa con la posicin del ltimo registro del archivo (menos 1 porque los registros
se numeran desde cero) y
2 Luego de calcular el valor de k hacemos un acceso directo al archivo para leer el registro
ubicado en esa posicin.
Notemos que el parmetro reg lo recibimos por referencia por lo tanto cuando leemos en el
archivo y utilizamos esta variable para que la funcin read guarde all lo que ley, los datos
leidos quedarn en la variable an despues de finalizar la funcin buscarBinArch.

Ahora veamos un programa principal que le pide al usuario que ingrese legajos y (si existen)
muestra los datos completos del registro encontrado.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:

//programaprincipal
varpos,leg:integer;arch:FEmpleado;reg:REmpleado;
begin
assign(arch,'EMPLEADOS.dat');
reset(arch);
write('Legajo:');
readln(leg);
while(leg>0)dobegin
//busquedabinariasobreelarchivo
pos:=buscarBinArch(arch,leg,reg);
if(pos>=0)thenbegin
writeln('Encontrado(',pos,'):'
,reg.legajo,','
,reg.nombre);
endelsebegin
writeln('Legajonoencontrado');
end;
write('Legajo:');
readln(leg);
end;
close(arch);
end.

Notemos que la funcin buscarBinArch retorna un entero positivo indicando en que posicin
fu encontrado el registro cuyo campo legajo es el que estamos buscando. Si la funcin
retorna un valor negativo significa que no se encontr ningn registro con ese legajo.
Si el registro se encontr entonces la misma funcin ya deja cargados los datos del registro en
la variable reg.

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

14/24

15/10/2015

HolaMundo.pascal(online):Captulo6

Indexar Archivos
Como dijimos anteriormente, no siempre es una buena idea ordenar el archivo. Y si el archivo
no est ordenado entonces no se puede realizar una bsqueda binaria sobre l. Tenemos un
problema.
La solucin consiste en crear un ndice ordenado por el campo por el cual queremos realizar la
bsqueda y luego acceder a los registros del archivo de manera directa, previa bsqueda sobre
el ndice.
El ndice se puede implementar en un array (si la cantidad de registros del archivo est
acotada) o en una lista enlazada.

Implementando el Indice sobre un Array


Veremos como implementar el ndice para el archivo EMPLEADOS.dat sobre un array
(arrIndice).
Supongamos que el archivo EMPLEADOS.dat tiene a lo sumo 100 registros. Entonces
definiremos un registro RIndice y un array (arrIndice) de 100 elementos tipo RIndice:
type
..// definimos el registro para el indice
..RIndice = record
....legajo: integer;
....posArch: integer;
..end;
..// definimos tipo para el array
..ARIndice = array [1..100] of RIndice;
var
..arrIndice: ARIndice; // definimos el array
La idea es recorrer el archivo e ir insertando registros en el array ordenados por el campo
legajo (que es el campo clave para la bsqueda), guardando en el campo posArch la posicin
que ocupa dentro del archivo el registro que tiene ese legajo.

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

15/24

15/10/2015

HolaMundo.pascal(online):Captulo6

En el ejemplo vemos que el array qued ordenado por legajo. As podremos realizar una
bsqueda binaria sobre el array para buscar (por ejemplo) el legajo nmero 271. Cuando lo
encontremos veremos que el campo posArch dir 1. Entonces si accedemos al archivo, al
registro nmero 1 habremos encontrado el registro que estbamos buscando.
Analicemos entonces la funcin indexaEmpleados que lee el archivo y genera el array ndice
sobre el que luego se podr realizar la bsqueda binaria por la clave legajo.
Primero veamos la seccin type donde definimos los tipos REmpleado (registros del archivo),
FEmpleado (tipo del archivo), RIndice (registros del array) ARIndice (tipo que define un
array de 100 RIndice).
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:

type
REmpleado=record
legajo:integer;
nombre:string[20];
end;
FEmpleado=fileofREmpleado;
RIndice=record
legajo:integer;
posArch:integer;//posicionenelarchivo
end;
ARIndice=array[1..100]ofRIndice;

Veamos ahora la funcin indexarEmpleados. Esta funcin recibe el archivo arch (ya abierto)
y el array arr (vacio, sin elementos). Retorna la cantidad de elementos tiles que se
insertaron en el array (len).
El objetivo de la funcin es recorrer el archivo arch y por cada registro del archivo ir
insertando (ordenado por legajo) registros (de tipo ARIndice) en el array arr.
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:

functionindexarEmpleados(vararch:FEmpleado
;vararr:ARIndice):integer;
varlen:integer;reg:REmpleado;regArr:RIndice;
begin
len:=0;
while(NOTeof(arch))dobegin
read(arch,reg);
regArr.legajo:=reg.legajo;
regArr.posArch:=len;
insertarOrdenado(arr,len,regArr);
end;
indexarEmpleados:=len;
end;

Como vemos, el algoritmo que resuelve la funcin es muy simple: recorre el archivo, por cada

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

16/24

15/10/2015

HolaMundo.pascal(online):Captulo6

registro del archivo asigna el legajo y la posicin en el archivo del registro leido (que coincide
con len) en un registro de tipo RIndice. Luego, utilizando la funcin insertarOrdenado
inserta ese registro en el array.

Cabe aclarar que en este caso la funcin insertarOrdenado fue modificada. El array que
recibe es de tipo ARIndice y el valor que recibe para insertar es de tipo RIndice. Y en todas
las condiciones lgicas pregunta por arr[i].legajo o valor.legajo ya que legajo es el campo
clave.
Veamos el cdigo del procedimiento insertar y de la funcin insertarOrdenado adaptados
para este problema.
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:

procedureinsertar(vararr:ARIndice
;varlen:integer
;valor:RIndice;pos:integer);
vari:integer;
begin
fori:=len+1downtopos+1dobegin
arr[i]:=arr[i1];
end;
arr[pos]:=valor;
len:=len+1;
end;
functioninsertarOrdenado(vararr:ARIndice
;varlen:integer
;valor:RIndice):integer;
vari:integer;
begin
i:=1;
while((i<=len)AND(arr[i].legajo<=valor.legajo))
dobegin
i:=i+1;
end;
if(i>len)thenbegin
len:=len+1;
arr[len]:=valor;
insertarOrdenado:=len;
endelsebegin
insertar(arr,len,valor,i);

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

17/24

15/10/2015

HolaMundo.pascal(online):Captulo6
48: insertarOrdenado:=i;
49: end;
50: end;
51:

Ahora podemos ver el programa principal. Pide al usuario que ingrese un legajo, lo busca en el
ndice y (si existe) accede directamente al archivo para leer y mostrar los datos del empleado.

El cdigo del programa principal es:


97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:

//programaprincipal
var
arch:FEmpleado;
arrIndice:ARIndice;
reg:REmpleado;
len,leg,posArr,posArch:integer;
begin
assign(arch,'EMPLEADOS.dat');
reset(arch);
len:=indexarEmpleados(arch,arrIndice);
write('IngreseLegajo:');
readln(leg);
while(leg>0)dobegin
posArr:=buscarBinIndex(arrIndice,len,leg);
if(posArr>0)thenbegin
posArch:=arrIndice[posArr].posArch;
seek(arch,posArch);
read(arch,reg);
writeln('Legajo:',reg.legajo
,'Nombre:',reg.nombre);
endelsebegin
writeln('Legajonoencontrado');
end;
write('IngreseLegajo:');

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

18/24

15/10/2015

HolaMundo.pascal(online):Captulo6
129:
130:
131:
132:
133:

readln(leg);
end;
close(arch);
end.

Inplementando el Indice sobre una Lista Enlazada


Esta tcnica la estudiaremos en el captulo de Estructuras Dinmicas, luego de analizar los
temas de punteros y listas.

Apareo de Archivos
Se considera "apareo de archivos" a los problemas que intercalan datos de dos o ms archivos
que se encuentran ordenados por la misma clave, recorrindolos simultaneamente.
Analicemos los datos de los siguientes conjuntos:

Los dos conjuntos estn ordenados en forma ascendente. Leemos el primer valor de A (lo
llamaremos a) y luego el primer valor de B (lo llamaremos b).
Si a(1) < b(2) podemos determinar que el valor a est en el conjunto A pero no en el conjunto
b, ya que como estn ordenados el siguiente valor de B ser an ms grande. Luego leemos el
siguiente valor de A a(3) y lo comparamos con b(2).
Si a(3) > b(2) entonces podemos determinar que el valor de b(2) est en el conjunto B pero no
en el conjunto A. Ahora leemos B b(3).
Si a(3) = b(3) entonces el valor se encuentra en ambos conjuntos. En este caso leemos A y B y
as sucesivamente hasta que finalice alguno de los dos conjuntos.
Si un conjunto (A) termina antes que el otro (B) podemos asegurar que todos los elementos
sobrantes (de B) no estarn contenidos en el otro conjunto (A).
Luego de este anlisis podemos plantear un enunciado que le de sentido a lo anterior.

Problema 6.1
Se tienen los archivos PRESTA_2006.dat y PRESTA_2007.dat, con la informacin
correspondiente a las prestaciones que realizaron los diferentes mdicos de una clnica. Ambos
archivos estn ordenados por id_medico y tienen los siguientes campos:
id_medico (5 dgitos)
cantidad (4 dgitos)
Existe la posibilidad de que durante el ao 2007 se hayan incorporado mdicos que no
trabajaron durante 2006. Tambin pueden haber mdicos que trabajaron durante 2006 pero no
durante 2007.
Se Pide

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

19/24

15/10/2015

HolaMundo.pascal(online):Captulo6
1. Un listado de los mdicos que se incorporaron durante 2007 (no sern ms de 100).
2. Un listado de los mdicos que solo trabajaron durante 2006 (no sern ms de 100).
3. Un listado indicando los mdicos que incrementaron la cantidad de prestaciones de un
ao al otro.

Anlisis
La idea del apareo de archivos es recorrer simultaneamente ambos archivos comparando su
campo clave (por el cual estn ordenados). Si encontramos un registro en PRESTA_2006 que no
est en PRESTA_2007 corresponder a un mdico que no trabaj en 2007. Si encontramos un
registro en PRESTA_2007 que no est en PRESTA_2006 entonces ese mdico se incorpor en
2007. Si encontramos un mismo id_medico en ambos archivos entonces ese mdico trabaj en
2006 y 2007.
Veamos el siguiente ejemplo (los datos de la izquierda corresponden a PRESTA_2006. Los de la
derecha corresponden a PRESTA_2007):

Leemos ambos archivos y comparamos su id_medico. Si coinciden entonces ese id_medico


corresponde a un mdico que trabaj en los aos 2006 y 2007. Leemos nuevamente en los dos
archivos:

Ahora id_medico en PRESTA_2006 es mayor que id_medico en PRESTA_2007. Evidentemente


el mdico con id_medico=2 se incorpor en 2007. En este caso leemos solamente
PRESTA_2007.

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

20/24

15/10/2015

HolaMundo.pascal(online):Captulo6

Ahora en los dos archivos tenemos registros con el mismo id_medico (3). Este mdico
tambin trabaj los dos aos. Volvemos a leer ambos archivos.

En este caso id_medico en PRESTA_2006 es menor que id_medico en PRESTA_2007. Esto


significa que el mdico con id_medico=4 no trabaj en 2007. En este caso solo leemos
PRESTA_2006 y as hasta el fin de datos de alguno de los dos archivos.
Si llega el fin de datos de PRESTA_2006 significa que todos los registros que quedan por leer en
PRESTA_2007 corresponden a mdicos que se incorporaron ese ao. Pero si llega primero el eof
de PRESTA_2007 entonces todos los registros que quedan sin leer en PRESTA_2006
corresponden a mdicos que dejaron de trabajar.

La estrategia de solucin ser la siguiente: tendremos dos arrays (arr06 y arr07) de 100
elementos cada uno. En arr06 iremos guardando los mdicos que solo trabajaron en 2006
(punto 2 del enunciado). En arr07 iremos guardando los mdicos que se incorporaron a
trabajar en 2007 (punto 1 del enunciado). Los mdicos que trabajaron los dos aos e
incrementaron la cantidad de prestaciones (punto 3 del enunciado) los listaremos a medida que
los encontremos.
Veamos la seccin type para definir los tipos de datos.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:

type
//registrodelosarchivos
RPresta=record
id_medico:longint;
cantidad:integer;
end;
//tipoparalosarchivos
FPresta=fileofRPresta;

//tipoparalosarraysdemedicos
AMed=array[1..100]oflongint;

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

21/24

15/10/2015

HolaMundo.pascal(online):Captulo6

En el programa principal resolvemos el apareo de archivos. Leemos PRESTA_2006 y


PRESTA_2007 e iteramos mientras no llegue el fin de archivo de alguno de los dos.
Comparamos reg06.id_medico con reg07.id_medico:
Si reg06.id_medico = reg07.id_medico entonces tenemos que comparar las
prestaciones que realiz ese mdico durante 2006 y 2007. Si las de 2007 fueron ms
entonces lo imprimimos para resolver el punto 3 del eununciado. Luego volvemos a
leer los dos archivos.
Si reg06.id_medico > reg07.id_medico entonces el mdico apuntado en
PRESTA_2007 es nuevo, se incorpor ese ao. Almacenamos su id_medico en el array
arr07.
Si reg06.id_medico no es mayor que reg07.id_medico entonces el mdico apuntado
en PRESTA_2006 no trabaj durante 2007. Almacenamos su id_medico en el array
arr06.
Por ltimo, preguntamos cual de los dos archivos termin. Si termino PRESTA_2006 tenemos
que recorrer PRESTA_2007 hasta el final agregando todos los registros que quedaron en arr07
ya que todos fueron mdicos que se incorporaron ese ao. Si el que termin primero fue
PRESTA_2007 entonces recorremos hasta el final PRESTA_2006 agregando todos los registros
que quedarn en arr06 porque todos corresponden a mdicos que dejaron de trabajar.

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

22/24

15/10/2015

HolaMundo.pascal(online):Captulo6

Algoritmos y Estructuras de Datos UTN UBA.


Publicado por PabloSZ

3 comentarios:
Annimo dijo...
Excelente tu trabajo en toda la pgina. Estoy cursando Algoritmos en la UTN y me
recomendaron tu pgina, la verdad me aclaraste muchas dudas con el tema de
archivos.
Hay algunos errores en los diagramas, lo cual lo veo muy til (apropsito o no) porque
me obliga a pensar y analizar si estn bien, y no nicamente a leer de corrido cada
ejercicio.
Un abrazo y segu con la pgina!
03 junio, 2008 23:28
Sebastian dijo...
Pablo veo en la parte de Indexacin de Archivos que usas un array para almacenar
DOS campos:
1 legajo
2 posArch
Luego usas ese Array para todo el ejemplo.
No tendras que usar una matriz de 2x100?
Creo que ahi tenes un error porque por mas que declares al array con un registro de
dos variables no las podes meter. O si?
Me agarro la duda...
Gracias
12 septiembre, 2008 18:33

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

23/24

15/10/2015

HolaMundo.pascal(online):Captulo6
Annimo dijo...
ola tengo una consulta...puede ser que solo pueda subir 2 campos de un registro..o sea
no el registro entero al array....a ver si me explico por ejmplo tengo
descripcion,monto,fecha,nombre en un registro de archivo..y yo solo kiero subir
descripcion y monto al array se usa la el mismo procedimiento de "subir archivo"?
25 septiembre, 2008 12:05

Publicar un comentario en la entrada

Entrada ms reciente

Pgina principal

Entrada antigua

Suscribirse a: Enviar comentarios (Atom)

Todos los Derechos Reservados Propiedad Intelectual Nro. 591212

http://holamundopascal.blogspot.mx/2007/08/capitulo6.html

24/24

Вам также может понравиться