Curso Básico de Programación
en Visual Basic

Entrega Dieciocho: 26/Abr/98
por Guillermo "guille" Som

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

 

Ya es hora de seguir con los ficheros de acceso aleatorio, después del alto en el camino para ver cómo se manejan las cadenas de caracteres en Visual Basic, aún no hemos visto todas las funciones, pero si las más comunes. No te preocupes... no voy a continuar en esta entrega con esas funciones... ya le llegarán el turno...

¡Por fin! Habrás exclamado... y con razón... pero son cosas que debes saber... y como de lo que se trata es de saber más y/o mejor, pues... nunca están de más...

No recuerdo exactamente en que punto me quedé en las explicaciones sobre el acceso aleatorio de la entrega dieciséis, así que... si ves que me repito, pues... ¡salud! y a seguir adelante...

Una cosa que hay que tener superclarísimo en esto del acceso aleatorio, es que debemos usar tipos definidos para acceder a los datos del fichero... ¿para que complicarnos con funciones conversoras de datos, si el Visual lo hace de forma automática por nosotros? Por tanto, te aconsejo que "siempre" uses variables definidas, ya que no hay un motivo válido para no usarlas.

En los siguientes ejemplos, vamos a usar el tipo definido que usamos en la entrega dieciséis, el del colega...
En este primer caso, vamos a guardar en un registro determinado el contenido de tres cajas de textos, cada una de ellas para cada uno de los campos del tipo definido:

Private Sub cmdGuardar_Click()
    Dim unColega As t_colega
    Dim nFic As Long
    Dim numColega As Long

    nFic = FreeFile
    Open "colegas.dat" For Random As nFic Len = Len(unColega)

    'Sacar un valor aleatorio
    numColega = Rnd * 25 + 1
    Label1(3) = "número de colega: " & CStr(numColega)

    With unColega
        .Nombre = Text1
        .Edad = Val(Text2)
        .email = Text3
    End With

    'Guardar los datos en el disco
    Put #nFic, numColega, unColega
    Close nFic

End Sub

Aquí vemos los pasos que normalmente se realizan con cualquier tipo de fichero...
--Se asigna a una variable un número de fichero libre (ya sabes: el canal por el cual VB se comunicará con el disco)
--Se abre el fichero en cuestión, recuerda que las extensiones que uses son a tu antojo, no hay necesidad de usar un tipo de extensión específica, salvo que hagas un programa que "entienda" los datos contenidos en ese tipo de extensión y puedas abrir los ficheros con sólo hacer doble click... pero esto es un tema para más adelante...
--Se asigna a la variable el dato a guardar y
--Se guarda...

Este ejemplo no sería práctico, ya que puede que salga el mismo número más de una vez y se perderían los datos anteriores.
Porque debes saber que, cuando se guarda información en un registro determinado, éste funciona de la misma forma que las variables... o casi, es decir: cuando se guarda un nuevo valor, el que hubiera antes "desaparece".

Una cosa que debes saber, aunque me imagino que lo habrás comprobado al ejecutar el programa, y si no ha sido así, no te preocupes... si te sirve de consuelo, tarde unos mesesillos en "detectar" esto que te voy a explicar ahora...
Puedes acceder a cualquier registro de un fichero aleatorio, incluso si antes no has guardado nada. De la misma forma, puedes guardar información en el registro 7 aunque antes no hayas guardado en ninguno de los 6 anteriores.

¿El problema?
Que si lees información de un registro en el que no has guardado información anteriormente... puedes encontrarte con "basura", y de hecho la encontrarás...
¿Por qué?
Porque accedes a una parte del disco que, posiblemente tenía guardada alguna otra información... aprovecho esto, para decirte que, cuando borras un fichero del disco, este fichero no se borra, al menos no se borra la información que contenía.
¿Cómo solucionar este problemilla?
Hay varios métodos, el que yo normalmente usaba, (ahora casi no trabajo con ficheros de acceso aleatorio), era guardar información "vacía" en unos cuantos registros y cuando esos estaban ocupados, guardaba otro puñado y así.
Algunas veces, si sabía que el fichero iba a tener un número limitado de registros, los grababa todos con datos vacíos, es decir cadenas con sólo espacios y números con valor cero.
En otras ocasiones tenía un campo del registro al que le asignaba un valor, si al leer el registro, tenía ese valor "predeterminado", quería decir que ya contenía información válida, si no era así, ponía los campos con valores "vacíos" y así evitaba la basura.

