Curso Básico de Programación
en Visual Basic
Entrega
Veinte: 24/Jun/98
por Guillermo "guille" Som
Si quieres linkar con otras entregas, desde el índice lo puedes hacer
![]()
Para compensar un poco el que hayan pasado casi dos meses entre la entrega 18 y la 19, te doy esta del tirón... no es por nada, es que ya la tenía lista y realmente tenía que ir junto con la diecinueve, lo que pasa es que no quise que la entrega anterior fuese demasiado larga y así te lo tomas con más ganas... espero.
Ya hemos visto las distintas
formas de acceder a los ficheros, ahora vamos a ver una
instrucción que puede sernos útil cuando decidamos
"movernos" dentro del fichero.
Me explico: cuando se trató el acceso secuencial, comenté que
la información había que leerla de forma secuencial, es decir
un dato después de otro, bueno, pues esto es cierto sólo a
medias. No empieces a pegar saltos de alegría, porque tampoco es
para tanto. El tema está en que si lees la información de forma
"seguida", entonces si que es así, pero, si te entra
hipo, puedes acceder a cualquier parte del fichero. ¿Cómo? Pues
con la siguiente instrucción que te voy a presentar ahora...
A ver, instrucción ven, que te voy a presentar... venga, no te de vergüenza, estamos en confianza... (es que dice que no le gusta su nombre), aquí está...
Ñoras, ñores, damas y caballeros, les presento a: SEEK
Esta instrucción (que también es
una función) se usa, en modo instrucción, para posicionarnos en
cualquier parte del fichero abierto, tanto para leer como para
escribir.
Si se usa como función, nos devuelve la posición actual, es
decir en la que nos encontramos, del fichero.
Veamos cómo usarla:
Seek #numFic, posición y también variable
= Seek(#numFic)
El valor devuelto es de tipo Long.
Dependiendo del modo en el que esté abierto el fichero habrá
que "interpretar" el valor de distinta forma:
Para los ficheros de tipo secuencial y binario, nos da la
posición en bytes (o caracteres).
Para los ficheros abiertos como aleatorios (random), nos da la
posición en número de registros.
Cuando lo usamos en modo instrucción, lo que hace es posicionar el puntero dentro del fichero, de modo que lo siguiente que se lea o se escriba se hará en la posición indicada. Ni que decir tiene que el valor de la posición debe ser un valor legal. Es decir, no podemos posicionarnos en la posición CERO ni en una posición NEGATIVA, ya que no es "legal".
El uso de esta función/instrucción es útil cuando necesitemos avanzar o retroceder dentro del fichero.
Y como el movimiento se demustra
andando, vamos a ver un ejemplo.
Vamos a crear una pequeña utilidad que leerá datos de un
fichero, buscando claves especiales.
Imaginate que quieres acceder a un fichero al estilo de los
ficheros INI, en ese tipo de ficheros existen una serie de
secciones que están "enmarcadas" entre corchetes y a
continuación vienen una serie de datos (claves) con una especie
de asignaciones que representan los valores de esas claves.
Veamos un ejemplo de un fichero de este tipo:
[elGuille] nombre=guille email=guille@costasol.net
La sección se llama
"elGuille" y los dos campos son "nombre" y
"email"
Realmente para acceder a este tipo de ficheros no necesitaríamos
usar Seek, ya que podemos acceder secuencialmente, pero vamos a
ver cómo podemos "posicionarnos" en una sección en
concreto, después de haber leido el contenido y haber tomado
"buena nota" de las posiciones.
La utilidad de ejemplo, nos va a
mostrar todas las secciones disponibles y después podremos
acceder rápidamente a una posición en concreto.
Veamos el aspecto del formulario y el código correspondiente:

'Esta fecha no está mal, es que ya lo tenía "manuscrito" desde entonces...
'Ejemplos del curso básico, ejemplo de Seek (26/May/98)
'
Option Explicit
Private Type tSecciones
Nombre As String
Posicion As Long
End Type
Private aSecciones() As tSecciones
Private nSecciones As Integer
Private sFic As String
Private nFic As Integer
Private Sub Form_Load()
'deshabilitar el botón de leer contenidos
cmdLeerContenido.Enabled = False
Text1 = ""
List1.Clear
'Creamos el fichero de ejemplo
sFic = "basico_20.ini"
nFic = FreeFile
Open sFic For Output As nFic
Print #nFic, "[elProfe]"
Print #nFic, "Nombre=Guillermo"
Print #nFic, "email=guille@costasol.net"
Print #nFic, ""
Print #nFic, "[Alumnos]"
Print #nFic, "Cantidad=2"
Print #nFic, "Nombre_01=Pepito"
Print #nFic, "email_01=pepito@servidor.com"
Print #nFic, "Nombre_02=Juanita"
Print #nFic, "email_02=juani@servidora.net"
Print #nFic, ""
Print #nFic, "[Fecha]"
Print #nFic, "Fichero creado el día=26/May/1998"
Print #nFic, "Fichero actualizado el día=" & Format$(Now, "dd/mmm/yyyy")
Close
End Sub
Private Sub cmdLeerContenido_Click()
'Leemos el contenido de la sección seleccionada en el list
Dim sCadena As String
Dim nItem As Long
nItem = List1.ListIndex
If nItem >= 0 Then
'borramos el contenido del Text1
Text1 = ""
nFic = FreeFile
Open sFic For Input As nFic
'posicionamos el fichero en el sitio que nos interesa
Seek nFic, aSecciones(nItem + 1).Posicion
'ahora leemos el contenido del fichero hasta encontrar [
'o hasta que se acabe el fichero
Do While Not EOF(nFic)
Line Input #nFic, sCadena
If Left$(sCadena, 1) = "[" Then
'nada más que leer
Exit Do
Else
Text1 = Text1 & sCadena & vbCrLf
End If
Loop
Close nFic
End If
End Sub
Private Sub cmdLeerSecciones_Click()
Dim sCadena As String
Dim Posicion As Long
'Borramos el contenido del ListBox
List1.Clear
'Leemos las secciones disponibles
nFic = FreeFile
Open sFic For Input As nFic
Do While Not EOF(nFic)
Line Input #nFic, sCadena
'guardamos la posición actual, justo después de leer,
'de esta forma nos indicará la posición a partir de la
'cual leeremos el contenido...
Posicion = Seek(nFic)
'Si es una sección
If Left$(sCadena, 1) = "[" Then
'incrementamos el número de secciones
nSecciones = nSecciones + 1
'redimensionamos el array
ReDim Preserve aSecciones(nSecciones)
'asignamos los valores al array
With aSecciones(nSecciones)
.Nombre = sCadena
.Posicion = Posicion
End With
'añadimos esta sección a la lista
List1.AddItem sCadena
End If
Loop
Close nFic
If nSecciones Then
'MsgBox "Se han hallado " & nSecciones & " secciones"
'seleccionamos el primer item del listbox
List1.ListIndex = 0
'habilitamos el botón
cmdLeerContenido.Enabled = True
Else
MsgBox "No hay secciones en el fichero: " & sFic
End If
End Sub
Private Sub List1_DblClick()
'También podemos ver el contenido de una sección
'haciendo doble-click
cmdLeerContenido_Click
End Sub
Te explico un poco cómo funciona todo esto. Ya que de lo que se trata no es sólo de ver código sino de explicarlo, ¿verdad?
En primer lugar declaramos las
variables que se van a usar.
Una de estas variables es un tipo definido que contendrá el
nombre de la sección y la posición dentro del fichero.
Creamos un array dinámico (es decir redimensionable) de este
nuevo tipo de datos, en él guardaremos cada una de las secciones
halladas en el fichero procesado. También dimensionamos una
variable que contendrá el número de secciones halladas, aunque
sólo se usa mientras se lee la información del fichero, por
tanto no es necesario que esté declarada en la parte general de
las declaraciones del formulario.
Recuerda que las variables declaradas en la parte general de un
formulario, están disponibles en todo el formulario.
En el Form_Load, que es el punto de entrada de nuestra utilidad, además de asignar el nombre del fichero y guardar un contenido de ejemplo, borramos el contenido del Text1 y el List1, además deshabilitamos el botón de leer el contenido de una sección, para que no se use hasta que no haya datos.
Al pulsar en el botón que lee las
secciones, primero borramos lo que hubiese antes en el array de
secciones y asignamos cero a la variable que contiene el número
de secciones.
Después de abrir el fichero, en modo secuencial, vamos leyendo
línea por línea, en cuanto nos encontramos con una línea que
empieza por corchete [, quiere decir que hemos encontrado una
sección, por tanto, incrementamos la variable que lleva la
cuenta de las secciones halladas, redimensionamos el array usando
Preserve para no perder la información antes almacenada, y
asignamos la información del nombre y la posición que hemos
obtenido con Seek.
Fíjate que la lectura de la posición se hace después de haber
leido la sección del fichero, esto es así, porque lo que
necesitamos saber es la posición que viene a continuación de la
sección, ya que después de la sección es cuando vienen las
claves. (En ralidad, se asigna siempre después de leer una
línea, pero a nosotros sólo nos interesa su valor cuando hemos
encontrado una sección).
Seguimos leyendo hasta encontrar el final del fichero.
¿Por qué se ha usado el acceso
secuencial?
Por la sencilla razón de que este tipo de fichero suele ser de
tipo ASCII, es decir que no contiene caracteres "raros"
y que normalmente se editan en programas del tipo NotePad. De
hecho el Windows tiene asociado al bloc de notas (Notepad) para
abrir los ficheros que contengan la extensión INI.
Además de que al no saberse la longitud de los datos que
contiene, pero que si sabemos que cada línea termina con un
retorno de carro (o cambio de línea), es más cómodo usar el
Line Input # para leer toda la línea; el modo binario no nos
sería de utilidad, salvo que leyesemos el fichero caracter por
caracter, cosa que ralentizaría el proceso.
Para acceder a una sección en
concreto, cosa que ocurre al pulsar en el botón
cmdLeerContenido, simplemente abrimos el fichero, posicionamos el
"puntero" en el sitio adecuado y leemos lo que haya en
el fichero hasta que encontremos otra sección, (que empezará
por un corchete), o hasta que lleguemos al final del fichero.
También he puesto código para que al hacer doble-click en un
elemento del List1, se lea el contenido de la sección
correspondiente, para ello lo único que se hace es llamar al
evento Click del botón cmdLeerContenido.
Y hasta aquí ha llegado esta entrega, (que realmente formaba parte de la entrega 19), ahora vamos a ver un par de ejercicios para que te vayas soltando en esto de la programación con el Visual Basic.
El primer ejercicio realmente no
usa Seek pero si algo parecido a la utilidad esta de leer el
contenido de los ficheros del tipo INI, y consiste en crear un
array con el contenido de todas las secciones y todas las claves
y valores de cada sección. De forma que el fichero sólo se lea
una vez y cuando se quiera mostrar el contenido de una sección
se use el contenido del array.
La verdad es que no es nada fácil, pero tampoco pienses que es
tan complicado como para no poder resolverlo, al menos deberías
intentarlo y no coger el camino fácil de ver la solución, entre
otras cosas, porque lo que se pretende con estos ejercicios es
que cojas "soltura" en la programación y si además de
soltarte te quedas con las "buenas" costumbres, pues
mejor.
¿A que buenas costumbres me refiero?
A usar Option Explicit en todos los módulos, para de esta forma
declarar las variables antes de usarlas y a "indentar"
el código para que te sea más fácil seguirlo...
Después del sermón vamos a ver la pista que te doy:
La pista es que puedes usar estos tipos definidos para crear el
array de claves y su contenido, y el array para almacenar cada
sección y las claves de cada una de ellas.
'
Private Type tContenidos
Clave As String
Contenido As String
End Type
Private Type tSecciones
Nombre As String
NumClaves As Integer
Contenidos() As tContenidos
End Type
Private aSecciones() As tSecciones
Como sabes cada clave tiene este formato: Clave=Contenido. Esto te lo digo para que la clave vaya por un lado y el contenido por otro, aunque sea algo más complicado que almacenar simplemente la clave y el contenido, a la larga te ayudará a manipular mejor las cadenas de caracteres y también le darán mayor utilidad al código que se cree con este ejercicio.
Como segundo ejercicio, haz lo mismo, pero en lugar de almacenar en el array cada clave y su contenido, usa Seek para "recordar" la posición de cada una de las claves de cada sección para después poder acceder a esa parte del fichero para leer lo que nos interesa.
Este segundo ejercicio es un poco
más complicadillo, ya que necesitará usar de forma correcta
Seek, tanto en modo función como en modo instrucción.
Este es el tipo de datos que tendrás que usar:
'
Private Type tSecciones
Nombre As String
NumClaves As Integer
Contenidos() As Long
End Type
Private aSecciones() As tSecciones
Fíjate que aquí sólo guardamos en Contenidos la posición de cada clave dentro del fichero.
Suerte y no desesperes si no lo consigues, no me gustaría perder a todos mis alumnos de golpe... creo que no lo soportaría.
Las soluciones de los dos ejercicios están en este link.
Y ya sólo queda que hagas tu comentario sobre esta entrega. Y si hay algo que necesites que te aclare, relacionado con lo que se ha visto en esta entrega, no te cortes y preguntame, pero sólo relacionado con lo que estamos viendo, ¿vale?
Nos vemos.
Guillermo
![]()