Curso Básico de Programación
en Visual Basic

Entrega Diecinueve: 24/Jun/98
por Guillermo "guille" Som

Si quieres linkar con otras entregas, desde el índice lo puedes hacer

 

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.
Guillermo


 
entrega anterior ir al índice siguiente entrega

Ir al índice principal del Guille