Si quieres comprobarlo... así de paso me sirve para que veas un ejemplo de cómo acceder a los datos del disco y mostrarlo en unas cajas de texto.

Private Sub cmdLeer_Click()
    Dim unColega As t_colega
    Dim nFic As Long
    Dim numColega As Long

    nFic = FreeFile
    Open "colegas.dat" For Random As nFic Len = Len(unColega)

    'Sacar un valor aleatorio
    numColega = Rnd * 25 + 1
    Label1(3) = "número de colega: " & CStr(numColega)
    'leer ese registro
    Get #nFic, numColega, unColega

    With unColega
        Text1 = .Nombre
        Text2 = .Edad
        Text3 = .email
    End With

    Close nFic

End Sub

Si has probado el ejemplo de guardar, para poder conseguir datos con contenido "basura", deberás probar algunas veces más que las que hayas probado antes... ¿por qué? porque estamos usando números aleatorios y al no existir un "Randomize", la secuencia de números aleatorios siempre es la misma cada vez que ejecutas el programa... ¿lo recuerdas?

Bien, aparte del asuntillo este de la "basura", no tiene ningún misterio esto del acceso aleatorio... pero aún no hemos terminado, no queda mucho para que te toque "trabajar", pero todavía tienes un respiro... si a que te explique más cosas se puede llamar respiro...

En el tercer ejemplo que vamos a ver, se van a mostrar todos los colegas que tenemos guardados en el fichero. Se supone que los datos los hemos guardado de forma ordenada, no como en los ejemplos anteriores, ya que no tiene ningún motivo guardar a los colegas aleatoriamente... no sea que cojan complejo de bola de bingo...

Ya te he comentado que la longitud de todos los registros de un fichero aleatorio tienen que ser iguales... y ahora lo podrás comprobar... que algunas veces no hay que fiarse de todo lo que yo diga...
El número de registros es el resultado de dividir la longitud del fichero por la longitud de cada registro...
numRegistros = Lof(nFic) \ Len(unColega)
Fíjate que uso \ para dividir. Las divisiones realizadas con este signo dan como resultado números enteros, mientras que el habitual / realiza una división de coma flotante... es decir que puede tener decimales.
Como un registro no puede estar formado por "nosecuantos caracteres y pico", en este caso es recomendable el uso de la división de números enteros, entre otras cosas porque también es más rápida...

Por tanto este código nos informaría del número de registros y haría un bucle entre todos los registros que tiene el fichero.

'
nFic = FreeFile
Open "colegas.dat" For Random As nFic Len = Len(unColega)

numRegistros = Lof(nFic) \ Len(unColega)
For i = 1 To numRegistros
    Get #nFic, i, unColega
    'Mostrar el contenido del registro
    '...
Next
Close nFic

Podemos sustituir el Get #nFic, i, unColega por esto otro: Get #nFic, , unColega.
Cuando no se indica el número de registro, tanto en Get como en Put, Visual Basic usa el registro actual. Cada vez que se accede a un registro determinado, el Basic lo "marca" como registro actual, de esta forma sabe cual debe ser el siguiente registro al que debe acceder si no se le indica ninguno.
Si hacemos esto: Get #nFic, 15, unColega, al hacer esto otro: Put #nFic, , unColega, se estará guardando en el número 16.
Es decir, el VB mantiene un "puntero" al siguiente registro. El bucle anterior podría haber quedado así:

For i = 1 To numRegistros
    Get #nFic, , unColega
    'Mostrar el contenido del registro
    '...
Next

De todas formas, siempre suelo especificar el número de registro al que quiero acceder, así me parece que estoy más seguro de dónde se leerá o escribirá el registro.

Uno de los problemas que tiene este tipo de ficheros es que estamos "atados" a la longitud del registro.
¿Que ocurre si nos dá el "punto" de quitar, añadir o cambiar la longitud de alguno de los campos?
Pues que lo tenemos más bien chungo... estaremos metidos en un pequeño lio...
Una vez que hemos definido el tamaño del registro, y tenemos datos en el fichero, cualquier cambio que hagamos, dará como resultado algo no esperado...
Pero, todo tiene arreglo... aunque en este caso, nos lo tendremos que "currar" nosotros. Tendremos que fabricarnos alguna rutina que se encargue de esta conversión.

