Delphi, El Hijo de Pascal
Delphi, El Hijo de Pascal
Delphi, El Hijo de Pascal
EXPERTOS PROGRAMACIÓN
Figura 1. Ésta es la pantalla principal Figura 2. Aquí está nuestro primer Figura 3. Aquí vemos cómo quedaría la
de Delphi. Como se ve, no es muy programa. En una de esas, Bill nos interfase gráfica de la calculadora.
diferente de otros programas de compra los derechos para incluirlo en su Obviamente, podemos hacerla como más
lenguaje visual, por lo cual no nos próximo Windows. nos guste.
resultará difícil familiarizarnos con él.
76 u s e r s . t e c t i m e s . c o m
ProgramacionU#117.qxd 4/28/01 6:07 PM Page 77
Si no tenemos Delphi,
podemos bajarlo de la
página oficial,
www.borland.com/downloads, donde
hay una versión trial de
30 días de duración.
como cadena, donde podremos indexar un carácter específico, Nuestra propia calculadora
lo que hace mucho más flexible el trabajo sobre este tipo de Ahora comenzaremos a programar una aplicación un poco
datos. más útil y, de esta forma, podremos adentrarnos un poco más
También encontraremos otros tipos de datos, que podremos en el lenguaje Delphi. Haremos una calculadora con las fun-
utilizar en nuestras aplicaciones y que desarrollaremos a me- ciones básicas, para lo cual la interfase gráfica quedará como
dida que avancemos en nuestra aplicación de ejemplo (se de- nos muestra la Figura 3, con 16 botones y un objeto memo
tallan en la Tabla 2). En esta ocasión, construiremos una (debido a que nos permite alinear a la derecha), alineándolo
sencilla calculadora. con la propiedad Align.
El manejo de las constantes es similar que en los demás len- Debemos tener en cuenta un par de cosas antes de empezar,
guajes, pero además, podremos usar constantes del tipo ty- como que hay que mantener el primer operando en el display
ped, que nos permitirán utilizar estructuras de datos (vecto- hasta ingresar el otro, no perder el último operando para ha-
res, registros, punteros, etc.) como constantes. cer que la tecla <=> funcione consecutivamente, etc., pero
iremos viendo todo esto a medida que creemos las funciones.
El clásico mensaje Debido a que necesitaremos muchas comprobaciones (posi-
Para familiarizarnos y perderle el miedo a Delphi, empezare- bles acciones por parte del usuario), deberemos definir algu-
mos con el típico ejemplo del mensaje. En este caso, usamos nos flags o banderas que nos sirvan de guía. La Tabla 3 mues-
el clásico “Hello World” u “Hola mundo“, que aparecerá cuan- tra las variables que usaremos y definiremos como globales,
do presionemos un botón. o sea que se declaran fuera de los procedimientos (como en
1) Creemos, dentro de un form vacío, sólo un botón, con el VB, pero sin la palabra Public) y debajo de la palabra reser-
nombre botón y el caption El clásico mensaje. El caption se- vada VAR.
rá el texto que aparecerá en él. Como toda buena calculadora, debe inicializarse con el 0,
2) Ahora hacemos doble clic para dirigirnos al código del por lo cual en el evento formcreate de nuestra form escribi-
evento clic. remos:
3) Escribimos el siguiente código: display.Text := ‘0’; {asignamos la cadena ‘0’ al
begin {El Begin y End estan escritos por de-
fault} Tabla 1: Parámetros
❙
u s e r s . t e c t i m e s . c o m 77
ProgramacionU#117.qxd 4/28/01 6:07 PM Page 78
EXPERTOS PROGRAMACIÓN
end;
Éstos son otros tipos de datos útiles que podemos utilizar en nuestras aplicacio-
nes. function realtotex (num:real):
string;
Tipo de dato Descripción Ejemplo var
Enumerado Dato de definición de valores Type colores = (blue,
green, red) aux: variant;
Puntero Dato de dirección de memoria Type pchar=^char begin
Subrango Dato definido en un rango I := 0..99 aux:= num; {del real al
ordinal de un tipo de dato. I := ‘A’..’Z’ variant}
realtotex:= aux; {del variant
78 u s e r s . t e c t i m e s . c o m
ProgramacionU#117.qxd 4/28/01 6:07 PM Page 79
LISTADO*: flagcons:=false;
flagop:=true
procedure opera (var operador:char; end;
operacion_actual:char ; display:TMemo; var end;
flagop,flagcons:boolean; var res:real);
begin ‘/’:
{operador: último operador utilizado} begin
{operación_actual: el nuevo operador} if flagcons =false then
begin
case operador of nume1:= textoreal(display.text);
‘+’: res:= suma(res,nume1);
begin display.text:= realtotex (res);
if flagcons =false then flagop:=true
begin end
nume1:= textoreal(display.text); else
{cambiamos de tipo de dato} flagcons:=false;
res:= suma(res,nume1); {acumulamos el resultado} flagop:=true
display.text:= realtotex (res); {mostramos el end;
acumulador}
flagop:=true else {si no había ningún operador presionado...}
end begin
else if flagcons =false then
begin begin
flagcons:=false; flagcons:=false;
flagop:=true operador :=operacion_actual;
end; if flagpor=true then
end; begin
res:= textoreal(display.text);
‘-’: flagpor:=false;
begin flagop:= true
if flagcons =false then end
begin else
nume1:= textoreal(display.text); begin
res:= resta(res,nume1); case operacion_actual of
display.text:= realtotex (res); {verificamos cuál es la operación actual}
flagop:=true ‘*’:
end begin
else nume1:= textoreal(display.text);
begin res:= producto(res,nume1);
flagcons:=false; display.text:= realtotex (res);
flagop:=true flagop:=true
end; end;
end; ‘/’:
begin
‘*’: nume1:= textoreal(display.text);
begin res:= cociente(res,nume1);
if flagcons =false then display.text:= realtotex (res);
begin flagop:=true
nume1:= textoreal(display.text); end;
res:= producto(res,nume1); ‘+’:
display.text:= realtotex (res); begin
flagop:=true nume1:= textoreal(display.text);
end res:= suma(res,nume1);
else display.text:= realtotex (res);
begin sigue en página 80
u s e r s . t e c t i m e s . c o m 79
ProgramacionU#117.qxd 4/28/01 6:08 PM Page 80
EXPERTOS PROGRAMACIÓN
viene de página 79 nes con los parámetros correctos, y asunto terminado. Reco-
mendamos hacer la prueba de escritorio (seguimiento de la
flagop:=true
función de las variables con papel y lápiz) para que sea más
end;
comprensible, ya que explicar cada declaración nos llevaría
‘-’:
unas cuatro o cinco revistas, porque el procedimiento contem-
begin
pla todas las posibles acciones por parte del usuario.
nume1:= textoreal(display.text);
res:= cociente(res,nume1);
Con este procedimiento resolvemos el núcleo del programa.
display.text:= realtotex (res);
Lo agregamos a cada operación, y misión cumplida.
flagop:=true
begin
end;
opera(operador,’+’,display, flagop, flagcons, res);
end;
end;
end;
end;
Ahora nos falta asociar el código del botón [CE], que sólo
end;
inicializa los flags y resetea el display.
end;
procedure Tcalcu.resetClick(Sender: TObject);
operador:= operacion_actual; {actualizamos el
begin
operador}
operador:= ‘ ‘; {no hay operaciones asocia-
end;
das}
res:=0; {el resto se resetea}
display.Text := ‘0’; {reseteamos el display}
al texto} flagpor:= true;
end; flagcons:=false;
El procedimiento general end;
Como buenos programadores, debemos encontrar la generali-
dad en los procedimientos de suma, resta, multiplicación y Una vez terminado todo, estamos listos para compilar. Para
división. No es muy difícil, porque todos actúan de la misma ello utilizamos [Project/Compile], y automáticamente se
manera. Después de ingresar dos operandos, devuelven un re- creará el archivo EXE de nuestra aplicación.
sultado y listo. Lo único que debemos tener en cuenta son las Antes de esto, podemos ponerle algún fondo o personalizar-
posibles combinaciones de teclas que el usuario puede presio- la de la manera que queramos. Ya estamos preparados para
nar. A continuación mostramos el código de este procedimien- usar nuestra propia calculadora hecha en Delphi.
to; bastará con incluirlo en cada evento Clic de las operacio-
La proyección Delphi
Si bien Delphi no ha sido un lenguaje muy difundido o utili-
Tabla 3: Variables del ejemplo zado bajo entorno Windows como lo fue el famoso y popular
❙
Visual Basic, Linux lo vio con otros ojos. Su nueva versión del
Éstas son las variables que usaremos como globales para clásico sistema operativo, llamada Kirix, incluye gran parte de
la aplicación. código de Delphi para las tareas de administración de proce-
sos y de memoria. Los programadores y expertos en Linux ase-
Nombre Tipo Uso guran que “un buen programador debe saber programar en
Nume1 Real Operando auxiliar para los
cálculos. Delphi”.
Operador Char Indica de qué operación se Entre otras cosas, Delphi se está orientando a la programa-
trata, +, -,* o /. ción de sitios web dinámicos, lo que le abre el paso para com-
EFlagcons Boolean Indica si se presionó el ‘=’ más petir con los lenguajes que están dominando esta área, como
de una vez. PHP, Pearl o ASP.
Resultado Variant Variable utilizada para cambios
de tipo de datos. Por lo visto, ahora es muy necesario comenzar a entender
ERes Real Acumulador de resultados. Delphi, ya que parece ser un lenguaje prometedor que, en
Flagpor Boolean Indica que se ha presionado el
igual (=) y que se espera el primer
operando.
En el CD
EFlagpunto Boolean Indica que se ha ingresado un
“.” para no repetirlo.
P ara que no tengas que copiar todo el código de los
ejemplos, y para no tener problemas de errores,
Flagop Boolean Indica si se ingresó el primer éste fue incluido en la carpeta PROGRAMACIÓN del
operando para mantenerlo en
el display hasta que se ingrese CD que acompaña la revista.
el otro. Además, ¡también encontrarás la calculadora fun-
cionando!
80 u s e r s . t e c t i m e s . c o m
ProgramacionU#118.qxd 4/28/01 4:56 PM Page 76
EXPERTOS PROGRAMACIÓN
Estructuras de datos
Ya dimos los PRIMEROS PASOS en este lenguaje al
crear nuestra propia calculadora en Delphi.
Ahora COMIENZA LO INTERESANTE: haremos la
Alex VALENCIA batalla naval utilizando ESTRUCTURAS DE DATOS,
avalencia@tectimes.com
Estudiante de Ciencias de la Computación,
una de las cosas más importantes que emplearemos
fanático del anime y la programación. para cualquier programa.
Figura 1. Así quedaría el tablero Figura 2. Éste es el code explorer. Figura 3. Éste es el objeto Timer, muy
principal del juego. Ésta es una opción, Cuando lo abrimos, lo tenemos al lado útil para cualquier cosa que hagamos.
pero podemos hacerlo como queramos. de la form, de modo de acceder a él Sólo se ve en tiempo de diseño.
rápidamente y que no se nos escapen las
ideas.
76 u s e r s . t e c t i m e s . c o m
ProgramacionU#118.qxd 4/28/01 4:56 PM Page 77
u s e r s . t e c t i m e s . c o m 77
ProgramacionU#118.qxd 4/28/01 4:56 PM Page 78
EXPERTOS PROGRAMACIÓN
78 u s e r s . t e c t i m e s . c o m
ProgramacionU#118.qxd 4/28/01 4:56 PM Page 79
LISTADO 1: end;
end;
procedure codigo_espacio (cordenada:string; Can- end;
t_espacios:TLabel; but_barcos, but_lanchas,but- end;
_submarinos:TButton; box:TCheckBox);
var {código para lancha}
auxint: integer; if lancha= true then
str:string; begin
begin marcar(mat,x,y);
cordalfatocordnum(cordenada,y,x); {cambia de coor- descontar_en_string(cant_espacios,1);
denadas alfanuméricas a numéricas} habilitar_botones (but_barcos,but_lanchas-
if marcado (mat,y,x) = false then {verifica que ,but_submarinos);
esté marcado} end;
begin
if (cant_espacios.Caption <> ‘’) and (cant_es- {código para submarino}
pacios.Caption <> ‘0’) then
begin if submarino= true then
{Código para el barco} begin
if barco= true then if cant_espacios.caption = ‘3’ then
begin begin
if cant_espacios.caption = ‘2’ then registrar_disponibles (posiciones_libres-
begin ,mat,x,y);
registrar_disponibles (posiciones_libres- marcar(mat,x,y);
,mat,x,y); descontar_en_string(cant_espacios,1);
{registra las posiciones posibles end
para el 2º espacio} else
marcar(mat,x,y); {marca el espa- begin
cio} if cant_espacios.caption=’2’ then
descontar_en_string(cant_espacios,1); begin
end if bien_posicionado(posiciones_li-
else bres,x,y) = true then
begin begin
if cant_espacios.caption=’1’ then marcar(mat,x,y);
begin descontar_en_string(cant_espacios,1);
if bien_posicionado(posiciones_li- determinar_posiciones(vector_dos_po-
bres,x,y) = true then siciones,posiciones_libres,mat,x,y); {devuelve las
begin dos posiciones posibles en los extremos para el 3º
marcar(mat,x,y); espacio del submarino}
descontar_en_string(cant_espacios,1); end
habilitar_botones (but- else
_barcos,but_lanchas,but_submarinos); begin
end if flagmens =true then
else begin
begin Application.Message-
if flagmens =true then Box(‘debe marcar un casillero contiguo’,’Error de
begin marcado’,MB_OK);
Application.MessageBox(‘debe mar- flagmens:=false;
car un casillero contiguo’,’Error de marcado’,M- box.checked:=false;
B_OK); end;
flagmens:=false; flagmens:=true;
{‘box’ es un objeto Tcheckbox pasado por paráme- end;
tro de entrada/salida} end
box.checked:=false; else
end; begin
flagmens:=true; sigue en página 80
u s e r s . t e c t i m e s . c o m 79
ProgramacionU#118.qxd 4/28/01 4:56 PM Page 80
EXPERTOS PROGRAMACIÓN
viene de página 79
if cant_espacios.caption=’1’ end;
then flagmens:=true;
begin end;
if bien_posicionado_dos_pos(vector- end;
_dos_posiciones,x,y)=true then end;
begin
marcar(mat,x,y); end;
descontar_en_string(cant_espacios,1); end;
habilitar_botones (but_barcos- end
,but_lanchas,but_submarinos); else
end begin
else box.checked:=false
begin end;
if flagmens =true then end
begin else
Application.MessageBox(‘debe mar- begin
car un casillero contiguo’,’Error de marcado’,M- box.checked:= true;
B_OK); end;
flagmens:=false; end;
box.checked:=false;
Algunas cosas importantes modificar. Para más detalles, observen la Tabla 3, que explica
Noten que en este ejemplo utilizamos dos matrices de cuánto ocupa cada tipo de dato en memoria.
10 x 10 posiciones booleanas. A simple vista, parece una Este tipo de aspectos, a los que si tenemos una máquina
barbaridad, pero el espacio que ocupa un dato booleano en grande generalmente no les damos importancia, resultan
memoria es de un solo byte (8 bits). Por lo tanto, la matriz fundamentales para que nuestro código sea transportable a
completa ocupa sólo cien bytes (mucho menos de lo que la mayor cantidad de máquinas posible. Quizás en un siste-
ocupa un dato de tipo string). ma pequeño no influya, pero sí en sistemas que manejen un
Es importante fijarse bien en el tipo de datos que guardamos volumen mayor de datos.
en las variables. Por ejemplo, en los vectores que usamos pa- Otra característica importante para resaltar es la función del
ra registrar las posiciones de la máquina, lo más grande que se objeto Timer (Figura 3), que es sumamente útil para cualquier
puede guardar es el par de coordenadas 10,10 (j10 en coorde- aplicación que se nos ocurra hacer. Este objeto cumple la fun-
nadas alfanuméricas), por lo cual no se justifica la utilización ción de temporizador, que, a partir de cierto tiempo de acti-
de un vector de tipo string completo que ocupe 256 bytes por vación, ejecuta la porción de código que le asignemos al even-
posición. Un vector indexado de cuatro posiciones (que ocupa to. En este caso lo usamos para separar un tiempo entre los
sólo 4 bytes) es más que suficiente para los datos que va a tiros del usuario y los de la máquina.
guardar, ya que un string no es más que una cadena de carac- OK. Todo listo para incluir nuestra batalla naval en los jue-
teres, que, por ser un tipo de datos encapsulado, no podemos gos de Windows, así que prepárense para fundar su propia em-
presa de juegos bajo Delphi.
80 u s e r s . t e c t i m e s . c o m
ProgramacionU#118.qxd 4/28/01 4:56 PM Page 82
EXPERTOS PROGRAMACIÓN
LISTADO 2:
marcar (mat,x,y);
{registramos en un vector los bar-
procedure armar_enemigo(var mat:matriz);
cos que puso}
var
barcos_ene[i,1]:= inttostr(x)+inttostr(y)
i,x,y:integer;
end
begin
else
randomize; {procedimiento que usamos para
begin
inicializar el random}
randomize;
for i:=1 to 5 do
x:=random(11);
begin
if x = 0 then
x:=random(11); {random de 0 hasta 10; es-
x:=x+1;
ta función viene con Delphi}
y:=random(11);
if x = 0 then
if y = 0 then
x:=x+1;
y:=y+1;
y:=random(11);
marcar (mat,x,y);
if y = 0 then
barcos_ene[i,1]:= inttostr(x)+inttostr(y);
y:=y+1;
end;
{calculamos los espacios posibles
if marcado(mat,y,x) = false then {¿no estaba
para el 2º espacio}
esta lancha?}
registrar_disponibles(posiciones_libres,mat,x,y);
begin
{Este procedimiento hace un random
marcar (mat,x,y);
de 1 a 8 para determinar la dirección de
{registramos en un vector las lan-
la 2º posición}
chas que puso}
llenar_segundo_espacio(posiciones_libres,x,y);
lanchas_ene[i]:= inttostr(x)+inttostr(y)
if marcado (mat,y,y) <> true then
end
begin
else
marcar (mat,x,y);
begin
barcos_ene[i,2]:= inttostr(x)+inttostr(y)
randomize;
end
x:=random(11);
else
if x = 0 then
begin
x:=x+1;
registrar_disponibles(posiciones_libres,mat,x,y);
y:=random(11);
llenar_segundo_espacio(posiciones_libres,x,y);
if y = 0 then
marcar(mat,x,y);
y:=y+1;
barcos_ene[i,2]:= inttostr(x)+inttostr(y);
marcar (mat,x,y);
end;
lanchas_ene[i]:= inttostr(x)+inttostr(y);
end;
end;
{llenamos los submarinos}
for i:=1 to 2 do
end;
begin
x:=random(11);
{llenamos los barcos}
if x = 0 then
for i:=1 to 3 do
x:=x+3;
begin
y:=random(11);
x:=random(11);
if y = 0 then
if x = 0 then
y:=y+3;
x:=x+2;
y:=random(11);
if marcado(mat,y,x) = false then
if y = 0 then
begin
y:=y+2;
marcar (mat,x,y);
submarinos_ene[i,1]:= inttostr(x)+inttostr(y)
if marcado(mat,y,x) = false then
end
begin
82 u s e r s . t e c t i m e s . c o m
ProgramacionU#118.qxd 4/28/01 4:56 PM Page 83
else llenar_segundo_espacio(posiciones_libres,x,y);
begin marcar(mat,x,y);
randomize; submarinos_ene[i,2]:= inttostr(x)+inttostr(y);
x:=random(11); end;
if x = 0 then determinar_posiciones(vector_dos_posiciones,posi-
x:=x+3; ciones_libres,mat,x,y);
y:=random(11); llenar_tercer_espacio(vector_dos_posiciones,x,y);
if y = 0 then if marcado (mat,y,x) <> true then
y:=y+3; begin
marcar (mat,x,y); marcar (mat,x,y);
submarinos_ene[i,1]:= inttostr(x)+inttostr(y); submarinos_ene[i,3]:= inttostr(x)+inttostr(y)
end; end
registrar_disponibles(posiciones_libres,mat,x,y); else
llenar_segundo_espacio(posiciones_libres,x,y); begin
if marcado (mat,y,x) <> true then determinar_posiciones(vector_dos_posiciones,posi-
begin ciones_libres,mat,x,y);
marcar (mat,x,y); llenar_tercer_espacio(vector_dos_posiciones,x,y);
submarinos_ene[i,2]:= inttostr(x)+inttostr(y) marcar(mat,x,y);
end submarinos_ene[i,3]:= inttostr(x)+inttostr(y);
else end;
begin end;
registrar_disponibles(posiciones_libres,mat,x,y); end;
u s e r s . t e c t i m e s . c o m 83
ProgramacionU#119.qxd 4/28/01 7:26 PM Page 84
EXPERTOS PROGRAMACIÓN
¡¡¡TIEMBLA WORD!!!
Archivos en Delphi
Ya les tomamos la mano a las variables y probamos las
estructuras de datos bajo DELPHI. Estamos preparados
para dar el próximo paso hacia los ARCHIVOS, que nos
Alex VALENCIA permitirán crear APLICACIONES MÁS
avalencia@tectimes.com
Estudiante de Ciencias de la Computación,
PROFESIONALES y más útiles. Comenzaremos con los
fanático del anime y la programación. de texto hasta llegar a los binarios.
Figura 1. Nace nuestro nuevo editor, Figura 2. Aquí configuramos los filtros Figura 3. Ésta es una herramienta muy
creado por nosotros mismos. Así podría de archivos que queremos leer, con las sencilla para crear un menú;
ser la primera fachada que le demos. famosas wildcards (*.txt,*.doc, etc.), simplemente le ponemos un nombre y le
Sean creativos con la interfase. para encontrar fácilmente cualquier asociamos código al seleccionarlo.
archivo. El código se ejecutará según los eventos.
84 u s e r s . t e c t i m e s . c o m
ProgramacionU#119.qxd 4/28/01 7:26 PM Page 85
Cambiando WordPad por FastPad rramos o modificamos (ABM), ya que no es otra cosa que una
FastPad es el nombre de nuestro programa, que reunirá las base de datos de archivos. Cuando abrimos el archivo, la FAT
opciones que más utilizamos en WordPad ([Abrir], [Guardar], nos dice en qué posición del disco empieza para poder comen-
[Guardar como], [Buscar] e [Imprimir]) en cuatro botones zar a usarlo.
y un menú que contendrá las mismas funciones. Les recomien- Para los fines de la programación, en el momento en que
do que, a partir de esto, vayan agregándole cosas para ver las abrimos un archivo de texto, éste se convierte en una varia-
distintas herramientas que Delphi ofrece. ble del tipo textfile (en el caso particular de Delphi), y a par-
En nuestro form principal ubicaremos los objetos que descri- tir de ahí podremos trabajar con ella como lo hacemos con
be la Tabla 1, con los que nos quedará algo como lo que cualquier otra variable.
muestra la Figura 2. Una vez hecho esto, estamos listos para Hay varias formas de abrir un archivo, pero las más impor-
comenzar. tantes son dos: lectura o escritura. Existe una tercera, que es
para lectura y escritura, pero como ven, desciende de las an-
Abrir un archivo teriores. En la Tabla 2 se indican las sintaxis y las maneras de
En este programa hay dos funciones fundamentales que real- abrir archivos.
mente nos importan: Abrir y Guardar, ya que con ellas abriremos
archivos, ya sea para escribir o para leer, y los almacenaremos. Abriendo un archivo para lectura
Para trabajar con un archivo debemos abrirlo, lo que signi- Ahora nos encargaremos de abrir nuestro archivo de texto
fica buscar el comienzo de éste y empezar a usarlo. Cuando para leerlo. Como habrán imaginado, esta acción va asociada
nosotros pedimos, por ejemplo, el directorio de una carpeta, a la función Abrir de nuestro programa. Como no queremos
lo que vemos no es más que un reflejo de lo que la FAT tiene que nuestro programa sea menos que cualquier otro de Win-
almacenado. La máquina sólo toca la FAT cuando creamos, bo- dows, usaremos para esto un objeto del tipo TopenDialog, que
u s e r s . t e c t i m e s . c o m 85
ProgramacionU#119.qxd 4/28/01 7:26 PM Page 86
EXPERTOS PROGRAMACIÓN
86 u s e r s . t e c t i m e s . c o m
ProgramacionU#119.qxd 4/28/01 7:26 PM Page 87
SelStart := FoundAt; Como siempre, les aconsejo que sigan ampliando esta apli-
SelLength := Length(dialogo_buscar.FindText); cación para poder conocer otras herramientas de Delphi. Acá
end; les tiro algunas sugerencias:
end;
end; 1) Agregar la opción para fonts.
2) Crear la personalización de la impresión (TPrintDialog).
Eso es todo para la función Buscar. La función Imprimir es 3) Crear la opción de reemplazar.
mucho más fácil; sólo debemos asociar esta línea al botón 4) Poder agregar la opción para copiar al clipboard.
[Imprimir], y listo:
begin Algo más acerca de archivos
editor.Print (filename); {función para imprimir el Si bien usamos un procesador de textos para poder enten-
archivo pasado por parámetro} der el funcionamiento de un archivo, los archivos manejan
end; todo el ámbito de nuestra máquina, no sólo en forma de
programas sino que, mirándolo desde el punto de vista de la
Obviamente, podríamos haber hecho esto también con un programación, son indispensables dentro de cualquier
objeto tipo TPrintDialog para tener aparte la opción de ele- aplicación. Por ejemplo, con ellos podemos crear flags per-
gir cantidad de copias, configuración de impresoras, etc. manentes, que es donde las aplicaciones se fijan para con-
Generalmente usamos el shortcut de impresión rápida, pero servar configuraciones. Los famosos *.ini no son más que
si creen necesaria esta opción, es muy fácil agregarla, y les archivos de texto que informan a su creador cómo fue con-
podrá servir de práctica para aprender a manejar mejor otros figurada cada aplicación.
diálogos para futuras aplicaciones. Como el objetivo de es- Por otro lado, los archivos de registro, generalmente llama-
ta nota es el manejo de archivos en particular, lo vamos a dos binarios, son la raíz donde se fundan todas las bases de
dejar ahí. datos, pero veremos este tema en el próximo número. La
Si todo salió bien, nuestro FastPad quedará como lo muestra administración de memoria “Swap” está basada en el manejo
la Figura 4. de archivos. Éstos son algunos ejemplos, en el próximo
u s e r s . t e c t i m e s . c o m 87
ProgramacionU#120.qxd 3/27/01 5:25 PM Page 78
EXPERTOS PROGRAMACIÓN
Archivos binarios
Ya estamos a pasos de sacarle el jugo a nuestro disco
rígido. Con lo que aprendimos sobre archivos de texto, sólo
abarcamos una parte de lo que podemos hacer. Ahora nos
Alex VALENCIA falta la otra mitad: LOS ARCHIVOS BINARIOS, que
avalencia@tectimes.com
Estudiante de Ciencias de la Computación,
brindarán a nuestras aplicaciones la posibilidad de trabajar
fanático del anime y la programación. con registros sin necesidad de un motor de base de datos.
Figura 1. El editor de lista para el Figura 2. Así es el programa terminado, Figura 3. Recordemos usar el debug en
ComboBox es amigable y muy fácil de listo para tomarse el trabajo de nuestros programas para verificar, entre
usar; es una verdadera lástima que sólo memorizar users y passwords. De una otras cosas, la lógica del código y los
podamos acceder a él en tiempo de vez por todas podremos decirles adiós a errores de ejecución que pudieran llegar
ejecución. los papelitos. a producirse.
78 u s e r s . t e c t i m e s . c o m
ProgramacionU#120.qxd 3/27/01 5:25 PM Page 79
Type {Palabra clave que indica que vamos a definir reset (archivo);
nuestro propio tipo de dato} if filesize(archivo)=0 then {si el archivo está
Pasreg = record vacío}
Nombre: string[50]; begin
user, pass: string[30]; rewrite (archivo); {setea el modo de aper-
descripcion: string [255]; tura del archivo en escritura desde el principio
end; del mismo}
Levantar_datos_a_registro(user,pass,lista,des-
Éste es el registro que vamos a usar en nuestro programa, así c,reg); {Procedimiento que pasa los datos de los
que ahora definimos el archivo y, de forma infaltable, el regis- objetos al registro}
tro que usaremos para escribirlo y leerlo. write(archivo,reg);
reg:pasreg; closefile(archivo);
archivo: file of pasreg; {Si no definimos el tipo end
de archivo, lo toma como Untyped} else
begin
¡Programando la base! {primero buscamos si el registro existe o no}
Como ya habrán observado, éste es un típico programa de ABM while (not EOF(archivo)) and (existe<>true)
(Altas, Bajas y Modificaciones), y tenemos muchas maneras de do {mientras no pisemos el final}
controlarlo. Vamos a basarnos en que la modificación en el cam- begin
po clave es tomada como un nuevo registro. if reg.nombre = lista.text then
El campo clave será aquél por el cual buscaremos, crearemos existe:=true
e identificaremos cada registro. Nuestro registro está conforma- else
do por un nombre, un username, un password y una descrip- read(archivo,reg)
ción, donde nuestro campo clave será el nombre del registro. end;
En nuestro form tendremos un ComboBox, tres EditBox y tres
botones para guardar, crear y borrar un registro. if existe= true then {si existe, se trata
de una Modificación}
La A de alta begin
La alta de un registro o, lo que es lo mismo, la creación de reg.user:=user.text;
un registro estará dada por el hecho de comprobar la existen- reg.pass:=pass.text;
cia de éste en el archivo. Si no existe, lo damos de alta. El pro- reg.descripcion:=desc.text;
cedimiento, que está asociado al botón [Guardar], será así: write(archivo,reg);
closefile (archivo);
begin end
AssignFile(archivo,’\passwords.dat’); else
u s e r s . t e c t i m e s . c o m 79
ProgramacionU#120.qxd 3/27/01 5:25 PM Page 80
EXPERTOS PROGRAMACIÓN
begin La B de baja
while not eof (archivo) do {Si no, Este procedimiento nos mostrará la forma más elegante y
de una Alta} correcta de trabajar con archivos binarios.
read(archivo); La baja estará asociada al evento Clic del botón [Borrar], y
Levantar_datos_a_registro(user,pass- el procedimiento es así:
,lista,desc,reg);
write(archivo,reg); {Escribe en el var
final} i,j:integer;
armar_combo(lista); {Procedimiento vec_pas: array [0..limite] of pasreg; {Vector de
que rearma el ComboBox con el campo Nombre} registro para levantar el archivo a memoria}
end; begin
end; AssignFile(archivo,’\passwords.dat’);
end; reset (archivo);
if sizeof(archivo) <> 0 then
Este mismo procedimiento nos indica si se trata de una mo- begin
dificación o de un alta, por lo cual también tenemos la parte {subimos nuestro archivo a memoria o, lo que es
de las modificaciones resuelta en el mismo procedimiento. lo mismo, lo pasamos a nuestro vector de registros}
Dentro del procedimiento armar_combo(), donde se pasa i:=0;
por parámetro el ComboBox llamado Lista, podemos ver que while not EOF (archivo) do
ésta tiene una propiedad denominada Items, heredada del ob- begin
jeto Tstring (es una lista de cadenas), por lo cual deberemos read (archivo,reg);
acceder también a la propiedad Add, para poder agregar una if not EOF (archivo) then
cadena a la lista del ComboBox. La Figura 1 nos muestra el begin
amigable editor que Delphi nos ofrece para armar la lista en vec_pas [i]:= reg; {esta asignación sólo es
tiempo de diseño. posible cuando la estructura de los registros es
Una de las cosas que podemos observar es que el trato para igual}
el primer registro es diferente que para los demás, ya que ha- inc(i);
bitualmente debemos buscar el final del archivo (registro con end
la marca EOF, End Of File) y agregar el registro nuevo. En cam- else
bio, para el primer registro usamos Rewrite, que es una de las begin
funciones dadas por el set de I/O (Input/Output) de archivos, vec_pas [i]:= reg;
cuyo fin es abrir un archivo en forma de escritura, borrar to- inc(i);
dos los registros existentes, si hubiera alguno, y posicionar el end;
puntero al principio del archivo o en BOF (Begin Of File). Ade- end;
más, en caso de no encontrar el archivo externo asociado en {ahora trabajamos en nuestra estructura ya en
el procedimiento AssignFile, lo crea automáticamente. memoria}
Tabla 1
❙
Éstos son algunos procedimientos y funciones útiles al momento de trabajar con archivos binarios:
Procedimiento Descripción
AssignFile Asocia una variable tipo file con un archivo externo.
ChDir Cambia el directorio actual.
CloseFile Cierra y disocia la variable con el archivo externo, actualizando este último.
Eof Indica si el registro actual tiene marca de End Of File.
Erase Borra el archivo externo asociado a la variable.
FilePos Devuelve el número de registro actual.
FileSize Devuelve el tamaño del archivo.
MkDir Crea un subdirectorio.
Read Lee un registro del archivo y lo pone en un registro que le pasemos por parámetro.
Rename Renombra el archivo externo asociado.
Reset Ubica el puntero del archivo en el comienzo de éste y lo abre para lectura.
Rewrite Crea y abre un nuevo archivo.
RmDir Borra un subdirectorio.
Truncate Trunca el archivo a partir del registro actual.
Write Escribe el registro que se le pase por parámetro en el archivo, en la posición donde se encuentre.
80 u s e r s . t e c t i m e s . c o m
ProgramacionU#120.qxd 3/27/01 5:25 PM Page 82
EXPERTOS PROGRAMACIÓN
82 u s e r s . t e c t i m e s . c o m
ProgramacionU#121.qxd 4/24/01 11:30 AM Page 80
EXPERTOS PROGRAMACIÓN
Punteros en Delphi
No se trata de un arma ni nada por el estilo; los
PUNTEROS son muy útiles para cualquier aplicación.
Si bien al principio es difícil entenderlos, son muy
Alex VALENCIA flexibles y conforman el conjunto FUNDAMENTAL de
avalencia@tectimes.com
Estudiante de Ciencias de la
elementos para las bases de cualquier lenguaje de
Computación, fanático del anime y la programación. programación. ¡Apuntemos a ellos!
Figura 1: Para los programadores más ociosos, el servicio Figura 2: “Encuestas” nos servirá muchísimo para entender los
Delphi Direct ofrece las últimas noticias del mundo Delphi. punteros; el ejecutable y el fuente están en el CD.
80 u s e r s . t e c t i m e s . c o m
ProgramacionU#121.qxd 4/24/01 11:30 AM Page 81
Ésta es la página
principal del site
www.clubdelphi.com,
donde podremos
encontrar muchos trucos y
técnicas de programación
en español.
en la memoria, la máquina lo hace de manera aleatoria; es- lo no tiene mucho sentido y que para apuntar a algo en me-
to significa que toma el dato que se almacenará y genera moria es más efectivo usar una variable; esto es cierto, pe-
una dirección permitida donde guardarlo, acelerando el pro- ro se vuelve útil cuando tenemos más de una cosa para su-
ceso de lectura y escritura. bir a memoria, como, por ejemplo, los registros de un archi-
El objetivo de los punteros es usar la memoria RAM como vo binario.
nuestro soporte de almacenamiento de datos, lo cual nos brin- El uso real de los punteros aparece cuando los aplicamos a
da muchísimas ventajas que iremos viendo durante esta nota. los registros; para entenderlo mejor, veamos la siguiente de-
claración de un tipo de dato.
Apuntando la atención a los punteros
La pregunta ahora es: ¿qué es un puntero? Es una variable type
que contiene la dirección de memoria de un dato específico. pun=^reg; {puntero a un registro}
Por ejemplo, podemos declarar un puntero que almacene la di-
rección de memoria de algún número que hayamos guardado reg=record {registro}
previamente en memoria; cuando solicitemos el valor de ese edad:string[2];
puntero, veremos una dirección, pero si pedimos ver a qué cole:string [50];
apunta éste, nos devolverá el número. nombre: string[50];
Como sucede con las variables, hay distintas clases de pun- sig:pun;
teros. Por ejemplo, si queremos hacer lo mencionado anterior-
mente, deberemos usar un puntero a integer (si el número es end;
un entero). Hay punteros para todas las clases de variables,
así que debemos asegurarnos de usar el correcto. Podemos dividir esta declaración en dos partes. Empecemos
Tal vez se hayan dado cuenta de que el puntero por sí so- por la segunda: estamos declarando un registro con un cam-
Tabla 1
❙
Éstas son las estructuras más conocidas para armar con punteros:
u s e r s . t e c t i m e s . c o m 81
ProgramacionU#121.qxd 4/24/01 11:30 AM Page 82
EXPERTOS PROGRAMACIÓN
po edad, uno cole y un nombre del tipo string, y, por último, nodo, que será el principio de la lista}
un campo sig del tipo pun. La primera parte de la declaración ultimo:=p; {como es el único, también es el úl-
nos indica que pun es un puntero a reg, o sea, al registro de- timo}
clarado abajo. La traducción de esto es un registro común con end
un campo puntero del tipo reg, es decir que dicho campo pue- else
de almacenar una dirección de memoria donde se encuentra begin
otro registro igual. ¡Eureka! new (p); {creamos un nuevo nodo en la lista}
Este procedimiento es realmente útil, ya que podemos en- p^.sig:=nil;
ganchar los registros entre sí de la forma que más nos con- levantar_datos (p,nombre,edad,cole);
venga (según el programa que hagamos). Hay muchas formas ultimo^.sig:=p;
de engancharlos, y deberemos utilizar la más óptima, depen- ultimo:=p;
diendo del uso que les vayamos a dar a los registros en el pro- lista.Items.add(nombre.text);
grama. La Tabla 1 nos muestra algunas maneras de armar end;
nuestros punteros. clear_fields (nombre,edad,cole);
end;
Empleando los punteros
Si tuviéramos que realizar una aplicación que levantara re- Una lista no es más que muchos registros enganchados con
gistros de un archivo para hacerles, por ejemplo, una modifi- un principio y un fin, y es una estructura del tipo FIFO (First
cación a todos (habitualmente llamado proceso Batch), dis- In First Out: primero en entrar, primero en salir), lo que po-
pondríamos de dos opciones. Una de ellas es crear un vector dríamos imaginar como un tren con cada vagón representan-
de registros lo más grande posible para almacenar los regis- do a un nodo (registro).
tros que levantemos, y la otra opción, mucho más eficaz, es Hay ciertos punteros que debemos declarar antes de trabajar
subirlos a una lista de punteros, ya que es más rápido, utili- con cualquier tipo de estructura, que deben ser del tipo del
zamos menor cantidad de recursos de memoria y es más co- registro con el que estemos operando. Necesitamos estos pun-
rrecto desde el punto de vista de la programación. teros para direccionar nuestra lista (en este caso), o bien pa-
A continuación, veremos el ejemplo encuesta, que, si bien ra marcar ciertos nodos importantes en la estructura; en el ca-
no es muy útil para uso personal, nos ayudará a comprender so de la lista, el principio y el final.
mejor la sintaxis de los punteros. Fíjense en el código la utilización de la función new(),
La Tabla 2 muestra los elementos que componen nuestra que crea un espacio en memoria en tiempo de ejecución
aplicación, y la Figura 2, el modo en que los pusimos en el –de forma aleatoria, claro está–, y apunta al puntero pasa-
form. La encuesta es de número indefinido de registros, o sea do por parámetro a ese espacio. En nuestro caso, le pasa-
que no sabemos la cantidad de encuestados, por lo que el uso mos el puntero “p”, que es del tipo pun (puntero al regis-
de punteros resulta ideal ya que acudiremos a uno de sus me- tro), por lo cual creará un espacio del mismo tamaño que
jores recursos: la creación de espacios en memoria en tiempo nuestro registro.
de ejecución. Después de esa línea, mostramos cómo direccionar a un cam-
Obviamente, la aplicación “encuesta” no es más que un pro- po con un puntero; la línea p^.sig = nil significa que
grama ABM, por lo cual iremos viendo cómo usar los punteros estamos asignando el valor nil al campo sig del registro apun-
en cada caso. tado por “p”. Nil no es más que “Nulo”; al asignar nil a un
puntero, le estamos indicando que no apunte a nada, a nin-
Comenzando con la encuesta guna dirección en memoria.
El registro que utilizaremos lo declaramos al principio de la También es importante ver que el trato al primer registro a
nota; tiene los campos nombre, edad y cole (colegio), y su- crear no es igual que el de los demás, debido a que el primer
ponemos que es una encuesta a estudiantes. nodo será también el comienzo y el final de la lista, y por eso
El código de la Alta está relacionado con el botón [Guar- le asignamos el puntero inicio y final al mismo nodo. Para los
dar], por lo que quedará algo así: demás, el trato es igual, salvo que iremos corriendo el punte-
ro final para que siempre apunte al último nodo creado. Es
begin importante ver que antes de posicionar el puntero en el últi-
if lista.Items.Count= 0 then {si no hay regis- mo nodo creado (llamado último), debemos apuntar el sig del
tros...} nodo anterior al nuevo puntero, para que de esta forma se va-
begin yan enganchando los nodos a la lista.
new (p); {creamos el primer nodo de la lista} También vamos agregando los nombres de los encuestados a
p^.sig:=nil; la ListBox para tener a la vista los que vamos creando.
levantar_datos (p,nombre,edad,cole); {levantamos
los datos del form al nodo o registro} Liberando memoria
lista.Items.add(nombre.text); Cuando deseamos eliminar un nodo de nuestra lista, en ca-
inicio:=p; {el puntero ‘inicio’ apunta al primer so de que queramos borrar un registro, lo que deberemos ha-
82 u s e r s . t e c t i m e s . c o m
ProgramacionU#121.qxd 4/24/01 11:30 AM Page 83
LISTADO 1: p:=p^.sig;
type end;
pun=^pasreg_puntero; {si el nodo a borrar es el 1ro. o el último}
Pasreg_puntero = record p_anterior:=p_inicio;
Nombre: string[50]; if p <> p_inicio then {si el puntero a
user, pass: string[30]; borrar no es el primero...}
descripcion: string [255]; begin
sig: pun; for j:=1 to (i-1) do
end; p_anterior:=p_anterior^.sig; {posi-
var cionamos un puntero en la posición anterior}
i,j:integer; end
p, p_final, p_inicio, P_anterior, p_siguien- else {si el puntero a borrar es el 1ro.}
te:pun; begin
begin if p_inicio.sig <> nil then {si hay
if lista.text <> ‘’ then un nodo después del inicio...}
begin p_inicio:=p_inicio^.sig; {el ini-
AssignFile(archivo,’\passwords.dat’); cio será ahora el próximo nodo}
reset (archivo); end;
if sizeof(archivo) <> 0 then {veamos el caso en el que el nodo a borrar
begin sea el último}
{subimos nuestro archivo, pero en una lista} p_siguiente:=p_inicio;
read (archivo,reg); if p <> p_final then {si no es el úl-
new(p_inicio); timo}
p_inicio^.Nombre:=reg.Nombre; begin
p_inicio^.user:=reg.user; for j:=1 to (i+1) do
P_inicio^.pass:=reg.pass; p_siguiente:=p_siguiente^.sig; {po-
p_inicio^.descripcion:=reg.descripcion; sicionamos un puntero en la posición siguiente}
p_inicio^.sig:=nil; end
p:=p_inicio; else {si el puntero es el último...}
p_final:=p_inicio; begin
p_anterior:=p_inicio; p_final:=p_anterior; {el nuevo final}
while not EOF(archivo)do end;
begin {si el nodo está en medio de la lista, só-
read(archivo,reg); lo bastará conectar el anterior con el siguiente}
new (p); if (p<>p_inicio) and (p<>p_final) then
p^.Nombre:=reg.Nombre; p_anterior^.sig:=p_siguiente;
p^.user:=reg.user; freemem(p);
P^.pass:=reg.pass; end
p^.descripcion:=reg.descripcion; else
p_anterior.sig:=p; begin
p.sig:=nil; freemem(p_inicio);
p_final:=p; p:=nil;
p_anterior:=p; end;
end; {pasamos los registros al archivo}
{ahora trabajamos en la lista ya en memoria} rewrite (archivo);
{nos fijamos si hay más de un nodo en la lista} if p<>nil then
if p_inicio^.sig<>nil then begin
begin p:=p_inicio;
p:=p_inicio; while p<>p_final do
i:=0; begin
while lista.Text <> p^.Nombre do reg.nombre:=p^.Nombre;
begin reg.user:=p^.user;
inc(i); {INC es una función que in- reg.pass:=p^.pass;
crementa la variable que le pasa por parámetro} Continúa en la página 84
u s e r s . t e c t i m e s . c o m 83
ProgramacionU#121.qxd 4/24/01 11:30 AM Page 84
EXPERTOS PROGRAMACIÓN
84 u s e r s . t e c t i m e s . c o m
ProgramacionU#121.qxd 4/24/01 11:30 AM Page 85
Tipo de objeto Nombre USERS #117 (donde empezamos a hablar de Delphi), están ca-
TButtons (x3) Nombre, Guardar, Borrar pacitados para realizar proyectos de gran nivel, y si a esto le
TEdit (x3) Nombre, Cole, Edad sumamos la integridad y flexibilidad del lenguaje Delphi, se
TListBox Lista darán cuenta de que lograron mucho. Los animamos a que si-
gan adelante con Delphi. ✕
u s e r s . t e c t i m e s . c o m 85