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 demuestra 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 protected]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 leído 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 protected]" Print #nFic, "" Print #nFic, "[Alumnos]" Print #nFic, "Cantidad=2" Print #nFic, "Nombre_01=Pepito" Print #nFic, "[email protected]" Print #nFic, "Nombre_02=Juanita" Print #nFic, "[email protected]" 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 SubTe 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 leído 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 realidad, 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 leyésemos 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 tSeccionesComo 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 tSeccionesFí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 pregúntame, pero sólo relacionado con lo que estamos viendo, ¿vale?
Nos vemos.
Guillermo