Y ese podría ser el ejercicio de esta entrega:
Realizar una pequeña utilidad que convierta un fichero de acceso aleatorio con registros de una longitud conocida, en otro con registros de otra longitud o con campos de longitud diferente al original.
Y digo "longitud conocida", porque si no sabemos la longitud de cada registro, incluso de cada campo de ese registro, poco podremos hacer...

Un detalle importante es que para acceder a los ficheros abiertos como "random", sólo podemos hacerlo con variables de longitud fija, ya sean tipos definidos o cadenas.
Se podría pensar que haciendo esto:

Dim unaCadena As String

'Asignamos 83 espacios a esta cadena
unaCadena = Space$(83)

nFic = FreeFile
Open "colegas.dat" For Random As nFic Len = Len(unaCadena)
'...
Get #nFic, 1, unaCadena

¡Pues no!
Ya que la variable unaCadena no tiene longitud fija y el Visual Basic necesita que lo sea.

Otra cosa es que hagamos esto otro:

'Esta cadena siempre tendrá este número de caracteres
Dim unaCadena As String * 83

nFic = FreeFile
Open "colegas.dat" For Random As nFic Len = Len(unaCadena)
'...
Get #nFic, 1, unaCadena

Ahora si que funcionaría bien la cosa, ya que el VB tiene la información que necesita...
La verdad es que hecho de menos una instrucción que tenía el Basic del MS-DOS y era que podías definir una serie de variables para acceder a los registros, incluso podías declarar una array... y cada uno de los elementos del array con la longitud de cada uno de los campos... Pero eso ya no está, así que... hay que usar los tipos definidos que al fin y al cabo es la mejor opción para este tipo de ficheros.

Para el ejercicio, se supone que tenemos un fichero con registros declarados de esta forma:

Private Type t_Colega
    Nombre As String * 30
    Edad   As Integer
    email  As String * 50
End Type

Y lo queremos convertir en registros que tengan esta otra:

Private Type t_Colega
    Nombre As String * 30
    Edad   As Integer
    email  As String * 50
    URL    As String * 128
End Type

Por supuesto, queremos que los registros que haya sigan conservando los datos que tuviesen... sino, ¿que clase de rutina conversora sería?
Acuérdate de lo que digo en muchas ocasiones, no pienses en complicarte la vida, siempre procura ir a lo simple, ya que en la mayoría de las ocasiones ese es el camino correcto...

Cuando veamos la siguiente entrega, o al menos cuando veamos los ficheros de acceso binario, (mi intención es que sea en la próxima entrega, pero ya sabes...), crearemos otra utilidad de conversión más flexible que esta, es decir, una rutina que convierta un fichero a un formato que pueda ser definido por el usuario en tiempo de ejecución.

Aunque no sea lo habitual, y normalmente una entrega termina cuando te pongo los ejercicios, vamos a ver un ejemplo completo para introducir y mostrar datos en un fichero de acceso aleatorio.
¿Cómo?
¿Que quieres hacerlo tú?
Pues vale, me parece estupendo...

Desde luego, es que tengo unos alumnos que no me merezco... 8-)

No te quejes... y no digas que tú no has dicho nada... yo he oído voces que me decían: ¡queremos hacerlo nosotros!
Y eso es lo que hay...

Para esta aplicación vamos a usar tres cajas de texto en los que introduciremos los valores a guardar en cada campo, con sus correspondientes labels para indicarnos los nombres de esos campos.
Existirá otro label/textbox para indicarnos el número del registro actual, es decir en el que se almacenarán los datos o del que se leerán esos datos.
Un par de botones para Guardar y Leer...
¿Te resulta familiar?
Pues así es... algo parecido a lo que ya vimos en la entrega número once cuando se usaron arrays de tipos definidos...

El aspecto del "programilla" sería algo así:

No he añadido nada para mostrar todos los registros... pero ya tendremos tiempo de hacerlo.

Bueno, pues hasta aquí hemos llegado...
Las
soluciones de la entrega 18 están en este link.

 

Y a pesar de parecer un poco pesado, realmente me gustaría recibir tu opinión sobre esta entrega.

Nos vemos.
Guillermo


 
entrega anterior ir al índice siguiente entrega

Ir al índice principal del Guille