|
Curso básico
de programación en Visual Basic
Lección
19
Acceso
binario a ficheros
|
Realizamos
programas para la gestión de empresas. Empresas medianas y
pequeñas. Programas de contabilidad, cartera de pedidos
clientes proveedores, facturación control de albaranes,
tesorería cartera de cobros y pagos y estadísticas.
Nuestro
agradecimiento a todos los que por unas causas o por otras
visitan nuestra web. Gestión de empresas PYMES. Curso de
programación de Visual Basic.
|
Sé que va a sonar a excusa barata
y esas cosas, pero si te dijera que el "manuscrito" lo
tengo terminado desde el 26 de mayo...
Ahora ya no es por aquello del portátil, aún no lo tengo, parece
ser que no hay "almas" tan bondadosas por esos mundos de
Internet... pero bueno...
Estaba pensando yo que lo mismo con un programilla de esos a los que
les hablas y escriben de forma automática... la verdad es que he
estado probando con el SDK que tiene Microsoft, pero ese no me
termina de valer, salvo que las entregas las escriba en
yankinglish... pero no es plan... en fin, tendré que seguir
buscando las teclas y después de pulsar una tecla mirar para el
papel...
Bueno, menos rollo, a ver si soy capaz de terminarlo pronto, para ir
a ponerme como un salmonete, que hoy es día de playa, además de
que es fiesta local y esas cosas pa que los catetillos podamos ir a
darnos un remojón a la playa, con una buena torta de San Juan...
Me acuerdo yo que antes... ¡vale, vale!, no hace falta que
grites..., lo dejo, pero que sepas que te pierdes lo que iba a
decir...
Acceso binario a ficheros.
Para terminar con los tipos de
acceso a ficheros, vamos a ver la forma más potente y a la vez la
más complicada... o casi.
Con el acceso binario podemos acceder a cualquier punto del fichero
y, lo más importante, leer o guardar la cantidad de caracteres que
queramos.
Antes de entrar en detalles, veamos
cómo indicarle al VB que vamos a usar este tipo de acceso.
Como siempre, esto se hará al abrir el fichero:
Open Nombre_Fichero For Binary As Numero_Fichero
Cuando abrimos un fichero en modo
binario, al igual que sucedía en el modo aleatorio (random), se
tiene acceso tanto de lectura como de escritura. También se usan
las instrucciones GET y PUT para leer o escribir la información,
pero a diferencia del acceso aleatorio, no estamos obligados a usar
una variable de longitud fija, (además de longitud previamente
especificada a la hora de abrir el fichero), si esto fuese así, no
habría diferencia con el acceso aleatorio... así que esta entrega
casi ni existiría...
¿Cómo leemos datos de un
fichero de acceso binario?
Ya te he comentado que se usa GET
para leer datos, la cuestión está en cómo indicarle al Visual
Basic la cantidad de caracteres a leer...
Pues hagamos la pregunta: ¿Cómo le indicamos al VB la cantidad de
caracteres a leer?
Vamos a verlo con un ejemplo:
A$ = Space$(10)
Get nFic, , A$
Esto leerá 10 caracteres.
Osea, se leerán tantos caracteres
como "capacidad" tenga la variable usada. Si sólo
quisiéramos leer sólo un caracter, esta variable tendría una
longitud de un caracter... (algunas
veces alucino con mi lógica tan contundente, en fin...)
La ventaja es obvia: no es necesario estar atados a un número fijo
de caracteres, simplemente asignándole una cantidad de caracteres a
la variable usada, es suficiente.
El problema puede surgir a la hora de determinar la posición desde
la que leeremos esos caracteres.
Ves, nada es perfecto, y si no controlamos el tema, pues, tendremos
algún que otro quebradero de cabeza.
La posición tendremos que
indicarla nosotros mismos, aunque también podemos dejar que sea el
propio Visual el que se encargue de este tema, todo dependerá de lo
que queramos hacer.
Ya vimos en el acceso aleatorio que
el cálculo de la posición de cada registro podíamos dejarlo de
forma automática, es decir que sea el propio VB el que
"decida" la posición. Realmente el VB no decide nada, ya
que es una característica de GET y PUT, si no se le indica la
posición, usa la "predeterminada" y esa posición se
ajusta automáticamente cada vez que se lee o escribe información,
el cálculo se hace tomando la última posición y añadiéndole la
longitud del dato. En el caso de los ficheros aleatorios esa
posición es "ficticia" (o relativa), ya que el VB
convierte la posición real dentro del fichero en número de
registros... Pero ahora no estamos con el acceso aleatorio, sino con
el binario y con este tipo de acceso, trabajamos con posiciones
"reales", es decir que si hacemos esto:
Get nFic, 3, A$
Leeremos caracteres desde la posición tres del fichero, el número
de caracteres leídos estará indicado por la longitud de la
variable A$
Como ya comenté antes, la ventaja
es que no estamos obligados a leer un número determinado de
caracteres y el inconveniente es que hay que saber lo que estamos
haciendo y se puede convertir en un inconveniente si no lo usamos de
la forma adecuada.
Pero, vamos a demostrar esto que
acabo de decir.
Crea un nuevo proyecto, asignale a la propiedad AutoRedraw del form
el valor TRUE, de esta forma no habrá problemas a la hora de
imprimir, (y ver lo impreso), en el formulario. Esto del Autoredraw
es útil cuando nuestro form quede oculto por otra ventana, nunca
perderá lo que hayamos imprimido en él.
Añade un commandbutton y escribe el siguiente código:
Private Sub Command1_Click()
Dim nFic As Integer
Dim sFic As String
Dim sCadena As String
'sCadena tiene 20 caracteres
sCadena = "Prueba de una cadena"
sFic = "binarios_19.dat"
nFic = FreeFile
Open sFic For Binary As nFic
Put nFic, , sCadena
Close nFic
'leer los datos guardados
nFic = FreeFile
Open sFic For Binary As nFic
Get nFic, , sCadena
Print sCadena
Close nFic
End Sub
Ejecuta la aplicación (pulsando
F5), pulsa en el Command1, y verás que todo funciona bien.
Ahora añade lo siguiente antes del Get nFic, , sCadena:
sCadena = Space$(5)
Y ejecuta de nuevo el programa.
Como verás sólo se han leido los cinco primeros caracteres de lo
que se guardó anteriormente. Es decir sólo mostrará Prueb,
porque la cadena usada para leer tiene esa cantidad de caracteres.
Este es un detalle que deberás recordar, así que apúntatelo.
La ventaja es que podemos guardar y
leer distintos tipos de datos mezclados.
Por ejemplo, si sabemos que tenemos un tipo definido y después una
cadena de caracteres, podemos mezclarlo. Pero es importante que a la
hora de leer los datos, leamos la cantidad "justa" de
caracteres, y en el orden correcto.
Vamos a ver esto que acabo de decir.
Borra el código anterior o crea un nuevo proyecto y añade un
command y el siguiente código:
'Esto en la parte General del form
Option Explicit
Private Type t_colega
Nombre As String * 30
Edad As Integer
End Type
Private Sub Command1_Click()
Dim nFic As Integer
Dim sFic As String
Dim sCadena As String
Dim unColega As t_colega
unColega.Nombre = "Guille"
unColega.Edad = 40
'sCadena tiene 20 caracteres
sCadena = "Prueba de una cadena"
sFic = "binarios_19.dat"
nFic = FreeFile
Open sFic For Binary As nFic
Put nFic, , unColega
Put nFic, , sCadena
Close nFic
'leer los datos guardados
nFic = FreeFile
Open sFic For Binary As nFic
Get nFic, , unColega
Get nFic, , sCadena
'mostramos los datos leidos
Print unColega.Nombre, unColega.Edad
Print sCadena
Close nFic
End Sub
Si inviertes el orden de las
variables a la hora de leer, pues causas un pequeño desastre, ya
que no lees lo que esperas leer. Osea que no uses esto del acceso
binario "al voleo", sino pensándolo bien.
Entonces, ¿cuando es conveniente
usar el acceso binario?
Siempre que queramos acceder a un fichero del que estimemos que
puede que no sea del tipo ASCII, es decir un fichero que pueda
contener cualquier clase de caracteres. Normalmente los ficheros
ASCII, (o los usados habitualmente para acceso secuencial), terminan
cuando se encuentra un código EOF (caracter ASCII número 26) o
cuando ya no hay más caracteres en el fichero; sin embargo con el
acceso binario sólo se "acaban" cuando no quedan más
caracteres que leer del fichero.
Por supuesto que si un fichero se ha guardado usando un tipo de
acceso, puede abrirse usando otro tipo de acceso, aunque estos casos
no son recomendables, salvo que sepamos lo que hacemos...
Veamos un nuevo ejemplo. Ya sabes, borra el código usado
anteriormente o crea un nuevo proyecto.
Private Sub Command1_Click()
Dim nFic As Integer
Dim sFic As String
Dim sCadena As String
sFic = "binarios_19.dat"
nFic = FreeFile
Open sFic For Binary As nFic
Put nFic, , "Prueba de una cadena"
Put nFic, , vbCrLf
'Se guarda una segunda cadena
Put nFic, , "Segunda cadena"
Put nFic, , vbCrLf
Close nFic
'leer como secuencial
nFic = FreeFile
Open sFic For Input As nFic
Do While Not EOF(nFic)
Line Input #nFic, sCadena
Print sCadena
Loop
Close nFic
End Sub
Como habrás comprobado, se ha
leído todo lo que había en el fichero, incluso cosas que había de
pruebas anteriores.
Ahora engañemos al VB y hagamos que piense que un fichero se ha
acabado antes de que se acabe de forma "real".
Sustituye el código del Command1 por este otro:
Private Sub Command1_Click()
Dim nFic As Integer
Dim sFic As String
Dim sCadena As String
Dim sEOF As String * 1
sEOF = Chr$(26)
sFic = "binarios_19.dat"
nFic = FreeFile
Open sFic For Binary As nFic
Put nFic, , "Prueba de una cadena"
Put nFic, , vbCrLf
'Añadimos un código de fin de fichero
Put nFic, , sEOF
'Se guarda una segunda cadena
Put nFic, , "Segunda cadena"
Put nFic, , vbCrLf
Close nFic
'leer como secuencial
nFic = FreeFile
Open sFic For Input As nFic
Do While Not EOF(nFic)
Line Input #nFic, sCadena
Print sCadena
Loop
Close nFic
End Sub
Ahora sólo se ha mostrado lo
guardado antes del código almacenado en la variable sEOF.
Pero el fichero continúa teniendo lo que antes tenía. Lo que
ocurre es que cuando se abre un fichero secuencial y el VB se
encuentra con el código 26, piensa que se debe haber terminado el
fichero en cuestión.
Ahora vamos a leerlo como binario... Por supuesto, sabiendo lo que
se ha guardado y cómo se ha guardado.
Private Sub Command1_Click()
Dim nFic As Integer
Dim sFic As String
Dim sCadena As String
Dim sEOF As String * 1
Dim sCRLF As String * 2
sEOF = Chr$(26)
sCRLF = vbCrLf
'sCadena tiene 20 caracteres
sCadena = "Prueba de una cadena"
sFic = "binarios_19.dat"
nFic = FreeFile
Open sFic For Binary As nFic
Put nFic, , sCadena
Put nFic, , sCRLF
Put nFic, , sEOF
'Se guarda una cadena de 15 caracteres
Put nFic, , "Segunda cadena"
Put nFic, , sCRLF
Close nFic
'leer los datos guardados
nFic = FreeFile
Open sFic For Binary As nFic
'Se leen sólo 5 caracteres de los 20 guardados
sCadena = Space$(5)
Get nFic, , sCadena
Print sCadena
sCadena = Space$(15)
'Leemos los caracteres que quedaron pendientes,
'ya que sCadena sólo leyó 5 de los 20 caraceteres que tenía
Get nFic, , sCadena
'también leemos los caracteres "extras" que se guardaron
Get nFic, , sCRLF
Get nFic, , sEOF
Print sCadena
Get nFic, , sCadena
Print sCadena
Close nFic
End Sub
Bien, ahora ya hemos conseguido
leer todo, pero fíjate en el detalle de que hemos tenído que leer
los códigos "extras" que se guardaron, es decir el
retorno de carro y el de fin de fichero. Si no lo hubieramos
hecho... pruebalo y lo compruebas...
Habrás observado una línea de más y un caracter extraño antes de
"Segunda cade"... creo que no hace falta que te explique
el porqué... ¿verdad?
Normalmente con el acceso binario
podemos leer todos los caracteres que haya en un fichero. Se suele
usar cuando no sabemos la estructura de ese fichero y tenemos alguna
forma de "interpretar" lo que leemos, aunque esto último
no se aprende en ningún curso y no hay regla fija. En la mayoría
de las ocasiones que uso el acceso binario, es cuando quiero leer
información de un fichero para buscar algo en concreto. Ese fichero
puede ser un ejecutable, una DLL o cualquier otro tipo de fichero.
Si de antemano se que es un fichero secuencial, seguramente no lo
leería como binario, ya que el tiempo de acceso y lectura de un
fichero secuencial es menor que uno abierto como binario. Osea que
se lee antes uno abierto con For Input que con For
Binary.
Esta diferencia en el tiempo de acceso es apreciable sobre todo
cuando se manejan muchos ficheros...
Hablando de leer todo el contenido
de un fichero, ya sabes que existe un función llamada Input$, a la
que se le indica el número del fichero abierto y la cantidad de
caracteres que queremos leer y nos lo devuelve para que podamos
asignarlo a una variable de cadena. El fichero que hemos creado con
el último ejemplo no es realmente un fichero ASCII, ya que contiene
caracteres binarios, es decir, caracteres que no están en el rago
ASCII del 32 al 255 (en algunos casos hasta el código 127).
Veamos la reacción del VB ante un caso "no deseado", es
decir leer lo que no debemos leer:
'Se supone que has probado los ejemplos anteriores y que existe el fichero indicado
Private Sub Command1_Click()
Dim nFic As Integer
Dim sFic As String
Dim sCadena As String
sFic = "binarios_19.dat"
nFic = FreeFile
'
Open sFic For Input As nFic
sCadena = Input$(LOF(nFic), nFic)
Close nFic
Print sCadena
End Sub
Aquí lo que se pretendía era leer
el fichero de una vez. Pero como se ha abierto el fichero como
secuencial, el VB ha detectado que se la leído un código de fin de
fichero, precisamente antes de que se acabe el fichero y nos avisa
con un magnífico mensaje de error.
Esto se soluciona, bien abriendo el
fichero secuencial, pero usando EOF(nFic) para comprobar si hemos
alcanzado el final del fichero o bien abriendo el fichero en modo
binario y usando la función Input$.
Private Sub Command1_Click()
Dim nFic As Integer
Dim sFic As String
Dim sCadena As String
sFic = "binarios_19.dat"
nFic = FreeFile
'
Open sFic For Binary As nFic
sCadena = Input$(LOF(nFic), nFic)
Close nFic
Print sCadena
End Sub
Ahora puedes observar que se lee
TODO lo que hay en el fichero, ya que al abrirlo en modo binario, no
se tiene en cuenta ningún caracter especial y se lee hasta dónde
se le indique.
Creo que es suficiente por hoy.
Hay más cosas, en cuanto al acceso
a los ficheros, pero no vamos a alargar esta entrega, que puede ser
que te empaches con tantas cosas y no es bueno.
Los ejercicios
¿Recuerdas uno de los ejercicios
de la entrega 18?
Consistía en cambiar el formato de un fichero de acceso aleatorio
en otro con los campos en otro formato (longitud de los campos).
Pues bien, con el acceso aleatorio sólo era posible si conocíamos
de antemano el tamaño del registro nuevo (y, por supuesto, la
longitud de cada campo). Ahora con lo que sabes del acceso binario y
unas cuantas miles de líneas de código, podrás hacerlo para
convertir cualquier fichero a un nuevo formato y así poder usarlo
de forma genérica.
Es decir, tenemos un fichero con
una serie de campos y queremos cambiar la estructura de ese fichero
y, por supuesto, traspasar la información existente al nuevo
formato. El usuario de esta utilidad, tendrá que saber la
estructura del fichero de origen y también saber la nueva
estructura a la que quiere convertir el susodicho fichero de datos.
Para no complicar demasiado la cosa, vamos a usar sólo 10 campos,
pero se podrían permitir más...
El aspecto del form en tiempo de
diseño sería el siguiente:
No te preocupes por los "campos" que faltan en el destino,
enseguida te explico cómo hacer que aparezcan, al menos a la hora
de ejecutar el programa.
Esto te servirá para otras veces en las que necesites crear
controles en tiempo de ejecución y posicionarlos adecuadamente, o
casi...
Veamos el aspecto del form y después veremos el código usado para
"crear" esos controles:

El código:
Private Sub Form_Load()
Dim i As Integer
'Crear los controles de destino
'(empezamos por UNO porque el control CERO ya está creado)
For i = 1 To 9
'Cargarlos en memoria
Load lblDest(i)
Load txtDestTam(i)
'Asignarles la posición y hacerlos visible
With txtDestTam(i)
.Visible = True
.Top = txtDestTam(i - 1).Top + .Height + 45
lblDest(i).Top = .Top - 15
lblDest(i).Visible = True
lblDest(i) = "Campo " & i + 1 & ":"
End With
Next
'Borrar el contenido de los TextBoxes
For i = 0 To 9
txtTam(i).Text = ""
txtDestTam(i).Text = ""
Next
End Sub
La cuestión está en que al pulsar
en el botón de convertir el fichero, antes se hayan asignado los
valores correspondientes.
La información que hay que pasarle a la aplicación de cada campo,
es la siguiente: tamaño de cada campo.
Lo que hay que saber es el tamaño de los números al guardarlo en
disco, para ello echale un vistazo a la ayuda del VB y te dirá lo
que ocupa cada tipo, aunque creo que en alguna de las primeras
entragas ya lo vimos.
Para muestra, un botón: Los Integers ocupan dos bytes, los Longs
cuatro bytes, etc.
La cuestión es que se asignen los
valores de forma correcta, sino, la cosa puede no funcionar.
Y con esto y un bizcocho... hasta
otra entrega, que no creo que sea mañana a las ocho...
Este es el link a la solución
de esta entrega, recuerda
no hacer trampas e intentarlo primero.
(De todas formas, el link no te llevará a ningún sitio, ya que
aún no la he puesto... je, je...)
Nota 25/Jun/98: Ya
está operativo el link de la solución al ejercicio.
Y continuando con la sana costumbre
de recibir tus preciados, y la mayoría de las veces aduladores,
comentarios, cosa que agradezco y que dicho sea de paso, es la
única razón por la que sigo con el curso básico... pues eso, aqui
te dejo el link para que me escribas lo que
te ha parecido esta entrega,
pero, recuerda: no para hacer consultas, gracias.
Nos vemos.
|