Sei sulla pagina 1di 95

Visual Basic 2 Año 2016 Omar Palermo 1

¿Que es una variable?


En cualquier programa siempre necesitaremos hacer cálculos, usar información, procesarla y mostrarla.
En la mayoría de los casos, necesitaremos un lugar temporal en el cual guardar parte de esa información, incluso
toda.
Todos los lenguajes, y el Basic no iba a ser menos, nos permiten guardar datos en la memoria, para que cuando
los necesitemos, podamos tomarlos, modificarlos y volverlos a guardar para usarlos más tarde. Si no seguimos
unas normas o usamos unas reglas, de poco nos iba a servir el guardar esa información, si después no podemos
almacenarla en ningún sitio, pero ese será tema de otro capítulo... ahora centrémonos en la memoria.
¿La memoria? Espero que este concepto lo tengas claro, pero si no es así, ahí va un poco de 'rollo':
La memoria es el lugar donde el ordenador almacena de forma temporal los programas y parte de la información
que necesita o utiliza. Así pues, los lenguajes de programación usan también esa memoria para guardar
información propia del lenguaje y del programa que queramos realizar.
El programa una vez que se está ejecutando puede necesitar también guardar información, aunque esta
información sólo estará disponible mientras se esté ejecutando. Esas posiciones o lugares de la memoria donde
los programas pueden almacenar información son las variables. El que se llamen de esta forma es porque
podemos hacer que el contenido de ese lugar de almacenamiento varie, es como si tuviesemos una serie de cajas
y en ellas pudiésemos guardar cosas, con la salvedad de que en cada caja sólo puede haber una cosa a la vez;
aunque también veremos cómo hacer que el contenido pueda variar y que varie dependiendo de lo que contenía
antes...
Veamos un ejemplo:
Imagínate que quieres guardar tu nombre en una variable, para ello tendriamos que 'guardar' el nombre en la
memoria, es decir asignar a una variable un valor. En este caso nuestro nombre. Para ello el lenguaje de
programación pone a nuestra disposición unos lugares donde almacenar nuestro nombre, pero nos impone una
serie de reglas de conducta.
Si queremos guardar en una de nuestras cajas una hoja, por lo menos tendremos una caja con el tamaño
adecuado y que tenga una 'forma', para que el papel no vuele al menor soplo de viento.
Esta es la regla básica para poder usar esos lugares de almacenamientos (variables):
Llamar a esas posiciones de memoria con un nombre. Simple, ¿verdad?
En principio, es todo el requisito necesario: que le demos un nombre al sitio en el que queremos guardar la
información.
Por tanto, si queremos guardar un nombre, (el nuestro, por ejemplo), en la memoria, podríamos llamarlo nombre.
Y ahora tendremos que seguir otra norma de conducta (o funcionamiento), que en este caso el lenguaje Basic nos
dicta:
Para guardar en una variable (posición de memoria) algo, debes hacerlo de la siguiente manera:
---Pon el nombre con el que quieres llamar a esa parte de la memoria,
---a continuación pones el signo igual (=) y
---después lo que quieras guardar.
Por tanto para guardar Guillermo en la variables nombre, tendriamos que hacer (o casi):
Nombre = Guillermo
Pero esto podía llevar a confusión, ya que el Basic no nos dice nada sobre cómo debemos llamar (o si lo prefieres,
cómo hay que escribir) el nombre de una variable, por tanto Guillermo también podría ser una variable.
Así pues, cuando queramos guardar en una variable una palabra, una frase, nombre o cualquier tipo de
información alfabética, tendremos que indicarlo poniendo dicha información dentro de comillas dobles, el ejemplo
quedaría así:
Nombre = "Guillermo"
Ahora no hay confusión posible, hemos seguido lo que el Basic nos ha dicho: variable, signo igual, valor a
almacenar.
Si queremos guardar un número en una variable, la cosa es más simple:
Numero = 7
¿Te estás enterando?
Pero, ¿que ocurre si quisieramos repetir 7 veces a Guillermo?
Podrías hacer esto, multiplicar a Guillermo por 7
Paliza = "Guillermo" * 7
Pero el Basic te diría que eso no está bien, no porque Guillermo (yo) no sea un paliza, sino porque te diría que no
coinciden los tipos (Type Mismatch)
¿Que son los tipos? Los distintos tipos de datos.
Los datos pueden ser, básicamente, de dos tipos:
Numéricos: sólo números y
Alfanuméricos: cualquier cosa, letras y/o números, pero es tratada como si fuesen palabras, frases, etc.
Para el Basic 7 y "7" son dos tipos de datos diferentes.
El primero es el número 7 y en el segundo caso, es el literal (o palabra) "7"
Así que cuando veas algo entrecomillado, piensa que no es un número, sino una palabra (más vulgarmente
llamada cadena de caracteres o string en inglés)
Visual Basic 2 Año 2016 Omar Palermo 2
Hemos visto que no podemos multiplicar una palabra (cadena) por un número, pero si podemos multiplicar una
variable por un número (siempre que la variable sea numérica, por supuesto)
Según esto, el Basic debería permitir hacer esto:
Guillermo = 5
Paliza = Guillermo * 7
El Basic tomaría el 5 y lo almacenaría en ena variable numérica llamada Guillermo.
Después se encuentra con: Paliza = Guillermo * 7 y aquí lo que hace es evaluar la expresión que está después
del signo igual, lo calcula y el resultado lo guarda en la variable que está a la izquierda del signo de asignación (=)
¿Expresión? Expresión es cualquier cosa que el Basic tenga que 'desglosar' para poder entenderla, incluso a
veces ni eso...
Por ejemplo cuando el Basic se encuentra con 5 * 2 tiene que 'evaluar' lo que significa, para poder hacer el
cálculo, de esta forma sabrá que tenemos una operación en la cual queremos multiplicar dos números, una vez
que ha evaluado nuestra intención de multiplicar esos dos números, efectuará el cálculo y almacenará el resultado
en... si no le decimos dónde, lo hará en una memoria que tiene para esas cosas, pero si no le indicamos que debe
hacer con ese resultado, nos dará un error...
Si le decimos simplemente: 5 * 2
El Basic no sabrá que hacer con el resultado de esta 'expresión' (que por cierto es 10) y nos dirá:
o te espabilas o lo tienes crudo conmigo.
Así que lo más juicioso sería decirle: vale, vale, guardalo en una variable, así que:
Resultado = 5 * 2 guardaría un 10 en la variable Resultado.
También podríamos decirle que nos mostrara el resultado, en lugar de guardarlo en una variable, y aquí llega
nuestra primera instrucción: Print. Con ella le decimos al Basic que lo imprima (o sea que los muestre, más
adelante vermos dónde), según lo dicho, haciendo esto:
Print 5 * 2, el Basic diría que muy bien y mostraría un 10
Pero, volvamos al Paliza del Guillermo, es decir al ejemplo de Paliza = Guillermo * 7
Si quisieramos mostrar el valor de Paliza, tendríamos que hacer algo como esto: Print Paliza, y nos mostraría 35,
ya que el valor de Paliza sería 35, porque el contenido de Guillermo es 5 y 5 * 7 es 35 (y sin calculadora!!!)
Veamos si es cierto que Guillermo vale 5. Haciendo Print Guillermo, mostrará un 5.
Antes de seguir 'imaginando' las cosas, vamos a verla en funcionamiento. Es decir vamos a probar que todo esto
es cierto.
Carga el Visual Basic (si es que aún no lo has hecho.
Te creará un Form nuevo, que estará vacío.
Cierralo y muestra la ventana de código.
Mostrará la parte de las declaraciones Generales del Formulario.
Si tiene escrito Option Explicit, (estará en la parte superior), borralo, más adelante te explicaré para que sirve.
Ahora situate en Form (seleccionalo de la lista desplegable que está a la izquierda), te mostrará:
Private Sub Form_Load()
End Sub
Situate en medio, es decir, en una línea en blanco después del Private... y escribe el ejemplo, quedaría así:
Private Sub Form_Load()
Guillermo = 5
Paliza = Guillermo * 7
Print Paliza
End Sub
Pulsa F5, para ejecutar el programa, y verás que se escribe el 35.
Bien, ya tienes una forma de mostrar datos. Ahora veamos otros ejemplos, antes debes parar el programa, para
ello cierra el Form, pulsando en el botón que tiene una X, o bien pulsa en el botón detener de la barra de
herramientas del VB.
Situate de nuevo en el código del Form_Load, escribe después de la línea del Print, lo siguiente:
Print Guillermo
Pulsa de nuevo F5 y verás que ahora además del 35, hay un 5 debajo. El valor de la variable Guillermo.
Pero, ¿que ocurriría si cambiasemos el valor de Guillermo?
Añade estas líneas a continuación de la anteriores, para que quede de esta forma:
Private Sub Form_Load()
Guillermo = 5
Paliza = Guillermo * 7
Print Paliza
Print Guillermo
Guillermo = 10
Print Guillermo
Print Paliza
Visual Basic 2 Año 2016 Omar Palermo 3
End Sub
Después de pulsar F5, te mostrará los siguientes valores (cada número en una línea), 35, 5, 10, 35
¿Esperabas que el último fuese 70?
Fijate que cuando asignamos a Paliza el contenido de Guillermo, éste era 5, por tanto el Basic evaluó la
expresión 5 * 7 y almacenó el resultado (el 35). Una vez almacenado el resultado, el Basic se olvidó de dónde
había sacado ese 5.
Si queremos que se 'actualice' el valor de Paliza, tendremos que indicarselo de nuevo al Basic, para que vuelva a
evaluar la expresión y hacer la correspondiente asignación. Para ello, pon en medio de los dos últimos prints la
siguiente asignación:
Paliza = Guillermo * 7
Esta vez, al ejecutar el programa, mostrará un 70, que será el nuevo contenido de Paliza.
Ya para terminar, borra todo lo anterior y escribe: (por supuesto debes detener el programa...)
Private Sub Form_Load()
Nombre = "Guillermo"
Print Nombre
End Sub
Pulsa F5 y verás que se muestra el contenido de la variable Nombre, es decir Guillermo.
Prueba ahora con esto otro (es un clásico):
Print "Hola Mundo"
Y para rematar, y de camino ver otra posiblidad del Print, escribe en lugar del Print Nombre:
Print "Hola " ; Nombre
El punto y coma, se usa para indicarle al Basic que se deben mostrar las cosas una a continuación de la otra.
Ahora te habrá mostrado: Hola Guillermo, fijate que después de hola y antes de cerrar las comillas hay un espacio.
Bien, creo que con esto es suficiente por hoy... o por ahora.
Nota: En todos los FORM_LOAD deberás poner SHOW al principio para que se muestre
lo que se imprime.

El manejo de entrada de datos y una forma un poco más práctica de mostrar los datos, empezaremos a vernos ya
con lo que es programar con Windows, es decir el manejo de eventos, o al menos el movernos dentro de los
eventos y saber "controlarlos" para las cosas que queramos hacer.
Con el "basic clásico", no había problemas. Las cosas se producían de forma lineal, es decir empezaban por el
principio del programa y se iban ejecutando a medida que se avanzaba por él. Salvo en las ocasiones en las que
en un momento dado "saltabamos" a otro sitio, pero era porque nosotros así lo habíamos programado. De una
forma u otra teníamos control total sobre lo que podía ocurrir. Pero en Windows (o los entornos dominados por los
eventos), la cosa no es tan sencilla.
Debemos controlar todas (o casi) las posibilidades que se pueden producir...
Antes vamos a dejar un par de cosillas claras. Según como tengas configurado el entorno de desarrollo, habrás
tenido problemas con los ejemplos de la entrega anterior. La razón es que el Visual Basic nos permite controlar un
poco mejor el tema de las variables que queremos usar. Ese control lo da la instrucción: Option Explicit. Si
ponemos esto en la parte destinada a las declaraciones de cualquier módulo, nos obligará a declarar las variables
que vamos a usar en dicho módulo.
En el último ejemplo de la entrega anterior, teníamos una variable llamada Nombre, en la cual almacenamos un
nombre, por tanto podríamos haberle avisado a Visual Basic que reservara espacio para una variable, y para
hacerlo usamos la instrucción DIM, con ésta le indicamos que nos guarde un "cachillo" de la memoria para
nuestro uso:
Dim Nombre
También nos ofrece una variante con respecto al "basic clásico" y es, precisamente, el tipo de datos variant. Con
este tipo podemos asignar a una variable cualquier tipo de dato. Desde un número hasta una cadena de
caracteres, psando por cualquier tipo de objeto que Visual Basic pueda manejar (más o menos).
Los que hayan tenido experiencias anteriores, con Basic u otro lenguaje, sabrán que cada variable debe ser del
tipo de datos que queramos asignarle. En VB por supuesto esto también es posible y recomendable.
Ya vimos que los tipos de datos podían ser numéricos o de caracteres. Pero dentro de los numéricos, tenemos
cuatro tipos básicos: enteros, enteros largos, simples y dobles. Cada uno de ellos tienen unas capacidades
determinadas, además de ocupar más memoria o menos, (ahora lo veremos), lo más importante es que los
números enteros y entero largo sólo admiten números enteros (de ahí sus nombres), es decir que no admiten
decimales. Sin embargo los otros dos si admiten decimales.
Estas capacidades puedes encontrarlas en el manual del basic o en la ayuda, lo que a mi me interesa que sepas
es cómo poder indicarle al Visual Basic que reserve ese espacio de memoria para un tipo determinado. Ya te he
dicho que el espacio que ocupa en memoria es diferente para cada uno de estos tipos, veamos en la siguiente
tabla cómo declararlos y cuanto ocupa:
Tipo Espacio ocupado Tipo de declaración Ejemplo
Visual Basic 2 Año 2016 Omar Palermo 4
Entero 2 bytes Integer Dim Numero As Integer
Entero Largo 4 bytes Long Dim Numero As Long
Simple 4 bytes Single Dim Numero As Single
Doble 8 bytes Double Dim Numero As Double
En el caso de las variables que van a guardar nombres (cadenas de caracteres), se deben declarar como String y
el espacio que ocupa será 4 bytes más un byte por cada caracter que tenga, en el caso de VB de 32 bits
realmente ocupará 2 bytes por cada caracter que tenga.
La longitud máxima de una variable del tipo String será de aproximadamente 32.000 caracteres y la forma de
declararla será:
Dim Cadena As String
Una vez declarada podemos asignarle la cantidad de caracteres que queramos (sin pasarnos) y cuantas veces
queramos.
Hay que tener en cuenta que en cualquier variable sólo se queda el último valor asignado. Ya lo vimos en la
entrega anterior, pero vamos a refrescarlo un poco:
Dim Número As Integer
Número = 5
Print Número
Número = 12
Print Número
En este ejemplo, el último valor almacenado en Número es el 12. El 5 que tenía en un principio se perdió.
Pero, ¿que tengo que hacer para sumarle una cantidad al valor almacenado en una variable?
Es decir, ¿cómo incrementar el valor de una variable numérica?
La respuesta la tienes en cómo se manejan las expresiones y las asignaciones a las variables. Como ya vimos
anteriormente, al asignar a una variable una expresión, primero se calcula la expresión y una vez obtenido el
resultado, se asigna a la variable.
¿Recuerdas lo que ocurría con la variable Paliza? Vamos a verlo de nuevo, pero usando otros nombres.
Dim N As Integer
Dim M As Integer
N = 10
M=N*3
Print M
El resultado de este programa sería 30, que es lo que resulta de multiplicar 10 por 3. Cuando se asigna a la
variable M el valor de N (que es 10) multiplicado por 3, el VB toma el contenido de N y lo multiplica por 3. Una vez
que sabe la solución, asigna ese valor, en este caso 30) a la variable que hay a la izquierda del signo igual.
Sabiendo esto, podriamos simplificar la cosa y hacer los siguiente:
N=N*3
Print N
También obtendriamos 30. Ya que cuando el Basic calcula la expresión de la derecha del signo igual, N vale 10,
una vez obtenido el resultado del cálculo lo asigna a la variable de la izquierda del signo de asignación, sin
importarle lo más mínimo de que variables es y como en este caso hemos usado la misma, pues se queda el
último valor, perdiéndose el que originalmente estaba "guardado". Esto es útil cuando necesitamos "contar" de
forma secuencial, para así incrementar el valor de una variable.
Ya veremos alguna "utilidad" para estos casos. Ahora vamos a ver cómo podemos manejar las cadenas de
caracteres, es decir las variables de tipo String.
Con estas variables ocurre lo mismo que con las numéricas, pero la única operación que podemos realiazar es la
suma.
Realmente una suma en una cadena de caracteres es "pegar" dos cadenas en una sola.
Por ejemplo si hacemos esto: N = 3 + 2. El valor obtenido es 5 y eso es lo que se guarda en N.
Sin embargo con los strings hacer esto: Cadena = "A" + "B", se guardará "AB", es decir se unirán las dos cadenas
en una sola. Para este tipo de operación se recomienda usar mejor el signo &. Que entre otras cosas le indica al
Visual Basic que lo que pretendemos hacer es unir dos cadenas, no sumarlas.
Aunque "teóricamente" no se pueden sumar cadenas, sólo con-catenarlas, veremos cómo podemos llegar a
producir "problemillas" de entendimiento entre el VB y nuestras "mentes poderosas".
Como te he comentado al principio el tipo de datos Variant acepta de todo, números, nombres, etc.
Si no le indicamos de forma correcta al VB cual es nuestra intención, podemos confundirle y hacer que el resultado
de algo que nosotros dabamos "por hecho", al final se convierte en un pequeño caos para nuestras pobres
mentes.
Vamos a verlo con un par de ejemplos, en estos casos: (al no indicarle de que tipo son las variables, el Basic
entiende que nuestra intención es usar el tipo Variant)
Dim Num1
Dim Num2
Visual Basic 2 Año 2016 Omar Palermo 5
Num1 = 5
Num2 = 3
Num1 = Num1 + Num2
Print Num1
¿Que imprimirá? Probalo y saldrás de duda. Bueno, imprimirá un 8.
Ahora veamos este otro ejemplo:
Dim Num1
Dim Num2
Num1 = "5"
Num2 = "3"
Num1 = Num1 + Num2
Print Num1
Fijate que lo que varía es sólo las comillas. El resultado en este caso es 53, es decir ha unido las dos cadenas.
Ahora quita las comillas del 5, para dejarlo así:
Dim Num1
Dim Num2
Num1 = 5
Num2 = "3"
Num1 = Num1 + Num2
Print Num1
¿Que ha pasado? Pues que ha impreso un 8, es decir ha "pasado" de que el tres sea una cadena de caracteres y
lo ha tomado por un número...
En esta ocasión, sólo vamos a cambiar la línea de la asignación para dejarla de esta forma:
Num1 = Num1 & Num2
El resultado será 53. Porque le hemos indicado que una las dos cadenas, por tanto al encontrase con esta
"operación" ha considerado al número 5 como una cadena, en lugar de un número.
Cambia ahora la asignación del Num2, para que sea: Num2 = 3
Vuelve a mostrar 53, el signo & tiene unos poderes enormes... y a pesar de ser dos números la única operación
que puede realizar es la concatenación de cadenas, por tanto el tipo Variant se convierte por arte de mágia en
cadena.
Pero fijate si es "fuerte" el poder de convicción que tiene este operador, que aunque cambiemos el tipo de las
variables, sigue "convenciendo" al basic que tipo de operación debe hacer. Esto no debería ocurrir así, pero
ocurre.
Dim Num1 As Integer
Dim Num2 As Integer
Num1 = 5
Num2 = 3
Num1 = Num1 & Num2
Print Num1
Sigue mostrando 53, aunque en este caso debería producir un error, ya que un Integer no es una cadena.
Así que "cuidado" con las operaciones que realizamos. Ya que si añades esta línea:
Print Num1 * 2
verás que realmente Num1 tiene guardado un número y el resultado será: 106
¿A dónde nos lleva todo esto? A que debemos usar los signos (operadores) de forma adecuada. Y si nuestra
intención es sumar números, empleemos el signo +, en caso de que queramos unir cadenas de caracteres,
usaremos el &
Para rematar esta segunda entrega, vamos a usar un textbox para que se puedan introducir datos y empecemos a
manejar los eventos, mejor dicho empecemos a "habituarnos" a los eventos.
Añade al form dos Label, un TextBox y un botón de comandos.
Añade el siguiente código y después ejecuta el programa, ya sabes F5. Escribe algo en el cuadro de texto y pulsa
en el botón.
Private Sub Form_Load()
Label2 = ""
Text1 = ""
End Sub
Private Sub Command1_Click()
Label2 = "Hola " & Text1
End Sub
Cuando pulsas F5, se produce el evento Form_Load, por tanto se asigna al Label2 y al Text1 una cadena vacía,
con lo cual borramos el contenido anterior, que es el que se muestra en la Figura.
Hasta que no pulsemos el botón mostrar, no ocurrirá nada y el programa estará esperando a que ocurra algo.
Visual Basic 2 Año 2016 Omar Palermo 6
Una vez pulsado el botón, se produce el evento Click del Command1 y se hace lo que se indica en su interior, que
es tomar lo que hay en la caja de texto y unirla a la palabra Hola, para asignarla al Label2.
Ahora, imaginate que quieres mostrar el nombre en mayúsculas. Lo único que tendrías que hacer es lo siguiente:
Private Sub Command1_Click()
Label2 = "Hola " & UCase(Text1)
End Sub
Lo que se ha hecho es decirle al VB que convierta en mayúsculas lo que ya está en Text1. Esa es la "utilidad" del
UCase.
Pero y ¿si quisieramos que conforme se va escribiendo se vayan convirtiendo los caracteres a mayúsculas?
Aquí entrarían más instrucciones/funciones del Visual Basic, así cómo otro de los eventos que pone a nuestra
disposición, en este caso el evento que se produce cada vez que se modifica el contenido del textbox: Change,
escribe lo siguiente:
Private Sub Text1_Change()
Text1 = UCase(Text1)
End Sub
Pruebalo y verás lo que ocurre. Queda "guay" ¿verdad? Pero no es lo que nosotros pretendiamos. Vamos a
intentar remediarlo y de camino vemos nuevas instrucciones/propiedades, en este caso del TextBox.
Private Sub Text1_Change()
Text1 = UCase(Text1)
Text1.SelStart = Len(Text1)
End Sub
La línea que se ha añadido (realmente la habrás tecleado tú), lo que le indica al Visual Basic es que haga lo
siguiente:
Calcula la longitud del contenido del Text1, (Len cuenta los caracteres de una cadena y lo devuelve como número),
SelStart es una propiedad del TextBox que entre otras cosas, le indica la posición en la que se insertará el
siguiente caracter que se escriba o bien nos puede indicar la posición actual del cursor. Por tanto obliga a poner el
cursor, (el palo ese que parpadea y que nos indica que podemos escribir), al final de la última letra que contiene el
Text1.
Ahora ya sabes que cada vez que "cambie" el Text1, se produce un evento Change.
Pero hay otra forma de hacer esto mismo y es controlando cada tecla que se pulsa. Esto lo podemos "controlar"
en el evento KeyPress, el cual se produce cada vez que se pulsa una tecla. Borra el procedimiento anterior y
escribe este otro:
Private Sub Text1_KeyPress(KeyAscii As Integer)
Dim s As String
s = UCase(Chr(KeyAscii))
KeyAscii = Asc(s)
End Sub
Ahora han entrado dos nuevas funciones en acción: Chr, la cual convierte un número en una cadena... realmente
convierte un código ASCII en la letra que representa (busca en la ayuda ASCII y leete lo que dice en las opciones
que te muestra). Por otra parte Asc hace lo contrario, es decir convierte una letra en el código ASCII. Y lo que
nosotros hacemos es: convertir el código de la tecla pulsada, representado por la variable KeyAscii, en una
cadena, la pasamos a mayúsculas y después la volvemos a asignar a la variable, para "engañar" al Visual Basic y
así hacerle pensar que realmente hemos tecleado una letra en mayúsculas.
Ya has visto cómo maneja el Visual Basic las variables, si a esta "libertad" (aunque más bien es libertinaje), le
añadimos que no nos obliga a nada, es decir el VB nos está diciendo: "puedes usar las variables para lo que
quieras, cómo quieras (o casi) y cuando quieras"
Y esto en principio podría parecer una buena cosa, pero realmente es un mal hábito, que muchos de los que venís
del BASIC, ya teneis formado y creo que ahora sería un buen momento para empezar a cambiar.
Lo primero que debes hacer es ir al menú Herramientas (Tools) y en Opciones (Options) marca la casilla que
indica "Requerir declaración de variables" (Require Variable Declaration), esto añadirá a cada nuevo módulo
(FRM, BAS o CLS) la siguiente instrucción: Option Explicit, de esta forma tendrás la obligación de declarar cada
una de las variables que uses en el programa. Y tu preguntarás: ¿Para que obligar a que se declaren las
variables? La respuesta es bien sencilla: para que las declares... (algunas veces me asombro de la lógica tan
aplastante de mis comentarios)
Bromas aparte, es recomendable que declares las variables que vayas a usar y te diría más: no sólo es bueno
declarar las variables, sino que mejor aún es declararlas del tipo adecuado.
Ya vimos que hay diferentes tipos de variables, no sólo de tipos genéricos como podrían ser para almacenar
caracteres y números, sino que dentro de las numéricas hay varios tipos, y cada uno de ellos tiene una razón de
ser.
En mis tiempos del BASIC normalito, es decir del MS-DOS, no existía esta obligación de declarar "forzosamente"
las variables y cuando estabas escribiendo un programa (proyecto que lo llaman ahora), grande, acababas
Visual Basic 2 Año 2016 Omar Palermo 7
"invitablemente" usando más variables de la cuenta porque ya no recordabas si la variable "i" o "j" estaba siendo
usada a nivel global o no... (yo es que con el despiste que gasto, me veía creando las variables "ii", "j2", etc, para
no "meter la pata") y esto no era lo peor, al fin y al cabo lo único que ocurría era que estaba "desperdiciando"
memoria, por no tener un control de las variables que estaba usando; lo malo era que se podían escribir
erróneamente los nombres de las variables de forma que al final, el programa no funcionaba bien porque al
escribir un nombre de variable, habíamos cambiado el nombre... era frustrante y algunas veces te volvías loco
buscando el fallo...
La ventaja de usar el Option Explicit, es que si escribes mal una variable, el VB te avisa... bueno, algunas veces
te avisa, sobre todo cuando se encuentra con la variable "mal escrita".
Aquí viene la segunda recomendación del día: cuando ejecutes un programa, hazlo con Control+F5, de esta
forma se hace una compilación completa y "más o menos" exhaustiva del código, avisándote cuando hay algo que
no "cuadra", con el VB3 no había problemas, ya que siempre se hacía la compilación completa, pero desde el VB4
se puede pulsar F5 y hasta que no llega al procedimiento actual, no comprueba si todo lo que hay en él está
correcto.
Así que para "curarte en salud" procura hacer la compilación completa
La tercera recomendación no es obligatoria, siempre que sigas la que voy a dar después, esta es uan norma que
también he usado desde mis tiempos de MS-DOS (aunque reconozco que últimamente no la pongo en práctica, ya
que hago lo que después comentaré en la cuarta recomendación).
En todos los módulos, antes sólo eran BAS, ponía al principio la siguiente línea:
DEFINT A-Z
de esta forma le indicaba al BASIC que mi intención era usar todas las variables del tipo Integer (entero),
(realmente después usaba del tipo que me daba la gana, pero mi primera intención era no complicarme la vida con
la mayoría de las variables), cuando quería usar una variable diferente de Integer, le indicaba "explicitamente" de
que tipo era y así me obliga a usar la mayoría de ellas de este tipo que a la larga es o era el más usado, ya que
para hacer bucles (ya te explicaré en un ratillo que es eso de los bucles y cómo hacerlos en VB) y otros cálculos
"normales", era más que suficiente y en la mayoría de los casos: más rápido.
En Basic, y por supuesto todavía en Visual Basic, aunque cada vez va a menos, se puede indicar el tipo de una
variable de varias formas, al declararlas con Dim, vimos que se hacía de la siguiente forma:
Dim unNumero As Integer
Dim unNumeroLargo As Long
Dim otroNumero As Single
Dim masNumeros As Double
Dim unNombre As String
Dim multiUso As Variant
Cada una de estas varibales es de un tipo distinto, las cuatro primeras numéricas, la quinta para almacenar
cadenas de caracteres y la última del tipo por defecto del VB: Variant que como su nombre indica (aunque en
inglés), es Variante y puede almacenar prácticamente cualquier cosa, objetos incluidos, (ya veremos los objetos en
otra ocasión). Lo del tipo por defecto, es siempre que no se haya especificado un tipo determinado para todas las
variables, por ejemplo usando el DEFINT A-Z, el tipo por defecto ya no es Variant, sino Integer.
Al grano, "quesnoche", a lo que iba era que además de declarar las variables de esta forma, también se puede
hacer de de esta otra:
Dim unNumero%
Dim unNumeroLargo&
Dim otroNumero!
Dim masNumeros#
Dim unNombre$
En el caso de Variant no existe un caracter especial para indicar que es de ese tipo, así que cuando quieras usar
una varible Variant, tendrás que declararla como en el primer ejemplo.
Aún queda otro carácter para otro tipo de datos numérico, el tipo Currency que se puede declarar con @. Estre
tipo ocupa 8 bytes y permite guardar números de tipo moneda, es decir números no enteros, pero con un número
determinado y fijo de decimales, en la ayuda o en los manuales podrás ver los números que cada tipo admite.
Para terminar con las recomendaciones de hoy, voy a indicarte algo que debes tener en cuenta cuando declaras
variables y que aún los más expertos caen en la trampa.
Además de declarar las variables con Dim, poniendo cada declaración en una línea, cosa que por otro lado queda
bastante claro y es como suelo hacerlo, aunque últimamente estoy volviendo a tener malos hábitos... ¿será la
edad?
También se pueden declarar más de una variable con un mismo DIM, vamos a verlo con un ejemplo:
Dim Numero As Integer, NumeroLargo As Long, otroNum As Single, Nombre As String, Numerazo As
Double
por supuesto también valdría de esta otra forma:
Dim Numero%, NumeroLargo&, otroNum!, Nombre$, Numerazo#
Y si me apuras, también de esta otra:
Dim Numero%, NumeroLargo As Long, otroNum As Single, Nombre$, Numerazo#
Visual Basic 2 Año 2016 Omar Palermo 8
Pero sea como fuere, en todos los ejemplos se ha especificado el tipo que queremos asignar.
Por supuesto también podremos declarar varibales de esta forma:
Dim unaVariable, otraVariable, terceraVariable
Pero, surge esta pregunta ¿de que tipo son estas tres variables? (al menos se espera que te surja...)
La respuesta es bien sencilla, si se ha entendido toda la "retahila" que te he soltado anteriormente:
Serán del tipo Variant o del especificado con el DEFINT A-Z (es decir Integer)
Voy a suponer que la tercera recomendación no la estás poniendo en práctica, por tanto serían del tipo Variant.
Pero fijate que podrías caer en el error, sobre todo si has programado algo en C, de pensar que esta línea:
Dim Numero, otroNumeroInt, elTercero As Integer
o esta otra:
Dim Numero As Integer, otroNumeroInt, elTercero
están declarando tres números Integer y no es así, lo que se está declarando sería, en el primer caso:
Numero y otroNumeroInt como Variant y elTercero como entero.
en el segundo caso sólo Numero sería del tipo entero y las otras dos variables del tipo Variant.
Sería "ideal" que fuese como aparenta, pero el VB no hace estas "virguerías", ni incluso en la versión 5.
Por tanto, cuando declares variables, fijate bien de que tipo son las que estás declarando, para no llevarte
sorpresas, sobre todo con los redondeos y errores de desbordamiento...
Un desbordamiento se produce cuando asignamos a un número un valor mayor del que está capacitado para
almacenar, así si un entero sólo acepta valores de +/- 32767 (realmente acepta hasta -32768), al asignarle un
valor de 40000, nos dirá que "tururú" y dará error.
En cuanto a que tipo de variable usar en cada caso, tendrás que tener en cuenta que quieres hacer. Normalmente
en los bucles se suelen usar variables enteras, bien Integer, si sabemos que no nos vamos a pasar de 32767, bien
Long Integer que puede almacenar un valor de dos mil y pico millones... (¡quien los tuviera, aunque fuese en
calderilla!)
Vamos a ver un ejemplo (al fin algo de código se escucha entre el público...), con este código podrás comprobar la
velocidad de los bucles con los distintos tipos de varibales y así poder comprobar cual es la más adecuada.
Crea un nuevo proyecto y asigna unos cuantos Labels (6 en total) y un botón.
Cuando ejecutes este programilla, puedes ir tranquilamente a tomar café, porque se tomará su tiempo...
En teoría nos mostrará el tiempo que emplea en hacer unos bucles con tipos diferentes de datos. Para que sea
fiable, deberás especificar unos valores altos, ya que con números pequeños no es demasiado fiable, e incluso
con números altos tampoco... la cosa era poner algo de código para "rematar" el capítulo de hoy...
En la próxima entrega explicaré las intrucciones que se han usado y en algunos casos, explicaré hasta el por qué
de usarlas.
Osea esto es lo que se dice un programa inútil que además de consumir recursos del sistema y hacernos perder el
tiempo, no vale para nada... (es que después de probarlo, me he dado cuenta de que o todos los formatos son
prácticamente igual de rápidos o yo he estado "engañado" durante todo este tiempo...)
Option Explicit
Private Sub Command1_Click()
Dim nInt As Integer
Dim nLng As Long
Dim nSng As Single
Dim nDob As Double
Dim nCur As Currency
Dim nVar As Variant
Dim timer1#, timer2 As Double
Const minBucle = 1, maxBucle = 10
Command1.Caption = "Calculando..."
timer1 = Timer
For nInt = minBucle To maxBucle
Contar CInt(nInt), Label1
Next
timer2 = CDbl(Timer - timer1)
Label1 = "Duración con Integer: " & timer2
DoEvents
timer1 = Timer
For nLng = minBucle To maxBucle
Contar CInt(nLng), Label2
Next
timer2 = CDbl(Timer - timer1)
Label2 = "Duración con Long: " & timer2
DoEvents
timer1 = Timer
Visual Basic 2 Año 2016 Omar Palermo 9
For nSng = minBucle To maxBucle
Contar CInt(nSng), Label3
Next
timer2 = CDbl(Timer - timer1)
Label3 = "Duración con Single: " & timer2
DoEvents

timer1 = Timer
For nDob = minBucle To maxBucle
Contar CInt(nDob), Label4
Next
timer2 = CDbl(Timer - timer1)
Label4 = "Duración con Double: " & timer2
DoEvents
timer1 = Timer
For nCur = minBucle To maxBucle
Contar CInt(nCur), Label5
Next
timer2 = CDbl(Timer - timer1)
Label5 = "Duración con Currency: " & timer2
DoEvents
timer1 = Timer
For nVar = minBucle To maxBucle
Contar CInt(nVar), Label6
Next
timer2 = CDbl(Timer - timer1)
Label6 = "Duración con Variant: " & timer2
DoEvents
Command1.Caption = "Calcular"
End Sub

Private Sub Contar(valor As Integer, etiqueta As Control)


Dim i As Integer
Dim unDoble As Double
Const elMaximo = 1000&

For i = 1 To elMaximo
unDoble = unDoble + 1
etiqueta.Caption = valor * elMaximo + unDoble
DoEvents
Next
End Sub

Para introducir código en cualquiera de los eventos de los controles o del formulario, lo único que tienes que hacer
es seleccionar el control y el evento que queremos codificar de las listas desplegables, en el módulo de código,
pulsando en Código en la ventana en la que se muestra los módulos y formularios que forma un proyecto. En la
lista de la izquierda seleccionamos el control y en el de la derecha nos mostrará todos los eventos soportados por
VB para ese control. Si sabemos el nombre del control y el del evento, podemos teclearlo directamente o bien si
copiamos código de otro sitio, simplemente con pegarlo, se va a su sitio.
En el caso de querer añadir al código, una función o procedimiento se puede hacer de varias formas, lo acabo de
decir, pero lo repito un poco más claro:
1. Directa: Escribir el código directamente, con lo cual se creará un nuevo "apartado" en la lista de las
funciones/ procedimientos. En caso de que no sea un evento soportado por los controles de nuestro
formulario, se mostrará en la lista de la izquierda, estando seleccionada en la derecha "General"
2. Copiar/Pegar: Pues eso, si copias una función/procedimiento y lo pegas en la ventana de código...
3. Por menú de VB: Según las distintas versiones de VB, será un menú u otro, deberás especificar el
nombre del procedimiento o la función, marcando la casilla correspondiente. En VB4/VB5 verás que aparte
de los Procedimientos (Sub) y las Funciones (Function) hay también Propiedades (Property), estas las
veremos en otra ocasión. También verás que puedes declararlas Públicas o Privadas. Esto no es posible
en VB3, al menos en los procedimientos y funciones de los formularios.
Visual Basic 2 Año 2016 Omar Palermo 10
En otra ocasión veremos todas estas cosas y con ejemplos.
Bueno, todo esto venía a cuento de cómo introducir código en los eventos de los controles o del formulario y cómo
crear nuetras propias instrucciones (esto es lo que más me gustó del QuickBasic 4.0, poder crear mis propias
instrucciones (subs) y funciones).
Ya es hora de coger el listado de la entrega anterior y "destriparlo". Vamos a ver cada cosa por separado, que
aunque parezca que es mucho código, realmente está "repetido", o casi...
Option Explicit
Esto nos obliga a declarar todas las variables que usemos en el módulo, ponerlo es una recomendación, incluso te
la impondría como norma. Para que salga de forma automática en cada nuevo módulo, selecciona del menú
Tools/Advanced la opción Declare variables required (o algo parecido, que viene a significar Requiere la
declaración de variables)
Siguiendo nuestro recorrido por el código, no encontramos con:
Private Sub Command1_Click()
Lo de Private significa que sólo se puede acceder a este procedimiento desde dentro del módulo en el que está
declarado. Es decir no se puede llamar desde otro form o módulo BAS.
Sub indica que es un procedimiento, no una función, ni una propiedad. Los Subs actuan como intrucciones propias
del lenguaje. Las funciones también, pero devuelven un valor, mientras que los subs no devuelven nada, lo que
cogen se los quedan ellos, aunque en su momento veremos que también nos pueden dar algo a cambio.
Command1_Click, este es el nombre que habrá que usar para acceder a él desde cualquier punto de éste
módulo.
Los paréntesis sin nada dentro, indica que este procedimiento no recibe parámetros; los parámetros lo veremos
dentro de un "ratillo"
Toda esta línea es la descripción del procedimiento y cuando se le llame, bien desde nuestro propio código, bien
porque se pulse sobre el botón, se ejecutará todo lo que esté dentro de él. El final del procedimiento está marcado
por End Sub.
Las líneas con DIM indican que estamos declarando las variables y lo que se especifica después del AS es el tipo
de variable, los cinco primeros son de cada uno de los tipos numéricos soportados (otro día veremos otro tipo
cuasi-numérico), el sexto es Variant, el multi-uso, el que vale para todo.
Veamos ahora que es lo que se hace con esta línea:
Dim timer1#, timer2 As Double
Aquí he declarado dos variables del tipo Double. Al separarlas con comas no hay que repetir la palabra DIM, pero
sí el tipo de cada variable. Ya vimos en la entrega anterior que algunos tipos de variables se podían indicar
mediante unos caracteres especiales, (estos tipos son los heredados de versiones anteriores al Visual Basic 2, en
esa versión, se introdujo el tipo Variant), en este caso # es lo mismo que Double, por tanto se podría haber hecho
también de cualquiera de estas tres formas:
Dim timer1#, timer2#
Dim timer1 As Double, timer2#
Dim timer1 As Double, timer2 As Double
Ahora fijate que esta otra no haría la misma tarea:
Dim timer1, timer2 As Double
Esto funcionaría con lenguajes como el C, (realmente el tipo se pone delante), pero en Basic no declara las dos
variables como Double. La segunda variable si es Double, pero la primera es del tipo por defecto, en nuestro caso
Variant.
Así que mucho ojito con las declaraciones de las variables. En algún sitio, no voy a decir dónde, porque lo mismo
fué un "lapsus" del autor, pero decía que de esta forma declaraba tres variables Integer: Dim i, j, k As Integer
Sigamos nuestra andadura, ahora veamos esta declaración/asignación:
Const minBucle = 1, maxBucle = 10
Aquí lo que se declaran son dos constantes, éstas a diferencia de las variables, no pueden cambiar de valor, de
ahí su nombre, por tanto permanecerán siempre con el mismo valor. Cuando se declara una constante, no es
necesario especificar el tipo, VB se encarga de adivinarlo y usar el tipo adecuado, realmente lo que hace es
sustituir estas "palabras" por el valor que hay después del signo igual. En caso de hacer esto: cNombre = "Una
palabra". Visual Basic sabe que es una cadena de caracteres y cada vez que se encuentre con cNombre lo
sustituirá por "Una palabra".
Ahora viene la explicación del "por qué" usar constantes. Además de "esclarecer" los listados, los hace más fáciles
de comprender, también nos permite modificar un valor en un sólo sitio, con lo que ganamos en "confianza", al
asegurarnos de que no omitiremos alguno de los sitios dónde tengamos o queramos cambiar el valor antiguo por
uno nuevo.
En nuetro ejemplo, minBucle y maxBucle se usan en seis partes diferentes del procedimiento, si quisieras probar
con otros valores, tendrías que cambiar en seis sitios esos valores, pero al declararlos como constantes, sólo
cambiando el valor asignado, tenemos todo el trabajo hecho. Esto además de ser más fiable y legible, nos puede
ahorrar algún que otro quebradero de cabeza y si además le añadimos que no ocupan espacio extra, salvo en la
tabla de símbolos, una vez compilado el programa sólo se "compilarán" las constantes usadas. Sin embargo con
las variables no ocurre esto, ya que aunque no se usen, ocupan memoria.
Visual Basic 2 Año 2016 Omar Palermo 11
Un inciso, esto de explicar tan detalladamente los listados, no va a ser norma, ya que al final todos nos
aburriríamos, sólo lo haré cuando lo crea conveniente o bien si lo solicitas, en este caso, no tendré más remedio
que cumplir tus deseos...
Command1.Caption = "Calculando..."
Cambiamos el texto mostrado en el botón, para avisarnos de que está haciendo algo...
timer1 = Timer
Asignamos el valor de la función Timer a la primera de las dos variables que usaremos para calcular el tiempo
empleado por cada uno de los bucles. Esta función devuelve el número de segundos transcurridos desde la media
noche.
For nInt = minBucle To maxBucle
Esto es un bucle, que se repetirá desde minBucle (1) hasta maxBucle (10) y la variable nInt es la que llevará la
cuenta o la que se usará para saber el valor actual del bucle.
Deberíamos ver primero la declaración del procedimiento Contar, para entender lo que hace la línea que viene
después del For.
Private Sub Contar(valor As Integer, etiqueta As Control)
Declaramos un procedimiento privado llamado Contar (actúa como una instrucción más del VB, ya que no
representa a ningún control ni evento), entre los paréntesis están declarados los dos parámetros que espera
recibir en la llamada, el primero es un número entero y el segundo (separado por una coma), un Control, que
puede ser cualquier control de VB.
El resto del procedimiento ahora no es demasiado significativo
Ahora veamos esta línea:
Contar CInt(nInt), Label1
Contar es el nombre del procedimiento y a continuación se deben indicar los parámetros que espera recibir. En
este caso no sería necesario CINT ya que lo que hace esta función es convertir el número que se pone dentro de
los paréntesis en un número entero y como resulta que nInt es un número entero... pues ¡no hay nada que
convertir!
El segundo parámetro es el control Label1, ya sabes que tenemos 6 etiquetas en nuestro programa desde Label1
a Label6
Cuando llegue estos datos al procedimiento Contar, valor tomará lo que valga nInt y etiqueta se adueñará de
Label1.
Next
Continua repitiendo el bucle hasta que se alcance el valor máximo, realmente el Next lo que hace es lo siguiente:
nInt = nInt + 1
¿Es nInt menor o igual que maxBucle? Si la respuesta es SI, sigue con lo que haya después de la línea FOR, en
caso negativo continúa con la línea siguiente al Next (realmente en la siguiente instrucción después del Next, ya
veremos esto en otra ocasión)
timer2 = CDbl(Timer - timer1)
Asignamos a la segunda variable que usamos para el cálculo del tiempo la diferencia entre el nuevo valor de los
segundos transcurridos desde la media noche (Timer) y el valor que tenía timer1, es decir cuantos segundos...
antes de empezar el bucle.
El CDBL es una fución que devuelve un valor Doble. Es decir hace la resta y ese valor resultante lo convierte en
doble.
Label1 = "Duración con Integer: " & timer2
Esta asignación realmente es Label1.Caption, si se omite la propiedad, Visual Basic usa la que tiene por defecto,
que según los manuales, suele ser la propiedad que se usa con más frecuencia. En este caso el Caption, es decir
lo que se muestra en la etiqueta.
DoEvents
Esta es una instrucción "controvertida" y a la que muchos programadores no les hace demasiada gracia usar, no
porque no tenga su utilidad, sino porque hay que saber realmente lo que hace y tener cuidado cuando la usamos,
ya que algunas veces puede crear errores o confusión... reálmente no es tan drástico, pero casi...
DoEvents, detiene la ejecución del programa y devuelve el control a Windows, para que ejecute los mensajes que
tiene pendientes en la cola de mensajes... ¿? no te preocupes si no te enteras, es así y punto. ¿por qué la uso?
Pues para dar tiempo a que se vea el contenido del Label; prueba a quitarla y verás lo que ocurre, o debería
ocurrir... que ya a estas alturas no me sorprendería nada que se mostrara...
El resto del programa es idéntico a este bucle, pero usando distintas variables y las demás etiquetas. El caso es
que Contar espera una variable de número entero y un control, en el caso del control es obvio que se están
poniendo las distintas etiquetas y en el caso del número se convierte a entero, porque eso es lo que espera
nuestra instrucción y si no lo hacemos así, se quejará.
Ya sólo queda ver una línea del procedimiento Contar:
etiqueta.Caption = valor * elMaximo + unDoble
unDoble contará desde 1 hasta elMaximo, en cada vuelta del bucle, se asignará al caption de la etiqueta pasada al
procedimiento y el DoEVents que viene a continuación se encargará de que se muestre ese contenido. Bueno,
también se asigna valor * elMaximo, es decir que cuando valor valga 1, se estará mostrando 1000 + unDoble,
Visual Basic 2 Año 2016 Omar Palermo 12
realmente para hacer un contador se tendría que haber usado lo siguiente:
etiqueta.Caption = (valor -1) * elMaximo + unDoble, para que mostrara desde el 1, en lugar de empezar desde
el 1001.
Una vez que Contar termina, por el End Sub, vuelve el control al bucle que lo llamó y se ejecuta la siguiente
instrucción. Por tanto Contar se llamará tantas veces como dure el bucle en el que se encuentra.
Creo que queda todo más o menos claro y aunque este código no es muy útil por sí, ha servido para ver algunas
cosillas del VB.
Para terminar vamos a ver una serie de cambios y a ver si adivinais que es lo que hace... así os servirá de
ejercicio, cosa que algunos me habeis pedido, pero que aún no es el momento de hacerlos.
En las declaraciones generales añade esta declaración:
Dim Contando As Integer
En Contar, añade lo siguiente después del DoEvents:
If Contando = 0 Then Exit For
Al principio del Command1_Click, añade estas líneas:
If Contando Then
Command1.Caption = "Calcular"
Contando = 0
DoEvents
Exit Sub
End If
Contando = 1
En cada uno de los bucles, pon esto después de llamar a Contar...
If Contando = 0 Then Exit Sub
Y antes del End Sub añade esto otro:
Command1.Caption = "Calcular"
Contando = 0
Bueno, ahí dejo esto y como ejercicio podrías añadir dos controles TextBox para especificar los valores de
maxBucle y elMaximo, de forma que según los valores introducidos, que por defecto deben ser 10 y 1000, se usen
los que especifiques en cada TextBox y se tengan en cuenta cuando pulsas (haces click) en el botón.
Como pista te diré que las variables usadas y/o declaradas dentro de un procedimiento son sólo visibles dentro de
ese procedimiento. No te quejarás del "pedazo" de pista que te he dado...

Para que tengas base suficiente, te voy a contar un poco cómo funciona el Visual Basic y por extensión todas las
aplicaciones de Windows.
En la segunda entrega creamos un programa que mostraba un form en el que teniamos una caja de texto
(TextBox), un botón (CommandButton) y dos etiquetas (Label).
Cuando, después de pulsar F5 o CRTL+F5, se ejecuta la aplicación, de lo único que podemos tener certeza es
que se ejecutará el código que se encuentra en el procedimiento Form_Load. Este procedimiento (sub) en
concreto es lo que se llama un evento, y se produce cada vez que el formulario (form) se carga (load) en la
memoria. Antes de entrar en detalles del porqué podemos tener la certeza de que ese código se va a ejecutar,
tengo que seguir con mi 'ponencia' de cómo funcionan las aplicaciones Windows.
Se dice, (otros lo dicen y yo me lo creo), que Windows es un sistema o entorno operativo 'dominado' por los
eventos. Esto ya lo dejé caer al principio de la segunda entrega. En Windows cada vez que movemos el ratón,
pulsamos una tecla, tocamos una ventana o cualquiera de los controles que están en ellas, se produce un evento.
Cuando se 'dispara', (los anglosajones usan 'fire' para indicar que se produce el evento), uno de estos eventos,
Windows le dice al form, (de forma bastante rápida, aunque en algunas ocasiones no tanto como nos hubiera
gustado), que se ha movido el ratón y que el ratón ha pasado por encima de tal ventana o de ese control y así con
todas las cosas. La verdad, no es de extrañar que de vez en cuando falle el sitema, ¡¡¡es que no para de
disparar!!! y algunos disparos se le puede escapar y......que chiste más malo, ¿verdad? Ya pensabas que el
comentario ese del 'fire' era porque "el Guille se cree que está traduciendo un artículo de VB Online"...
Lo que quiero dejar claro es que a diferencia de los lenguajes que funcionan en MS-DOS, en Windows no
podemos 'predecir' cual será el código que se va a ejecutar. No debes 'planificar' tu programa dando por sentado
que... "después de esto el usuario 'tiene' que hacer esto otro y así yo podré hacer una comprobación para..." ¡ NO
! Aquí (en Windows) no existe la programación lineal, no des nunca por hecho que esto es lo que va a ocurrir...,
porque casi con toda seguridad ¡no ocurrirá!
Veremos cómo podemos 'controlar' que algunas cosas se hagan cuando nosotros queramos, pero esto será sólo
cuando el usuario de nuestra aplicación realice una 'acción' que estaba prevista; también codificaremos para que
se ejecute parte del código cuando el usuario no haga lo que habíamos previsto que hiciera. Pero eso lo iremos
viendo poco a poco...
Todo programa de Windows tiene un punto de entrada; en el caso de Visual Basic, puede ser bien un formulario o
bien un procedimiento de un módulo BAS, (de debe llamarse obligatoriamente Main); los módulos los veremos en
Visual Basic 2 Año 2016 Omar Palermo 13
otra ocasión.
Normalmente las aplicaciones suelen tener más de un formulario y algún que otro módulo. Pero tenga uno o
ciento, siempre hay un único punto de entrada (o de inicio). Por regla general suele ser un formulario. En nuestro
ejemplo sólo tenemos un form en el proyecto, por tanto no hay duda alguna de cual será el punto de entrada del
programa.
Perdona si me extiendo en esto, pero tanto si tú lo sabes como si no, creo que tú ahora lo sabes... (es difícil esto
de escribir una cosa para tanta gente con distintos niveles...)
Cuando Windows inicia el programa, 'debe' cargar el formulario en la memoria. Y desde el momento que se
prepara para hacerlo, ya está con los 'tiritos' y mandando 'mensajes' a la ventana (todo formulario es una ventana),
pero no sólo le está avisando a la nuestra que la acción ha empezado, sino que lo hace prácticamente a los cuatro
vientos; si otra aplicación quiere enterarse de lo que ocurre en Windows, sólo tiene que conectarse a la
'mensajería' de éste entorno operativo y leer las 'noticias'... pero esto ya es complicarse la vida y todavía no nos la
vamos a complicar tanto... o más... (pensará alguno después de respirar aliviado...)
Lo que ahora interesa es saber que el 'evento' Form_Load se produce cuando esta ventana pasa del anonimato a
la vida pública, aunque no la veamos, estará en la memoria de Windows y se producirá el primer evento del que
tenemos 'certeza', por tanto este es un buen sitio para poner código de inicialización.
Realmente el Form_Load no es lo primero que puede ocurrir al iniciarse un formulario, pero por ahora vamos a
pensar que sí; porque sino esta entrega no la termino hasta después del verano... ¡¡¡que me gusta darle vueltas a
las cosas!!!
Ahora que se ha cargado el form en la memoria... ¿que ocurrirá? Pues, si el usuario se va a tormar unas cañas:
nada. Sólo ocurrirá algo cuando interactuemos con el form, es decir, le demos razones a Windows para 'pegar
tiritos'.
Nuestra aplicación, (te recuerdo que tenía, entre otras cosas, un textbox y un botón), esperará a que se produzca
algunas de las acciones para las que está preparada.
Y la pregunta es ¿que es lo que puede ocurrir? Para saber 'todas' las cosas que pueden ocurrir en nuestra
ventana, (no recuerdo si has pulsado F5 o no), finaliza el programa y muestra la ventana de código.
En la parte superior de la ventana hay dos listas desplegables, la de la izquierda tiene todos los controles, (en otra
ocasión veremos que no siempre es así), que tenemos en el form, incluido éste, además de uno especial que se
llama General.
Pulsa en la lista de la izquierda, para que se despliegue.
Estos son los cinco controles, incluyendo el form, que pueden recibir mensajes de Windows. Pulsa en cualquiera
de ellos. En la lista de la decha están todos los procedimientos (eventos) soportados por el control de la izquierda.
Cada control mostrará los eventos que VB y/o Windows permite que se produzcan. Estos ocurrirán por distintos
motivos, cada uno tiene su 'tarea', nos pueden avisar de que se ha pulsado el ratón, que se acaba de pulsar una
tecla, que se ha modificado lo que antes había, etc.
Una llamada a la precaución.
Los eventos son 'procedimientos' y no sólo se puede llegar a ellos porque se produzca una acción del usuario o
del propio Windows y si me apuras, incluso de Visual Basic... Nosotros podemos 'provocarlos' ¿cómo?
simplemente haciendo una llamada al SUB que queramos o actuando en el control de manera que ocurra alguno
de los eventos soportados.
Por ejemplo, en el Form_Load tenemos que se asignan cadenas vacias tanto al Label2 como al Text1:
Label2 = ""
Text1 = ""
Cuando VB asigna una cadena vacía (o cualquier otra cosa), al Label2 está borrando el contenido del 'Caption' de
esta etiqueta y asignando algo nuevo, es decir lo está cambiando. En nuestro programa no ocurre nada, salvo que
se borra lo que allí hubiera, pero realmente se está produciendo el evento Label2_Change, porque hemos
cambiado el contenido. VB sabe que no hemos escrito código para manejar esta situación, asi que... hace la vista
gorda y simplemente cambia el contenido del Label2.Caption sin hacer nada más.
Pero al asignar una cadena vacía al Text1, también se borra el contenido y se produce un Change, sólo que en
este caso, al no tener Caption, lo que se borra es el Text; de todas formas sea como fuere, se produce el evento
Text1_Change. Nuestro querido amigo Visual Basic, sabe que hemos escrito código para este evento y sabe que
tiene que hacer lo que allí se dice...
En este caso, nuestro código se ejecuta, pero realmente no hace nada de interés o por lo menos nada que se
pueda apreciar de forma visual. No voy a explicar para que sirve ese código, porque ya lo hice en su día, lo que
espero es que hoy lo entiendas mejor...
¿Cómo? que no sabes de qué código estoy hablando... pues del ejemplo de la segunda entrega, creo que ya lo
dije al principio... a ver si prestamos más atención y dejamos de pensar en las vaciones... ¡estos niños!
El código interesante es el que se ejecuta cuando se pulsa en el botón:
Label2 = "Hola " & Text1
Aquí se asigna al Caption del Label2 lo que hay después del signo igual, en este caso actúa 'casi' como una
variable. Y ya sabrás que antes de asignar un valor a una variable, se procesa todo lo que está después del signo
igual, por tanto, Visual Basic tomará el contenido del Text1, es decir lo que se haya escrito y lo unirá (&) a la
palabra "Hola ", una vez hecho esto, lo almacena en el Caption del Label2.
Visual Basic 2 Año 2016 Omar Palermo 14
Y si en lugar de guardarlo en un control label, lo asignaramos a una variable... y si en lugar de escribir nuestro
nombre, escribiesemos un número... y si la variable en la que guardamos ese número se llamara, por ejemplo,
maxBucle o elMaximo...
Pues que tendríamos una parte resuelta de la tarea esa que puse como ejercicio en la entrega anterior.
Pero, este form de la segunda entrega no nos sirve. Tendremos que cargar el de la vez pasada y añadirle un par
de cajas de textos y un par de etiquetas, para que indique lo que se debe introducir en cada textbox;
Pero nos encontramos con un problema: ¿cómo puedo asignar un valor a maxBucle, si las constantes no pueden
cambiar de valor? Fácil, conviertela en variable. Pero debes recordar la pista que di al final: "Las variables
declaradas dentro de un procedimiento son solamente visible dentro de ese procedimiento".
De este tipo de variables se dice que son locales.
Alumno: ¿Que significa esto?
Guille: Que sólo pueden usarse dentro del procedimiento en el que se han DIMensionado o declarado.
A: Vale, "mu bonito" y ¿que pasa?
G: Esto... que no pueden usarse en otro sitio...
¿Recuerdas la recomendación de usar Option Explicit?
Pues gracias a Option Explicit, se solucionan el 99% de los fallos 'involuntarios' con las variables... y no exagero!!!
Es super-fácil escribir de forma incorrecta el nombre de una 'bariable' y no vengas con el cuento de que a tí nunca
te ocurre, porque no me lo voy a creer... bueno, de tí si, pero no todos son tan minuciosos como tú...
(para que nadie se sienta ofendido/a, quiero que veas en esto que acabo de poner... la intención que tiene, es
decir que me dirijo a más de un "ti"... ya sé que no eres tan torpe como para no haberlo 'captado', pero con esta
aclaración me quedo más tranquilo.)
Según cómo y dónde se declare una variable, su 'visibilidad' o área de cobertura, será diferente... también se
puede usar la palabra ámbito... es que como en las emisoras de radio se habla de la cobertura... pues...
Una variable puede tener estas coberturas:
--Privada o Local a nivel de procedimiento (Sub, Function, etc.)
--Privada o Local a nivel de módulo (FRM, BAS, etc.)
--Pública o Global a nivel de aplicación (en este tipo hay una forma especial de usar las variables que veremos en
otra ocasión)
Explicando los dos primeros puntos.
Cuando declaramos o dimensionamos una variable 'dentro de' un procedimiento, ésta sólo es visible en ese
procedimiento; fuera de él no es conocida y cualquier uso que se intente hacer de ella, producirá un error... si has
sido obediente y has usado el Option Explicit. En caso de que no hayas puesto la 'obligación' de declarar todas las
variables, te llevarás una sorpresa de que no tiene el valor esperado.
A: ¡JA!
G: ¿No te lo crees? Vale. Vamos a comprobarlo.
Abre un proyecto nuevo, pon un textbox y un botón..
Abre la ventana de código, borra el Option Explicit.
En el Form_Load haz esta asignación: Incredulo = "No me lo creo"
En el Command1_Click escribe esto otro: Text1 = Incredulo
Pulsa F5 y haz click en el botón...
¿Que ha pasado?
(Tengo que comprobarlo, para no meter la pata, pero se supone que el texto se borrará sin poner nada...)
Bien, eso ocurre porque la variable usada en el Form_Load no tiene nada que ver con la usada en el
Command1_Click.
Con esto comprobamos o demostramos que podemos tener variables diferentes con el mismo nombre. La única
condición es que no pueden estar juntas, aunque hay un truco para juntarlas sin que ellas se enteren...
En este último ejemplo, nuestra intención es tener una variable que sea 'conocida' en todo el form. Cuando
necesitemos variables con un ámbito a nivel de módulo, tenemos que declararla o dimensionarla en la sección de
las declaraciones generales; ya sabes, en la lista izquierda de la ventana de código seleccionas General y en la de
la derecha Declarations ( Declaraciones).
Muestra la ventana de código y en General/Declaraciones escribe:
Option Explict 'retornamos a las buenas costumbres
Dim Incredulo As String
Pulsa F5 para ejecutar el programa, pulsa en el botón y... ¡AHORA SI!
Tenemos una variable que puede ser 'vista' en todo el form. Ya puedes usar 'Incredulo' donde quieras, ahora
siempre será la misma variable y contendrá lo último que se le haya asignado.
A partir de la versión 4 del VB, entra en juego una nueva palabra, 'Private', que suele usarse en las declaraciones
de las variables a nivel de módulo, de esta forma es más fácil entender la intención de la misma; por tanto la
declaración anterior podemos hacerla también así:
Private Incredulo As String
Hay veces, sobre todo si ya has programado antes en MS-DOS, que usamos variables como a, b, c, i, j, k...etc., (al
menos yo estoy acostumbrado a llamar i, j, k a las variables que uso en los bucles FOR), cuando hago un bucle
dentro de un procedimiento uso i como variable índice, (o variable 'contadora'), pero ¿que ocurre si esta
Visual Basic 2 Año 2016 Omar Palermo 15
'costumbre' quiero aplicarla en varios procedimientos? Pues que dimensiono una variable i en cada uno de ellos y
aquí no ha pasado nada!!!
Usar el mismo nombre de variable en distrintos procedimientos
Como indica el encabezado de este nuevo párrafo, cosa que ya he comentado antes, podemos tener distintas
variables con el mismo nombre en distintos procedimientos; para ello, sólo hay que dimensionarlas y el VB las
almacenará en distintas posiciones de la memoria para que no se 'mezclen'.
En la entrega anterior, teniamos un procedimiento llamado Contar que se usaba para eso, contar...
En este ejemplo vamos a usar un sub también llamado contar, para ver en acción todo esto que estoy diciendo.
Situate en la parte General/Declarations de la ventana de código y escribe o "copia/pega" lo siguiente:
Private Sub Contar()
Dim i As Integer
For i = 1 To 2
Print "i en contar= "; i
Next
End Sub
Ahora en el Form_Load, escribe esto otro:
Dim i As Integer
Show
For i = 1 To 3
Print "i en el Form_Load= "; i
Contar
Next
Ejecuta el programa y ...
Has visto lo que ocurre... A pesar de que ambas variables tienen el mismo nombre, son diferentes. La variable i del
Form_Load no es la misma que la variable i de Contar.
Cuando usamos variables locales es como si cambiasemos el nombre y se llamaran
NombreProcedimiento_Variable.
Sé que puede ser una forma 'rebuscada' de explicarlo, pero asi te haces una idea.
Todas las variables declaradas en un procedimiento, sólo son visibles en ese procedimiento. Si has usado
QuickBasic o el compilador Basic de Microsoft (que usaba el QuickBasic Extended QBX), esto ya existía y también
existía la forma de hacer que una variable declarada en un procedimiento, fuese visible fuera de él; para ello
declarabas la variable como Shared (compartida); pero en VB eso NO EXISTE. La única forma de compartir una
variable es declarándola en la sección General de las declaraciones.
Prueba ahora esto. Sustituye el procedimiento Contar por este otro:
Private Sub Contar(j As Integer)
Dim i As Integer
For i = 1 To j
Print "i en contar= "; i
Next
End Sub
Aquí hacemos que Contar reciba un parámetro y el valor que recibe lo usamos como el límite final del bucle For,
es decir contará desde UNO hasta el valor de j.
Sustituye la llamada a Contar del Form_Load por esta:
Contar i
Le damos a Contar el parámetro i. Por tanto cada vez que se llame a este procedimiento le estamos diciendo que i
es el valor máximo que tomará el bucle For que tiene dentro.
¿Cómo reaccionará? ¿Se confundirá? ...
No, no voy a dejarlo para la siguiente entrega, es tan obvio que lo voy a explicar ahora mismo:
Al procedimiento Contar le da igual que se use una varibale llamada i o cualquier otra, incluso un número. Lo único
que necesita y espera, es recibir un valor numérico (del tipo Integer) y lo asignará a la variable j. Por tanto no
ocurrirá nada extraño. Ejecuta el programa y fijate en lo que ocurre. Sé que lo has deducido, eso está bien... vas
aprendiendo... 8-)
¿Cómo? ¿Que tú aún no lo has captado? Pues dimelo...
Otra cosa sería pretender usar una variable declarada a nivel de módulo, dentro del procedimiento, que tuviese el
mismo nombre.
Si te has quedado 'con la copla', tu mismo sabrás la respuesta... ¡Efectivamente! Si dentro de un procedimiento
tenemos una variable dimensionada con el mismo nombre de una declarada a nivel de módulo o a nivel global,
(para usarla en cualquier sitio de la aplicación), tendrá 'preferencia' la variable local... Ésta 'tapará', ocultará o
como prefieras decirlo a cualquier otra variable del mismo nombre...
En la próxima entrega veremos más casos y cosas de las variables. Comprobaremos cómo usarlas a nivel Global.
Pero por ahora vamos a terminar con el programa que teníamos planteado en la entrega anterior:
Visual Basic 2 Año 2016 Omar Palermo 16
Aunque relativamente deberías saber cómo solucionarlo...
Lo que seguramente no sabrás, es cómo hacer que estas variables tomen el valor...
De acuerdo, lo explicaré. Carga el ejemplo de la cuarta entrega.
Hay varias soluciones a este problema; una sería usar variables locales, esta decisión no 'justifica' el 'pedazo' de
pista que te di... pero esto es lo que hay.
La línea Const minBucle = 1, maxBucle = 10. Debe quedar asi:
Const minBucle = 1
Dim maxBucle As Integer
Esto hará que maxBucle deje de ser una constante y pase a ser una variable, con lo cual podremos asignarle
cualquier valor.
Pero, ¿cómo le asignamos el valor?
Vamos a dar por sentado que lo que se escriba en el Text1 será el valor que debe tener maxBucle; entonces lo
único que haremos es asignar a maxBucle lo que se escriba en el Text1...
Vale, pero ¿dónde?
Pues... por ejemplo, después de la declaración, asi que en la siguiente línea al Dim maxBucle... escribe los
siguiente:
maxBucle = Text1
Esto en teoría no daría problemas, al menos en condiciones normales, ya que el contenido de un textbox es del
tipo Variant y ya vimos que un Variant puede almacenar cualquier cosa, por tanto si es un número 'intentará'
convertir al tipo de la variable que recibirá el valor.
Esto no siempre funciona, sobre todo si el contenido del Text1 no es numérico. Por ahora vamos a hacerlo simple,
si el usuario (en este caso tú), escribe algo no numérico lo vamos a considerar CERO... o casi...
Cambia la asignación anterior por esta otra...
¡¡¡ ALTO !!! Antes de hacerlo, pruebalo e intenta escribir una palabra en lugar de un número... ¿que ocurre?
Pues que VB no se complica la vida y te dice que 'nones'... (realmente dice Type Mismatch... Error de Tipos, es
decir que lo que has escrito no es capaz de convertirlo a Integer)... así que escribe esto otro:
maxBucle = Val(Text1)
Con esto lo que hacemos es convertir el contenido del Text1 a un VALor numérico y lo asignamos en la variable...
¿Problemas? Que el valor sea mayor del que se puede guardar en un Integer, pero eso ya no es asunto de esta
entrega...
Ahora nos queda convertir elMaximo en variable y asignarle el valor que hay en el Text2. ¡Efectivamente! hacemos
lo mismo, sólo que esta vez dentro del procedimiento Contar, por tanto la declaración Const elMaximo = 1000&, la
tienes que quitar y poner estas dos líneas:
Dim elMaximo As Integer
elMaximo = Val(Text2)
Aquí el único inconveniente es que esta asignación se hace cada vez que se entra en este procedimiento... y eso,
amigo mio, no es un buen estilo de programación... Sobrecargamos de forma innecesaria al procesador... ten en
cuenta que la conversión a número y la asignación ¡¡¡se ejecuta cada vez que se entra en Contar!!!
Lo mejor para este caso sería declarar elMaximo como variable a nivel de módulo. Por tanto, borra el Dim
elMaximo... del sub Contar y colocalo en la parte de las declaraciones generales del form.
Ahora... ¿dónde asignamos el valor para evitar la sobre-carga? Ya que tenemos la variable a nivel de módulo, ésta
será 'vista' en todos los procedimientos del formulario, por tanto lo lógico sería hacerlo en el Command1_Click, ya
que cuando nos interesa a nosotros saber cuanto tenemos que contar es precisamente cuando pulsamos en el
botón...
Pero... ¿dónde exactamente?, después de Contando = 1
Bien, ahora está la cosa mejor... haz tus pruebas y si aún no lo tienes claro... preguntame.
Prácticas y ejercicios
¿Quieres algo para practicar?
Este ejercicio se lo ponía a mis alumnos, cuando daba clases de BASIC, hace ya unos 10 años o más... y
consistía en pedir el nombre, pedir la edad y mostrar el nombre tantas veces como años tengamos...
Claro que con el BASIC del MS-DOS era más directo y se sabia cuando se debia empezar a mostrar el nombre,
para solventar esto, se mostrará el nombre 'edad' veces cuando se pulse en un botón.
No creo que sea complicado, así que vamos a complicarlo un poco más:
Mostrar el nombre 'edad' veces, dentro de un label, para ello el label deberá ocupar la parte izquierda del form.
Y una tercera versión, sería lo mismo que esta última, pero cada vez que se muestre el nombre se haga en una
línea diferente.
La pista: En la segunda entrega vimos de pasada el CHR. Pues decirte que si añadimos a una variable un
CHR(13), lo que hacemos es añadirle un retorno de carro, es decir lo que venga después se mostrará en la
siguiente línea... siempre que se 'concatene'. También existe una constante definida en VB4 o superior que es
vbCrLf, esto es un retorno de carro (Cr) y un cambio de línea (Lf)

Apéndice Quinta Entrega


Visual Basic 2 Año 2016 Omar Palermo 17
Y es que con tanta explicación de la Quinta Entrega, al final expliqué cosas, pero no todo.
Al final de la Cuarta Entrega se hicieron una serie de cambios al programa ese de los bucles. La explicación de la
utilidad de todo ese código es, en términos generales, para cancelar en cualquier momento el programa.
¿Cómo?
Hemos declarado Contando a nivel de módulo, por tanto será visible en todo el form.
Cuando se declara una variable, el VB le asigna siempre un valor predeterminado, en caso de las cadenas de
caracteres (string) ese valor es "" (cadena vacía); a los números se le asigna un valor CERO. Por tanto, al iniciar el
programa, la variable Contando valdrá CERO, así que la primera vez que se pulse en el botón Command1 y se
compruebe esto:
If Contando Then... Ah, si... no he explicado para que sirve esto del If...Then...
Las instrucciones If/Then se usan para comprobar si la 'expresión' que se usa después del IF se cumple o no...
En caso de que se cumpla (sea cierta), se ejecutará todo lo que haya después de THEN... algo así:
IF <EXPRESIÓN> THEN <INSTRUCCIONES>
También puede usarse de esta otra forma:
IF <EXPRESIÓN> THEN
<INSTRUCCIONES>
[<MÁS INSTRUCCIONES>]
END IF
En la primera forma de uso, las instrucciones se ponen a continuación de THEN com en el caso de:
If Contando = 0 Then Exit For
En la otra forma de usarlo, las instrucciones se ponen en las siguientes líneas y podemos escribir tantas líneas
como queramos. Todas se intentarán procesar...
Esto para el caso de que al evaluar la expresión se cumpla como verdadero.
En las ocasiones en las que no se cumple la expresión se hará lo siguiente, según la forma de usar el IF/THEN:
--En el primer método, se procesan las intrucciones que hay en la siguiente línea y se continúa a partir de ahí el
programa.
--En el segundo caso, se busca END IF y se continúa por la siguiente línea...
Más adelante, en otra entrega, veremos otras formas de usar IF...THEN...
Ahora voy a explicar un poco esto de la evaluación de las expresiones.
El IF espera encontrar un valor CERO (FALSO) o un valor distinto de cero (por extensión VERDADERO)
En If Contando = 0 Then Exit For, la expresión es Contando = 0, aquí no se está usando la
asignación, sino que se está evaluando si el contenido de la variable Contando es igual a cero; en caso de que
sea cierto, es decir que Contando valga cero, (algunas veces me maravillo de mi lógica aplastante...), se devuelve
un valor Verdadero (TRUE).
Por otra parte si Contando NO vale cero, se devolverá un valor Falso (FALSE, CERO).
En otros lenguajes se usa un símbolo distinto para el igual, según sea una asignación o una comparación.
En Pascal (Delphi) la asignación es := y la comparación es =
En C, C++, la asignación es = y la comparación == (esto del == puede 'sonarte' si has hecho algo con
JavaScript)
En el caso de If Contando Then, al no haber una expresión que evaluar, lo que se comprueba es si el
contenido de la variable es cero o no es cero, en caso de que sea NO CERO, se cumple la condición y se procesa
todo lo que viene a continuación; pero si la variable vale CERO, el IF lo interpretará como FALSO y pasa a
ejecutar lo que haya a continuación de End If.
Por tanto, (volviendo por fin al listado), la primera vez que se pulse en el botón Command1 y se compruebe esto:
If Contando Then
No se cumplirá la condición, ya que el contenido de Contando es cero, y se pasará a lo que hay después del End
If, es decir a:
Contando = 1
y se continúa con el programa como antes de añadir todas estas cosas...
Pero, si pulsas de nuevo en el botón, se vuelve a procesar lo que hay dentro de Command1_Click, (esto es
posible porque al usar DoEvents, permitimos que Windows siga recibiendo y procesando 'tiritos'), pero cuando
entramos por segunda vez, Contando vale 1 (uno), (ya que al ser una variable a nivel de módulo conserva el
último valor que hayamos asignado), y esta vez al evaluar:
If Contando Then
si se cumple, así que se procesan las líneas que vienen a continuación... entre ellas está Contando = 0 y
DoEvents que vuelve a permitir a Windows que las otras cosas que antes de pulsar en el botón continúen
ejecutándose, (esto se hace de forma asíncrona, es decir, Windows se da por enterado y da los avisos
(mensajes) cuando quiera), pero continúa con la siguiente instrucción sin esperar a que esos mensajes se
terminen de procesar, entonces se encuentra con el Exit Sub que lo manda fuera del evento... (Si no te ha
quedado demasiado claro, no te preocupes veremos más de esto a lo largo del curso...)
Visual Basic 2 Año 2016 Omar Palermo 18
En caso de que hayamos pulsado el botón cuando aún no había terminado todo lo que este Sub estaba haciendo,
se continúa igual que si se hubiese hecho PAUSA y después PLAY.
Con la salvedad de que si VB se encuentra con alguno de los If Contando = 0 Then Exit Sub, dejará de procesar y
se saldrá del procedimiento... Esta no es la mejor forma de cancelar una tarea ya iniciada, pero algo es algo...
También es posible que al pulsar por segunda vez en el botón, se estuviese dentro del Sub Contar, en este caso,
también se evaluaría la expresión y se saldría del procedimiento... así que también dejaría de procesarse todo.
Cuando pulsemos por tercera vez... iniciaremos el proceso de nuevo...
Forma de especificar las instrucciones en Visual Basic
Las instrucciones en Basic no tienen porqué estar cada una en una línea. Se pueden escribir varias instrucciones
en la misma línea, pero separándo cada una de ellas con el signo : (dos puntos).
Cuando VB encuentra los dos puntos, deja de 'interpretar' la instrucción y pasa a la acción, una vez traducido a su
lenguaje interno, toma lo que hay después del signo : y sigue su camino en busca de más instrucciones o el final
de la línea.
Veamoslo de forma práctica:
Nombre = "Pepe" : Print Nombre
Esta línea tiene dos instrucciones: una asignación y una instrucción Print.
Podemos poner cuantas instrucciones queramos, separadas con los dos puntos.
Pero, (siempre hay un pero), si una de las instrucciones es el IF/THEN la cosa puede cambiar...
Ya vimos que IF comprueba la expresión que viene a continuación, si es cierta, ENTONCES procesa lo que haya
después de THEN. En caso de ser en la misma línea, interpretará todas las instrucciones que estén a
continuación; en caso de ser un bloque IF... THEN... END IF, ejecutará todo lo que esté dentro de ese bloque.
Ahora bien, si la expresión es falsa pasa a la siguiente línea, tanto si es o no un bloque. En el caso del bloque la
siguiente línea a interpretar será la que esté después de END IF.
En los tiempos del BASIC interpretado de MS-DOS, era habitual encontrar las líneas con varias instrucciones
separadas por dos puntos. En mi caso, cuando empecé a usar el QuickBasic 2.0 y al poder usar bloques IF...
THEN... END IF, fui dejando a un lado el "mogollón" de instrucciones en la misma línea... Ahora prácticamente y
salvo contadas excepaciones, escribo cada instrucción en una línea.
Después de este pequeño respiro, veamos cómo estaría formada una línea de VB para usar con un IF... THEN...
[instrucciones:] IF <expresión> THEN <instrucciones si es cierto [:más
instrucciones...]>
A continuación de THEN podemos incluir cuantas instrucciones queramos, separadas por dos puntos.
Estas sólo se ejecutarán cuando la expresión sea cierta. Si el resultado de la expresión es falso, se obvia 'todo' lo
que hay después de THEN y se pasa a la siguiente línea.
Espero que lo hayas asimilado y que no te indigestes con lo siguiente...
Pero, (...), existe otra instrucción que PUEDE acompañar al IF... THEN... y es para los casos en los cuales el
resultado de la expresión sea FALSO.
Si, ya sé que dije que cuando es falso se pasa a la siguiente línea, pero eso es cuando no se usa la cláusula
ELSE.
Con ésta, la definición de la instrucción "tomadora de decisiones" quedaría así:
IF <expresión> THEN <si se cumple> ELSE <si no se cumple>
Tanto en <si se cumple> como en <si no se cumple> pondremos tantas instrucciones como queramos,
(separadas por dos puntos).
Pero no te recomiendo que lo hagas, es preferible, al menos para darle "claridad" a nuestro código, usar el bloque:
IF <expresión> THEN
<si se cumple>
ELSE
<si no se cumple>
END IF
Sé que esto puede ocupar más líneas de código, pero nuestro "coco" lo agradecerá, ya que es más fácil de
comprender, sino veamos un ejemplo:
IF numero > limite THEN
Print "tu número es grande"
ELSE
Print "OK, McKey!"
END IF
Ahora veamoslo en una sóla línea:
IF numero > limite THEN Print "tu número es grande" ELSE Print "OK, McKey!"
En este ejemplo, aún queda claro, pero lo podríamos complicar con más instrucciones para ambos casos, es decir,
para cuando la expresión es cierta y también cuando es falsa.
En los tiempos del BASIC que venían incorporados con los ordenadores, cada línea del programa había que
numerarla, ya que todo lo que se escribía sin número de línea, se ejecutaba inmediatamente; al igual que ocurre
con lo que se escribe en la ventana Inmediate del Visual Basic.
Visual Basic 2 Año 2016 Omar Palermo 19
Los números de líneas se usaban, además de porque era obligatorio, para cambiar el orden de ejecución, sobre
todo después de una comparación. De esta forma, aún sin tener bloques IF/THEN/ELSE/END IF, se podían
simular.
¿Cómo se lograba?
Usando una instrucción que muchos creen que es "indecente, antisocial, etc"
La estrella del Basic: (redoble de tambores) "GOTO"
A partir de hoy, más de un purista de la programación no me dirigirá la palabra... pero no me importa...
Se ha "denostado" (injuriado) con exageración el uso de esta instrucción. Realmente es una instrucción de
"bifurcación", es decir, sirve para "IR A" otro sitio de nuestro programa.
Su uso ha sido el más criticado de los NO PARTIDARIOS del Basic y siempre han basado sus críticas en el
exagerado uso del GOTO en todos los programas Basic. Pero aclaremos que C también tiene esta instrucción y
que cualquier programa en código máquina (ensamblador) está "plagado" de JMP que para el caso es lo mismo
que el sufrido GOTO, realmente una instrucción GOTO número_linea se convierte en JMP número_linea.
No voy a recomendar el uso del GOTO, para nada, ya que hoy día es innecesario su uso. Antes no teníamos más
remedio, porque el BASIC no disponía de instrucciones para poder estructurar el código. Pero sería una tontería
querer hacer creer que no existe esta instrucción. Sabiendo que está y cómo podemos evitarla, es preferible a
decir que no existe y si por casualidad la descubres... a que la uses.
Por tanto, insisto en mi recomendación, (de esta forma los PURISTAS volverán a dirigirme la palabra), NO USES
EL GOTO, ni aún cuando creas que no tienes más remedio... aunque, aquí entre nosotros, algunas veces es más
cómodo usarlo... pero que no se entere nadie...
Este es un programa de los de antes, sirve para mostrar en pantalla los números del 1 al 10 y sin usar el
FOR/NEXT
10 A = 1
20 Print A
30 A = A + 1
40 IF A <= 10 THEN GOTO 20
'Con el Commodore este programa se solía escribir así:
10 A=1
20 PRINTA:A=A+1:IFA<=10GOTO20
'Sin ESPACIOS NI NADA... TODO APELMAZADO... ¿que más daba usar el GOTO?
En este ejemplo, es obvio que podríamos sustituirlo con:
10 For A = 1 To 10
20 Print A
30 Next
El NEXT hace lo mismo que la asignación y la comparación.
Pero hay otras maneras, para ello existe una serie de instrucciones que funcionan de manera similar, veamos
otros ejemplos con más instrucciones para hacer bucles.
10 A = 1
20 While A <= 10
30 Print A
40 A = A + 1
50 Wend
El WHILE/WEND ya casi ni se usa, porque tienen un sustituto más versátil, ahora lo veremos, pero el uso sería:
WHILE <expresión>
<instrucciones si se cumple>
WEND
Es decir, MIENTRAS la expresión sea cierta, repite todo lo que haya hasta el WEND.
Por supuesto podemos ponerlo todo en una sóla línea:
10 A = 1 : While A <= 10 : Print A : A = A + 1 : Wend
Pero esto tampoco es recomendable, queda algo "difuso"...
El WEND funciona como IF A <=10 THEN GOTO xxx, con la ventaja que evitamos el GOTO y lo que hace es
comprobar si la expresión que hay tras el WHILE es cierta o no, en caso de que sea verdadero el resultado, (ya
sabes, distinto de CERO), se ejecutará todo lo que hay entre WHILE y WEND. Estas instrucciones ya existían en
el GWBASIC (el basic de los PCs)
Hay que tener cuidado con esto de que la expresiones evaluan el cero como FALSO y cualquier otro valor como
VERDADERO, por ejemplo:
A=1 o
While A While 1
Print A Print A
A=A+1 A=A+1
Wend Wend
Visual Basic 2 Año 2016 Omar Palermo 20
En ambos casos el bucle sería "infinito", se detendría en un momento dado, ya que llegaría a "desbordarse" el
valor máximo y en ese momento el programa acabaría por producirse un error... pero prueba esto y verás:
While 1
Print "Hola Mundo"
Wend
Esto nunca finalizará, salvo que pulses CRTL+BEAK (o INTERrumpir), para detener el programa.
Más instrucciones para hacer bucles
Con el QuickBasic 3.0, (yo no llegué a tenerlo, pero creo que fue ahí dónde se introdujo), entró en funcionamiento
una nueva forma de hacer bucles:
DO... LOOP
El último ejemplo podríamos escribirlo así:
Do
Print "Hola Mundo"
Loop
Pero la "gracia" de este bucle es que podemos usar dos nuevas cláusulas para ver cuanto durará el bucle.
La primera es WHILE y funciona igual que en WHILE/WEND
A=1
Do While A <= 10
Print A
A=A+1
Loop
La ventaja es que WHILE se puede poner tanto después de DO como a continuación de LOOP.
Si lo usamos como DO WHILE <expresión>... la forma de actuar es igual que WHILE/WEND, se evalua la
expresión y sólo en caso de que sea cierta, se ejecta lo que está dentro del bucle, es decir entre DO y LOOP.
Pero si evaluamos la expresión en LOOP, se ejecutará todo lo que hay tras el DO, como mínimo una vez y se
seguirá repitiendo si se cumple la condición. De esta forma, como he dicho, se ejecutará el contenido del bucle,
como mínimo una vez. Veamos un ejemplo:
A=1
Do
Print A
A=A+1
Loop While A <= 10
El problema es que si A, en lugar de valer 1, tiene un valor superior a 10, también se ejecutará, una vez.
A = 11 : Do : Print A: A = A + 1: Loop While A <= 10
Que mal queda en una sóla línea, ¿verdad?
Pero con DO/LOOP también puede usarse UNTIL, en este caso, el bucle se repetirá HASTA que se cumpla la
expresión
A=1
Do Until A > 10
Print A
A=A+1
Loop
Fijate que la expresión ha cambiado de <= (menor o igual) a > (mayor), ya que ahora se evalúa de esta forma:
Hasta que A sea mayor que diez, REPITE todo hasta LOOP. También podemos usarlo después del LOOP:
A=1
Do
Print A
A=A+1
Loop Until A > 10
Aquí hago la misma aclaración que antes, si el valor incial de A es más de 10 se ejecutará mínimo una vez.
Realmente para contar de forma secuencial y prácticamente para casi todo tipo de bucle, no es necesario hacer
los bucles con DO/LOOP, ya que FOR/MEXT lo hace bastante bien.
Sigamos con estos bucles, pero en lugar de contar de menor a mayor, vamos a contar "pa trás", es decir de mayor
a menor... quién sabe, lo mísmo necesitas hacer un programa que cuente al revés...
A = 10
Do While A >= 1
Print A
A=A-1
Loop
Cuando se muestre el 1, A=A-1 se convertirá en A = 0 y la comparación A >= 1 no se cumplirá, por tanto dejará de
repetirse el bucle, pero esto también se puede hacer con FOR/NEXT:
For A = 10 To 1
Visual Basic 2 Año 2016 Omar Palermo 21
Print A
Next
El único inconveniente es que NO SE REPITE NI UNA VEZ... ¿Por qué?
Porque si no se le indica lo contrario, FOR/NEXT siempre cuenta de forma ascendente y cuando ve que A debe ir
de 10 hasta 1 y que eso no es ascendente... pasa de ejecutar el bucle. Esto es una cosa a tener en cuenta, FOR
siempre evalúa los valores del bucle que tiene que hacer y si no está entre los valores que debe, no se ejecuta ni
una sola vez. En este caso debe empezar por DIEZ y llegar hasta UNO, así que se da cuenta de que ya ha
terminado... incluso sin haber empezado... ¡que listo es el FOR!
Para que el FOR cuente hacia atrás, necesitamos un nuevo peldaño (esto en inglés quedaría "clavado"), en la
escala evolutiva del FOR/NEXT (ahí queda eso!!!)
Se necesita la palabra STEP para indicarle que no queremos ir de uno en uno de forma ascendente, en nuestro
ejemplo lo usariamos así:
For A = 10 To 1 Step -1
Print A
Next
De esta forma contará desde 10 hasta 1, restando uno en cada repetición. Pero, ¿que hacer si queremos usar
otros valores? Simplemente ponerlo después de STEP, por ejemplo:
For A = 10 To 1 Step -1
For A = 1 To 10 Step 3, etc, etc.
Insisto, todo esto está muy bien, pero en la práctica usaremos otras cosas además de contar de forma lineal, con
incrementos o sin ellos... habrá veces que queramos salir de un bucle.
Ya lo hemos visto, por ejemplo Exit Sub salía del procedimiento, ¿recuerdas el Exit For?
Para salir de los bucles podremos Exit y a continuación For, Do, etc. Pero NO podremos salir de un bucle
WHILE/WEND.
Ya veremos ejemplos para estos casos y otros que surgirán más adelante.
Bien, creo que ya hemos dado demasiadas vueltas con tanto bucle, para terminar:
1) Haz un programa que al pulsar en un botón (CommandButton) haga un bucle entre dos valores que habrás
introducido por medio de dos cajas de textos (una para el inicio y otra para el final)
2.) Otro que tenga una tercera caja de textos y que el valor introducido en ella sea el incremento.
3.) Como tercer ejercicio, una vez terminado el bucle, que muestre en un Label las veces que se ha repetido.
Por ejemplo, si hacemos un bucle del uno al diez de uno en uno, se repetirá diez veces; pero si lo hacemos de dos
en dos, se repetirá cinco veces...
Como pista, decirte que no tendrás que hacer ninguna comparación para obtener el resultado, la solución es tan
SIMPLE que seguramente la descartarás "porque no puede ser tan fácil"
Por supuesto, me gustaría que los bucles los hicieras tanto con FOR/NEXT y DO/LOOP. Ya puestos, podrías
hacerlo con el WHILE/WEND e incluso con el GOTO...
Solución de los ejercicios:
He preferido poner la solución a los ejercicios en una página separada, así creo que será mejor.
Solución al primero, preguntar el nombre, preguntar la edad y mostrar el nombre "edad" veces:
Tenemos dos TextBoxes: Text1 y Text2, un botón: Command1, el código sería:
Private Sub Command1_Click()
Dim i As Integer
Dim j As Integer
Dim Nombre As String
j = Val(Text2)
Nombre = Text1
For i = 1 To j
Print Nombre
Next
End Sub
El segundo es un poco más complicado, pero no tanto, espero.
Sólo hay que asignar el nombre al Label, suponiendo que fuese Label3, sería algo como esto:
Private Sub Command1_Click()
Dim i As Integer
Dim j As Integer
Dim Nombre As String
j = Val(Text2)
Nombre = Text1
For i = 1 To j
Label3 = Label3 & Nombre
Next
Visual Basic 2 Año 2016 Omar Palermo 22
End Sub
Por último, para que cada nombre se muestre en una línea diferente, hay que añadirle a continuación un retorno
de carro y cambio de línea, en VB4 hay una cosntante definida para ello: vbCrLf, en el VB3 habría que declararla
de esta forma:
Dim vbCrLf As String
vbCrLf = Chr$(13) & Chr$(10)
Este sería el código:
Private Sub Command1_Click()
Dim i As Integer
Dim j As Integer
Dim Nombre As String
j = Val(Text2)
Nombre = Text1
For i = 1 To j
Label3 = Label3 & Nombre & vbCrLf
Next
End Sub
Vamos a ver las soluciones de los ejercicios de la sexta entrega, para ver que nivel llevas, SOLUCIONES A LOS
EJERCICIOS DE LA SEXTA ENTREGA:
1.) Private Sub Command1_Click()
Dim A As Integer
Dim B As Integer
Dim i As Integer
A = Val(Text1)
B = Val(Text2)
For i = A To B
Print i
Next
Show
End Sub
'El bucle también se puede hacer de esta forma:
i=A
Do While i<=B
Print i
i=i+1
Loop
'Y de esta también
i=A
While i<=B
Print i
i=i+1
Wend
2.) Private Sub Command1_Click()
Dim A As Integer
Dim B As Integer
Dim C As Integer
Dim i As Integer
A = Val(Text1)
B = Val(Text2)
C = Val(Text3)
For i = A To B Step C
Print i
Next
Show
End Sub
'Otra forma de solucionarlo
i=A
Do While i<=B
Print i
i=i+C
Wend
3.) 'Añadir estas líneas:
Visual Basic 2 Año 2016 Omar Palermo 23
Dim D As Integer
'...
'...For
D = D +1
'...
Next
Label1 = "Número de repeticiones: " & D

Ya disponemos de instrucciones suficientes para empezar a "profundizar" en las cosas más difíciles... o casi.
Ya sabes que hay que usar Option Explicit en todos los módulos de código... Esto no es obligatorio, pero si no
quieres que perdamos la amistad, usalo. Gracias.
También cómo usar las variables y los diferentes tipos, puedes hacer bucles, tomar decisiones, relamente es más
correcto decir: hacer que VB tome decisiones, ya sabes cómo pedir datos al usuario y también cómo mostrarlos.
Sabes crear tus propias instrucciones... ¡Jo! ¡Cuanto sabes! Me tienes "anonadado"
Pero aún no sabes una cosa: cómo crear Funciones
¿Qué son las funciones?
Para simplificar, te diré que una función es un procedimiento (como el Sub), que puede devolver un valor.
Normalmente se usa para que lo devuelva, aunque veremos que no siempre es necesario; de todas formas,
cuando necesites un procedimiento que no necesite devolver un valor, usa el Sub.
¿Cómo declarar/codificar una función?
Ámbito Function Nombre ([parámetros]) As Tipo
Dónde Ámbito puede ser Public o Private, dependiendo de la "cobertura" o visibilidad. Ya sabes, Private sólo es
visible en el propio módulo en el que se encuentra definido y Public es en todo el proyecto, incluso fuera de él...
Los parámetros son los valores que esta función puede necesitar para "cumplir" su misión. Éstos son opcionales,
es decir puede tenerlos o no, incluso si tiene, puede ser uno o varios, para declarar varios parámetros hay que
separarlos por comas... Los corchetes, que se suelen usar en los manuales, la ayuda, etc, sirven para indicar que
son opcionales, ¡pero no se te ocurra ponerlos en ninguna función!, ya que no forman parte del lenguaje
Basic...
El tipo es para saber que tipo de dato devolverá la función.
El valor devuelto por una función lo podemos usar para asignarlo a una variable: a = MiFunción()
o incluirlo en una expresión: If MiFunción() + 15 > LoQueSea Then
La ventaja real frente a los Subs es la posibilidad de devolver un valor, imaginate que quieres crearte tu propio
procedimiento para averiguar si un determinado archivo existe... si lo hace como función podrías devolver un valor
cero para indicar que no existe el archivo y un valor distinto de cero indicaría que el archivo en cuestión existe. Por
tanto, podríamos usarlo de esta forma:
If Existe(NombreArchivo) Then ...
Ya que estamos puestos, veamos cómo hacer esta función de forma simple y así te explico uan cosa muy
importante de toda función: ¡poder devolver el valor!
Public Function Existe(sArchivo As String) As Integer
Existe = Len(Dir$(sArchivo))
End Function
Para devolver un valor, éste se asigna a una variable que tiene el mísmo nombre que la función.
Ya vimos que LEN devuelve el número de caracteres de la cadena que ponemos entre los paréntesis; si, LEN
también es una función, pero incluida en el propio Visual Basic.
Dir$ es otra función del VB que devuelve el nombre de un archivo, (sólo el nombre), o una cadena vacía, en caso
de que no haya ninguno en la dirección pasada por el parámetro que se ha usado. Para saber más de esta
función, así como de otras, puedes buscar en la ayuda...
Hemos visto que en las expresiones usamos unos operadores para hacer las comparaciones, aquí tienes los seis
posibles:
= igual, > mayor que, < menor que, >= mayor o igual, <= menor o igual y <> distinto.
Recuerda que el signo igual funciona de forma diferente, según se use en un expresión o en una asignación.
Pero además de estos signos, podemos usar en nuestras expresiones unos operadores lógicos, estos son: AND,
OR y NOT
Podríamos desear hacer una comparación y comprobar si varias cosas se cumplen, por ejemplo:
If A>10 And Len(Nombre)<>0 Then ...
Para que esta expresión se cumpla, deben ser ciertas las dos condiciones, es decir que A sea mayor que 10 "y"
que la longitud de Nombre sea distinta de cero. Podemos usar tantas condiciones como queramos, sin pasarnos
demasiado para que la cosa funciones mejor. Aquí las dos condiciones deben cumplirse, pero en este otro jemplo:
If A>10 Or Len(Nombre)<>0 Then ...
cumpliéndose cualquiera de las dos, se acepataría como válido.
Cuando el If se procesa, se toma todo lo que hay entre IF y THEN y se considera como una sóla expresión.
Si quieres puedes asignar a una variable el resultado de una expresión, el valor devuelto siempres será 0 (cero) en
caso de que no se cumpla todo lo expuesto y -1 cuando sea cierta.
Visual Basic 2 Año 2016 Omar Palermo 24
Para manejar estos valores de Cierto (-1) y Falso (0), Visual Basic tiene un tipo especial llamado Boolean, los
valores que puede aceptar una variable de este tipo son: True (verdadero) y False (falso).
Veamos un ejemplo:
Dim b As Boolean, i As Integer
Dim a As Integer, Nombre As String
Show
a = 15
Nombre = "Guille"
b=(a>10 And Len(Nombre)<>0)
i=(a>10 Or Len(Nombre)<>0)
Print "Valor de B "; b
Print "Valor de i "; i
¿Te has fijado en el detalle? B vale True (verdadero), sin embargo i vale -1. Pero para el caso los dos valores
significan lo mismo: si estas expresiones se hubiesen usado en una comparación, las dos hubiesen devuelto un
valor verdadero.
El tercer operador lógico (Not) sirve para negar algo, es decir invertir el valor contenido en una variable, o casi...
If Not A>10 Then ...
Parece lógico el resultado, ¿verdad?, si no se cumple que A sea mayor que diez, será cierto; comprobemoslo:
Dim A As Integer
'¿recuedas que tienes que poner Show?
A=5
If Not A>10 Then
Print A;"no es mayor que 10"
End If
Se toma A > 10 y se procesa, como A no es mayor que 10, se devuelve un valor falso (0) y después se hace Not 0
que da como resultado -1 (verdadero), por tanto se cumple la condición.
Ya vimos que el valor devuelto por una variable se puede usar en una comparación, si es cero se toma como falso
y si es distinto de cero, como verdadero.
Prueba ahora esto:
A=0
If Not A Then
Print A;"es cero"
Else
Print A;"es distinto de cero"
End If
También funciona, ya que Not 0 es -1, por tanto el If lo da por cierto, si cambiamos el valor incial de A por un valor
distinto de cero:
A=5
If Not A Then
Print A;"es cero"
Else
Print A;"es distinto de cero"
End If
¿Que ha pasado aquí? Simple, que no es lo que esperabamos... Cuando hicimos Not 0 era evidente, ya que se
convierte en -1, pero Not 5 no se convierte en cero, sino en: -6 y ya sabes que el IF considera como verdadero
todo lo que no sea cero.
No quiero entrar en detalles de porqué ocurre esto, sólo decirte que la responsable de todo es la notación binaria...
los ceros y unos que dicen que es el lenguaje nativo de los cacharros estos... talvez más adelante tratemos un
poco de la notación binaria, pero no por ahora... recuerdo que en mis tiempos del GwBasic la usaba bastante,
incluso tenía rutinas para "representar" en ceros y unos un número...

Private Function Dec2Bin(sNumDec As String) As String


'Recibe una cadena que será un número decimal
'Devuelve ese número representado por ceros y unos '
Dim i As Integer
Dim lngNum As Long 'Long, por si las moscas
Dim sTmp As String 'Cadena temporal

lngNum = Val(sNumDec)
sTmp = ""
For i = MaxBits - 1 To 0 Step -1
If lngNum And 2 ^ i Then
Visual Basic 2 Año 2016 Omar Palermo 25
sTmp = sTmp & "1"
Else
sTmp = sTmp & "0"
End If
Next
Dec2Bin = sTmp
End Function
Ahora puedes comprobar porqué NOT 5 da como resultado -6, usa esta rutina para probarlo, si escribes 5, te
mostrará:
00000101 y si escribes -6 lo que muestra es: 11111010, fijate que ha cambiado todos los ceros por unos y
viceversa.
Eso es lo que hace el NOT, invertir los valores binarios y como un valor binario sólo puede ser 0 ó 1, no se
complica demasiado la vida. Prueba a escribir el valor 0 y el valor -1 y conviertelo a notación binaria, fijate lo que el
Visual Basic normalmente ve.
Prueba con el tema de la notación binaria, así sabrás realmente cómo funciona todo esto de las comparaciones
(por dentro).
Lo que nunca falla es completar la expresión, por ejemplo si haces esto:
If Not A<>0 Then
Print A;"es CERO"
Else
Print A;"NO es CERO"
End If
Esto siempre funcionará de la forma esperada.
Pero sería más fácil, o al menos más inteligible, hacerlo así:
If A=0 Then
Print A;"es CERO"
Else
Print A;"NO es CERO"
End If
Es que algunas veces se puede uno complicar la vida más de lo necesario...
Cuando quieras comprobar un valor devuelto por cualquier expresión, puedes hacerlo asignándolo a una variable
o bien mostrando el valor: Print Not A
Cuando se usa AND pra evaluar varias partes de una expresión hay que tener presente que siempre se procesan
todas las condiciones y finalmente se decide si es cierto o no el valor devuelto. Esto que parece lógico, algunas
veces puede llevar a confusión e incluso producir efectos no deseados en el programa.
Prueba con esta nueva versión de la función Existe. En un form debes poner una etiqueta Label1.
Private Function Existe(Archivo As String) As Integer
Existe = Len(Dir$(Archivo))
If Existe Then
Label1 = Archivo & " Si existe"
Else
Label1 = Archivo & " No existe"
End If
'Esto es más corto, pero talvez menos evidente:
'Label1 = Archivo & IIf(Existe, " Si", " No") & " existe"
DoEvents
End Function
Private Sub Form_Load()
Dim A As Integer, Nombre As String
Show
Label1 = ""
Nombre = "C:\Autoexec.BIN"
A=5
If A > 10 And Existe(Nombre) Then
Print A; "mayor de 10 y " & Nombre & " existe"
Else
Print A; "no es mayor de 10 o " & Nombre & " no existe"
End If
End Sub
En el ejemplo comprobarás que a pesar de que la segunda parte de la comparación no se cumpla, a no ser que
tengas en tu disco C un archivo que se llame así, el caption del Label se ha cambiado. Es decir que se ha
procesado la segunda parte de la expresión a pesar de que la primera A>10 es FALSA. Imaginate que en lugar de
Visual Basic 2 Año 2016 Omar Palermo 26
ser una función rápida, hubiese sido otra cosa que tardara un poquito más de la cuenta...
Para casos como estos, (la verdad es que no son demasiado habituales), deberías hacerlo así:
Private Sub Form_Load()
Dim A As Integer, Nombre As String
Show
Label1 = ""
Nombre = "C:\Autoexec.BIN"
A=5
If A > 10 Then
If Existe(Nombre) Then
Print A; "mayor de 10 y " & Nombre & " existe"
Else
Print A; "es mayor de 10 pero " & Nombre & " no existe"
End If
Else
Print A; "no es mayor de 10 o " & Nombre & " no existe"
End If
End Sub
Talvez sea más largo y haya que usar más código, pero en ocasiones es más "resultón".
Usando este nuevo "estilo", sólo se comprobará si existe el archivo cuando A sea mayor que diez. Lo que debes
sacar en claro de todo esto es que después de un THEN puedes "anidar" más expresiones IF...THEN...ELSE.
Incluso se puede usar en una sóla línea, sólo que el resultado "visual" del código no es tan "presentable"...
If A > 10 And Existe(Nombre) Then Print A; "mayor de 10 y " & Nombre & " existe"
Else Print A; "no es mayor de 10 o " & Nombre & " no existe"
Aunque podríamos usar el caracter _ que se puede usar en VB para separar líneas largas, pero es como si
estuviese toda en la misma línea, así que la línea anterior, se quedaría así:
If A > 10 And Existe(Nombre) Then _
Print A; "mayor de 10 y " & Nombre & " existe" _
Else _
Print A; "no es mayor de 10 o " & Nombre & " no existe"
Fijate que a pesar de aparentar que es un BLOQUE IF, no tiene el END IF del final, esto es porque yo lo he
"estructurado" de esa forma, no porque sea lo mismo. El uso de _ es sólo estético y para VB todo se trata de una
misma línea, por tanto tendrá un límite de caracteres posibles a usar, el límite que VB le ponga, que creo que es
1024... pero no me hagas demasiado caso...
Antes he mencionado la palabra "anidación", ésta se usa para indicar que una serie de instrucciones están dentro
de otras. En este caso hemos anidado dos IF... THEN, pero lo más habitual es hacerlo con los bucles (FOR, DO,
etc), veamoslo:
Dim i%, j%, c%
For i = 1 To 10
For j = 1 To 10
c=c+1
Next
Next
Print c
Lo que debes saber, o al menos tener en cuenta, es que cuando anidamos varios bucles, lo externos empiezan
antes (elemental querido Watson), pero los internos finalizan primero (...) y hasta que no lo hagan, no podrán
continuar los de fuera.
En el ejemplo, por cada repetición del bucle i, se completa un bucle j. Por eso el valor de c es 100 (10*10)
Esto, en ocasiones, puede ralentizar el programa, y dar la impresión de que el programa se ha quedado "colgado",
prueba a poner otro bucle dentro del j y cambia los valores máximo de los dos bucles internos a 1000, te
recomiendo que la variable c sea LONG y que te sientes... No hace falta que hagas la prueba, es una chorrada...
Lo que interesa es que dentro de un proceso cualquiera y por supuesto también en los bucles, podríamos
necesitar que el Visual Basic nos mostrara alguna indicación de que está "ocupado", por ejemplo cambiando la
forma del cursor del ratón, como hacen otros programas, incluso el propio VB cuando está "atareado". Para ello
tendremos que cambiar la propiedad MousePointer para que muestre el reloj de arena:
MousePointer = vbHourGlass 'vbHourglass es igual a 11, por si tienes usas el VB3
'... lo que sea
MousePointer = vbDefault '0 si usas VB3
Pero algunas veces el cursor no se cambia... para asegurarnos que cambie, usa el DoEvents después de asignar
el valor para el reloj de arena. De esta forma permitimos que Windows procese sus mensajes (¿recuerdas?) y así
tiene ocasión de cambiar el puntero del ratón.
Visual Basic 2 Año 2016 Omar Palermo 27
Ya has visto todo el tema de las variables y sabes para que sirven, (al menos así debería ser, ya que nadie me ha
preguntado sobre el tema), pero hay veces que necesitamos más.
Imaginate que quieres saber cuantas veces por hora te rascas la cabeza intentando comprender lo que hay en mis
páginas...
Podríamos tener unas cuantas variables, una por cada hora del día, con nombres como: Hora1, Hora2, etc. y
cuando te arrasques a las 22 horas, harías esto:
Hora22 = Hora22 + 1
Con lo que aumentas en uno el contenido de la variable Hora22... y tu dirás... ¿que problema hay? Ninguno, pero
es que se me ha ocurrido contarte esto como podría haberte contado otra cosa. Pero imaginate que quieres sumar
las veces que te has rascado la cabeza en todo el día, podrías hacer:
VecesDia = Hora1 + Hora2 + ...+ Hora24
Tampoco hay problema, sumamos todas las variables (24 en este caso) y guardamos el total en la variable
VecesDia.
Pero la cosa se va complicando, ¿verdad? ¿No lo crees? Pues toma un poco más de complicación:
¿Cómo harías para saber la hora en que más veces te has rascado la cabeza?
No te doy la solución... Es demasiado largo y seguramente hasta complicado como para ponerlo como ejemplo
para que salgas de tu incredulidad... Ahora bien, si quieres hacerlo, hazlo, pero después no me preguntes si está
bien o no... sería una pérdida de tiempo, ya que, como vas a ver dentro de muy pocas líneas, hay una forma de
hacerlo bastante más simple.
Todo esto, es para explicarte la forma con que el Basic, y otros lenguajes, nos facilita la vida en tareas de este
tipo... realmente no creo que haya nadie que tenga una utilidad de este tipo, ya que no es útil esto de saber
cuantas veces nos rascamos la cabeza por no comprender algo... ni aún cuando ese algo sea lo que yo escribo...
Un poco de historia (la mía)
La primera vez que me topé con los ARRAYS (de eso va esta entrega), fué con un programa de dados Lo copié
de un libro inglés que tenía 50 juegos, sabía que funcionaba, pero no sabía cómo...
Durante un montón de tiempo, a lo mejor fué una o dos semanas, es que antes los días duraban más que ahora,
usé métodos parecidos, sin saber porqué funcionaba... sólo sabía que funcionaba y simplemente lo aceptaba.
Después cayó en mis manos un libro, esta vez en español, y me puse a leer la parte que explicaba esto de las
"matrices", antes no eran arrays... la verdad es que lo leí como 20 veces o más... y parecía que nunca iba a lograr
asimilarlo, es que soy bastante duro de mollera y antes, que era más joven, calculo que unas 14 veces más joven
que ahora, tenía la cabeza más dura. En aquella ocasión si que me hubiese venido bien el programilla este de
rascarme la cabeza...
Esto es lo que decía, el susodicho libro:
"El lenguaje BASIC permite definir otro tipo de variables numéricas:
A(1), A(2)...A(N)
se llaman variables con índice y están formadas por un nombre de variable, [...], seguido de un
número natural entre paréntesis. [...] el conjunto ordenado de estas variables se llama lista. [...]"
Ahora que lo he vuelto a leer, casi lo entiendo; pero aquello sonaba a chino. Y si tu lo entiendes, me alegro.
Como te he comentado el parrafo ese está sacado de un libro que fue de los pocos que tuve, al menos en
castellano, de los ingleses sólo me interesaban los listados, que era lo único que prácticamente traían. La cosa
que a pesar de eso, hasta aprendí un poco, a duras penas, sobre todo porque en aquellos tiempos no tenía a
quién preguntarle, ni quién me explicara algunas de las muchas dudas que tenía... y si no tenía más dudas era
porque tampoco había profundizado demasiado. Pero lo que he sacado en claro es que para aprender a
programar hay que practicar, practicar y seguir practicando... es como todo, cuanto más practicas... o terminas por
aburrirte o aprendes...
Vamos al tema, pero sin teorías, las teorías se las dejo a los eruditos... ellos saben cómo explicar "bien" las
cosas... Y que conste que no tengo nada en contra de las teorías, lo que ocurre, al menos a mi, es que prefiero
entender las cosas de forma práctica,
En el ejemplo ese de las horas, nos vendría muy bien que pudiesemos usar otra variable para la hora de nuestro
"picor" y hacer algo como:
HoradelPicor = HoradelPicor + 1, dónde "delPicor" sería la hora en que nos rascamos la cabeza, así si "delPicor"
es 22, incrementar Hora22. Si, lo reconozco, una vez intenté hacerlo, creía que se podía hacer así:
delPicor = 22
HoradelPicor = HoradelPicor + 1
Pero el basic no hacía lo que yo quería, ni siquiera me daba error, si en aquellos tiempos hubiese existido el
Option Explicit, me habría percatado de muchas cosas antes de tiempo...
Por suerte para todos, existen los ARRAYS (o variables con índice) y realmente la forma de hacerlo es casi como
yo creía, lo único que cambiaba era la forma... total, por un par de paréntesis...
Hora(delPicor) = Hora(delPicor) +1
Con esto le decimos al Basic: toma lo que hay guardado en la variable que está en la posición delPicor del array
Hora...
Vale, captado. Me estoy lanzando y aún no te he presentado a los arrays.
Visual Basic 2 Año 2016 Omar Palermo 28
Un Array es una serie de variables que tienen el mismo nombre y para acceder a cualquiera de esas variables,
usamos un número (índice) para indicar cual de esas variables es la que nos interesa... ¿Te has enterado de que
estoy hablando de variables o tengo que decirlo más veces? Vale, adimito que tampoco he sido demasiado claro,
es que realmente no es tan fácil de asimilar, pero en cuanto lo veas con algunos ejemplos, seguro que lo
"asimilas".
Hora(delPicor), dice: "¿Cuanto vale lo que está dentro del paréntesis?" (en caso de que sea una expresión en
lugar de un número o una variable, la evaluaría primero y usaría el resultado), una vez que sabe cuanto vale lo
que está dentro del paréntesis... "ahora tomemos, del array, el contenido de la variable que está en esa posición"
(para el basic un array no es más que una serie de variables que tienen el mismo nombre y lo único que varía es
"la dirección" en la que está guardado el valor.
Osea que maneja los arrays de la misma forma que a las variables simples, pero ampliando nuestros horizontes
"variablisticos".
Ahora fijate cómo podemos sumar las veces que nos hemos rascado la cabeza a lo largo del día:
For i= 1 to 24
vecesDia = vecesDia + Hora(i)
Next
Cada vez que i cambia de valor, (en este caso tomando valores desde 1 hasta 24), el Basic usa una variable
diferente del array Hora, cuando i vale 1 está usando Hora(1) y cuando i vale 22, usa Hora(22).
Lo de saber a que hora te has rascado más veces la cabeza te lo dejo como ejercicio, sólo te diré que si haces
esto:
masVeces = Horas(HoraMasVeces)
sabrás cuantas veces te has rascado a la hora HoraMasVeces...
Los elementos de un array se comportan como variables normales, pudiendo usarlas en expresiones y en
cualquier otro sitio en el que podamos usar una variable, realmente uno de los pocos sitios donde no puede usarse
es como índice de un bucle FOR, pero por lo demás, en cualquier parte.
Imaginate que en la posición 22 del array Hora, es decir en Hora(22) tenemos guardado un valor 15, al hacer esto:
Print Hora(22) * 10
mostraría 150, porque el VB ha sustituido Hora(22) por su valor y lo ha multiplicado por 10, es como si
internamente hubiese hecho: Print 15*10.
Y tu dirás: esto mismo es lo que hace con las demás variables...
Efectivamente, ya que es una variable, especial, pero una variable al fin y al cabo.
Sigamos imaginando... suponte que quieres guardar en una variable a que horas te pones a leer, cada día, las
páginas del Guille y que sabes que serán tres veces diarias, lo hay masoquistas... Podrías hacer algo como esto:
Vez(1) = 20: Vez(2) = 22: Vez(3) = 23 '...(de nueve a diez vas a cenar)
con lo cual tendrías un array con tres índices. El valor de cada una de las variables del array "Vez", sería la hora
en que "guilleas" ... y si quieres incrementar los "rascones" de la hora que está en la posición H, (que puede ser 1,
2 ó 3):
Ahora = Vez(H) 'Si H vale 1, Ahora sería igual a 20
Hora(Ahora) = Hora(Ahora) + 1
Pero esto podrías hacerlo ahorrándotela variable Ahora, sería así:
Hora(Vez(H)) = Hora(Vez(H)) + 1
¿Complicado? Pues si... que quieres que te diga... pero dejemoslo estar...
Veamos cómo podemos usar en nuestros programas este tipo especial de variables. Antes yo las llamaba:
"variables dimensionadas", entre otras cosas porque era únicamente cuando necesitaba usar DIM, al menos si iba
a usar más de 10 "posiciones", aunque también puede ser que lo leyera en algún sitio, no importa...
Para decirle al Basic que vas a usar tres variables en el array Vez, hay que hacerlo de esta forma:
Dim Vez(3)
Ahora lo que necesitamos es un array para guardar los picores de las 24 horas del día:
Dim Hora(24)
Bueno, ya sabes casi todo lo que tienes que saber de los arrays... ahora comprate un buen libro "teórico" y
estudiatelo... o leete lo que dice el manual del Visual Basic... que también puede valer.
¿Aún sigues por ahí...?
Bueno, ya que insistes, te explicaré algunas cosillas más...
Los arrays son variables, algo especiales, pero variables al fin y al cabo.
Por tanto, podemos tener arrays numéricas, de carateres y en definitiva de cualquier tipo que el Basic permita, lo
único que tenemos que hacer es indicárselo al reservar memoria:
Dim Vez(3) As Integer
Dim Amigos(1000) As String
Dim Salario(1000) As Currency
Cuando declaramos un array, el Basic reserva memoria para cada una de las variables que vamos a usar, o casi,
ya que en realidad reserva una posición más, no por nada en especial, sino porque empieza a contar desde cero;
por ejmplo en el array Vez, el índice más bajo que podríamos usar es el 0 y el más alto el 3.
Esto ha sido así desde siempre... aunque en un intento de "cambiar" las cosas, un listillo dijo: "El Basic debería
Visual Basic 2 Año 2016 Omar Palermo 29
empezar a contar desde uno" y se sacaron una nueva instrucción de la manga, desde mi punto de vista lo podrían
haber hecho mejor, pero como mis "preferencias" no las tuvieron en cuenta... (tampoco tuve la oportunidad, la
verdad sea dicha...), el caso es que dijeron:
OPTION BASE 1 para que el índice menor de un array sea UNO y
OPTION BASE 0 para empezar por CERO, esta será la predeterminada.
Por tanto si usamos este código:
Option Base 1
Dim Vez(3) As Integer
Crea un array con tres variables (del 1 al 3)
Más adelante, otro listillo, (este fué un poco más inteligente), dijo: "Y si el usuario pudiera decidir el valor menor y
el mayor del índice de una array"... "pues que bien", contestó otro...
Y así fué. Imaginate que tu sólo te rascas la cabeza de 10 a 23, puedes dimensionar así el array Hora:
Dim Hora(10 To 23) As Integer
De esta forma sólo "reservas" la memoria que necesitas... Ya ves que todo son facilidades, aunque hay una cosa
"muy" importante que hay que tener en cuenta: Si pretendes acceder a una posición del array que no está
reservada, el Visual Basic te avisa de que hay un error y detiene (termina) el programa. Esto es lo único grave,
pero si tu aplicación tiene información importante pendiente de guardar, o se encontraba en medio de un proceso
largo de cálculo... realmente si que será grave... en otra ocasión veremos cómo detectar los errores y poder
"manejarlos" para que no nos dejen en la "estacada"... Por tanto si declaras un array que reserva tres posiciones,
siempre consecutivas, no podremos acceder a ninguna posición anterior o posterior a las que tenemos declarada.
Pero... ¿y si necesito más espacio? Lo único que tienes que hacer es re-dimensionar el Array:
ReDim Vez(5) As Integer
¿Problemas? Si, que ya has perdido lo que antes había almacenado...
Cuando yo empecé con el Basic (¿otra batallita?, habrá que seguirle la corriente...), no existía el ReDim. Pero si
existía uan forma de conseguir esto mismo. El truco consitía en "borrar" el array creado y volver a dimensionarlo...
también recuerdo que me dió muchos quebraderos de cabeza adaptar mi código (escrito en el intérprete
GwBasic... la GW ¿será Gates, William?) a un compilador... eso de usaar varias veces el DIM no lo digería bien...
pero eso es otra historia, que seguramente no contaré...
Para borrar un array de la memoria, hay que usar ERASE seguido por el nombre del array, por ejemplo: Erase
Hora
Con esto conseguimos lo mismo que con Redim Vez(5) As Integer:
Erase Vez
Redim Vez(5) As Integer
Pero, y si no quisieramos perder los valores anteriores...
Pues copia los datos en otro Array temporal, borras el primero, lo vuelves a dimensionar con el nuevo número de
elementos y a continuación copias los datos del array temporal en el array quie acabas de redimensionar, después
borras el array temporal, ya que no lo necesitarás...
No, no es necesario tantas cosas, pero esto es lo que había que hacer con el VB antes de la versión 3 y con el
QuickBasic antes de la versión 4, ahora sólo harás esto:
Redim Preserve Vez(5)
Además cuando se ReDimensiona un array no hace falta volver a especificar el tipo de dato, ya que tomará el
mismo que se usó inicialmente al declararlo.
La ventaja del ReDim, con o sin Preserve, (podría haber hecho un chiste malo con esto del Preserve, pero me
abstengo...), es que puedes ampliar o reducir el número de variables de un array... supón que después de
dimensionar Vez a cinco, lo piensas mejor y decides que con dos veces es suficiente, pues nada, haces esto:
Redim Preserve Vez(2) y ya está. Lo importante es que sólo reserves la memoria que vas a necesitar.
En la siguiente entrega veremos más cosas de los arrays, así como algunas otras instrucciones, pero no te voy a
adelantar nada, no sea que después no esté lo que tengo pensado y te mosquees.
Ahora vamos al apartado de los ejercicios, que además del que te dije casi al principio, te voy a poner otro más:
1.) Tienes un array con un número cualquiera de elementos, averigua cual de las variables de ese array es la que
tiene el valor mayor.
2.) La que tiene el valor menor y que no sea cero.

Solución a los ejercicios de la Octava entrega


El primero:
'Poner este código en el Form_Load
Dim Hora(24) As Integer
Dim i As Integer, Dim Mayor As Integer
'Llenar el array con números...
'(en esta entrega veremos cómo hacerlo de forma aleatoria)
'...
'Comprobar cual es el mayor
Visual Basic 2 Año 2016 Omar Palermo 30
For i = 1 To 24
If Hora(i) > Mayor Then
Mayor = Hora(i)
End If
Next
Print "El número mayor es:"; Mayor
Un poco de explicación, ya que no creo que sea suficiente con enseñar la solución.
El problema que te has podido encontrar es, seguramente, la forma de asignar valores al array, aunque siempre
queda el recurso de poder "llenarlo manualmente"; pero eso lo veremos en esta misma entrega.
La cuestión es comparar el contenido de cada una de las horas con la variable que guardará el número mayor. Al
principio esta variable, como ya deberías saber, tiene el valor CERO, así que cuando se haga la comparación If
Hora(i)>Mayor Then, si la variable que está en la posición "i" del array "Hora" tiene un valor mayor que cero, se
cumplirá la condición y se pasará a asignar ese valor a la variable que contendrá el número mayor de los 24 que
tenemos en el array.
El bucle continúa y cada vez que se cumpla la condición de que el contenido de Hora(i) es mayor que el que
tenemos en la variable Mayor, se asignará este y así hasta que se termine de dar vueltas...
Si no te has enterado... preparate para la solución del segundo ejercicio.
El segundo:
'Los mismos comentarios iniciales que el primero
Dim Hora(24) As Integer
Dim i As Integer, Dim Menor As Integer
'
For i = 1 To 24
If Hora(i) Then 'Sólo si no vale cero
If Menor = 0 Then 'Si aún no tiene un valor
Menor = Hora(i) 'se lo asignamos
Else
If Hora(i) < Menor Then 'Si el contenido de Hora(i) es menor
Menor = Hora(i) 'lo asignamos como menor
End If
End If
End If
Next
Print "El número menor es "; Menor
Este está más o menos explicado en los comentarios, pero voy a dejartelo un poco más claro:
La cuestión consiste en comprobar primero si el contenido del elemento "i" del array "Hora" tiene un valor distinto
de cero, (si vale cero no lo tendremos en cuenta), lo siguiente que se hace es comprobar si el contenido de
"Menor" vale cero, si es así, quiere decir que aún no le hemos asignado ningún valor, por tanto le asignamos lo
que tenga Hora(i). En posteriores comprobaciones lo que se hace es averiguar si el valor guardado en el elemento
del array es menor que el que tenemos en nuestra variable "Menor" y si es así, quiere decir que tenemos un
número más pequeño, por tanto lo asignamos para que siempre "Menor" tenga el número menor (valga la
redundancia).
Pero y si quisieramos tener en cuenta también el CERO... Pues que tendríamos que hacerlo de otra forma, ya que
esta es sólo para el caso expuesto... te dejo que lo pienses, pero no es demasiado difícil, incluso más simple que
esta solución, lo que ocurre es que entran en juego pequeños detalles que seguramente veremos en esta
entrega...
Ahora vamos a empezar la Novena Entrega.
No voy a empezar, o mejor dicho, no voy a continuar con los arrays, pero no te preocupes que sólo será un
pequeño alto en el camino, lo que veremos primero es algo que nos va a facilitar hacer pruebas con los arrays...
se trata... (redoble de tambores) de:
Números Aleatorios
En algunos casos vamos a necesitar generar números aleatorios, (números sacados al azar, al menos en
teoría...), y si no necesitas usar números aleatorios, vamos a usarlos en algunos de los ejercicios, así que voy a
explicar cómo va esto:
La función que se usa para generar números aleatorios es: RND
Esta función devuelve un número que será mayor o igual a CERO y menor que UNO. Creo que se representa así:
0<RND<1, pero si no es así, da igual o algún "experto" me lo dirá. Lo que interesa saber es que nunca llegará a
valer uno y que puede ser igual a cero.
Si hacemos esto: x = Rnd * 6 El valor X nunca llegará a 6, en la mayoría de los casos se suelen quitar los
decimales usando INT, en este caso, haciendo x = Int(Rnd * 6), x podrá valer 0, 1, 2, 3, 4 ó 5 y si hacemos esto
otro: x = Int(Rnd * 6) + 1. Los valores serán de 1 a 6.
Si queremos valores del 65 al 90 la expresión sería esta: x = Int(Rnd * 26) + 65. Ya que Int(Rnd * 26) producirá un
número que estará entre el 0 y el 25 (ambos inclusives), al sumarle 65... pues eso, estará en el rango deseado.
Visual Basic 2 Año 2016 Omar Palermo 31
Este ejemplo nos sirve para cuando necesitemos obtener un código ASCII de una letra de la A a la Z, (en
mayúsculas), ya que los códigos ASCII de las letras son: A=65, B=66... Z=90 en caso de que sean mayúsculas,
para obtener los valores en minúsculas sólo hay que añadirle 32 y ya los tendremos porque a=97, b=98... z=122.
En estos rangos se excluye la eñe, tanto mayúsculas como minúsculas y las vocales acentuadas... es que los
señores que crearon esta norma (American Standard Code for Interchange Information, o algo parecido y se suele
pronunciar ASKI), eran de los USA y allí no usan esas letras... recuerdo mis tiempos de comics, cuando Alex Niño
se llamaba Alex Nino, al menos en los créditos de los comics USA...
El problema de los números aleatorios no son tan aleatorios, es decir cada vez que se inicia el programa produce
el mísmo número, (al menos en los basics anteriores, he leído que ahora el VB4 no genera la misma secuencia
cada vez que se inicia el programa, eso lo veremos después), vamos a ver un ejemplo para que lo compruebes,
esto lo he comprobado con el VB2 (que es el que tengo en el portátil que me he llevado a casa para escribir las
entregas, hasta que mi jefe me lo pida... que será en pocos días, puede ser), se supone que en los demás
funcionará igual, pero como te digo para el VB4 hay un "truco" que veremos después... cuando lo compruebe, jé.
Escribe esto en el Form_Load del nuevo proyecto que habrás tenido que crear para probar... si no lo has hecho, ya
tardas...
Private Sub Form_Load
Show 'Aquí debe estar esto, sino no se verá nada... ¿recuerdas?
Print Rnd * 25
End Sub
A mí me ha mostrado 17.63869, ejecutalo varias veces y verás que siempre muestra el mismo número... Existe
una forma de solucionar esta falta de "aleatoriedad" y es cambiando la "semilla" que se usa como base para la
imPLANTAción de números aleatorios, para ello se usa Randomize seguido de un número, pero si el número es el
mismo... no conseguimos nada... Prueba poniendo Randomize 5 después del Show y antes del Print, prueba a
ejecutarlo varias veces, a mi me ha mostrado 8.143144 todas las veces que lo he ejecutado. El problema es que al
usar un número "fijo" como semilla para la generación de nuevos números, el número producido siempre es el
mismo, esto está bien para hacer pruebas fijas o cuando queremos "darnosla" de mago con los colegas que saben
menos que nosotros, ya que podemos "predecir" los números que mostrará en una secuencia seguida... con este
"truco" dejaba "alucinado" a los chavales a los que le daba clases... sólo tenía que memorizar una serie de
números y se quedaban "alucinados" cuando les decía el que iba a salir... claro que después tenía que poner "pies
en polvorosa" cuando les explicaba "la trampa".
Bueno, al tema, que no esplan de contar batallitas... El VB nos proporciona una función que devuelve el número
de segundos transcurridos desde la media noche (TIMER) y usando esta función como "semilla" lograremos
producir números que "casi" será aleatorios... al menos serán más difíciles de "pronosticar", cambia el Randomize
5 por Randomize Timer y verás que ya no se produce el mismo número cuando ejecutes varias veces el
programa...salvo que hagas "trampas" cambiando la hora del equipo...
Eb VB4 y superior, se puede hacer esto mismo poniendo Randomize -1, de esta forma la "semilla" es diferente
cada vez que se ejecuta, pero prefiero usar el Timer ya que es más "compatible".
¿Que tal un jueguecito para practicar?
Hay que hacer un programa que genere un número entre uno y cien y hay que intentar adivinarlo...
Si el número que damos es mayor o menor, que el VB nos avise y cuando acertemos que nos lo comunique y
termine el programa...
Para que VB se comunique, te voy a decir cómo hacerlo...
Para preguntarte el número y guardarlo en la variable N, haz esto:
N = Val(InputBox("Escribe un número entre 1 y 100")).
El InputBox muestra una pantalla reguntando y devuelve lo que escribamos o una cadena vacía si pulsamos en
cancelar, el Val convierte esa cadena en número.
Para avisar que el número N, (el que nosotros le decimos al VB), es menor o mayor, cambiar xxxx por lo que
corresponda:
MsgBox "El número " & CStr(N) & " es xxxx"
De esta forma se mostrará un cuadro de diálogo indicando si vamos bien encaminados o no...
Ya mejoraremos o ampliarremos este "ejercicio" para hacer más cosas, incluso que el ordenador averigüe el
número... que sin ningún tipo de "suerte" lo adivinará en 5 ó 6 veces, lo mismo que tu deberás hacer si sigues
algunas normas o trucos... sólo decirte lo de "divide y vencerás" (no sé porqué me ha dado ahora por esa cita...)
En la próxima entrega veremos más cosas sobre los arrays...
Si te atreves podrías hacer los siguientes cambios al "problemilla" planteado anteriormente:
1. Comprobar que el número introducido en el InputBox esté entre 1 y 100, en caso de que no sea así, volver a
preguntar.
2. Si se escribe CERO mostrar el número que el VB había "pensado" y terminar.
3. Cuando lo acertemos que nos indique en cuantos intentos lo hemos conseguido.
4. Un programa que sea al revés, es decir: que nosotros pensemos un número del 1 al 100 y el VB intente
adivinarlo, para ello deberá mostrarnos un número y nosotros indicarle si lo ha acertado.
5. Otro igual, pero indicándole si nuestro número es Menor, Mayor o es correcto... habrá que darle las mismas
Visual Basic 2 Año 2016 Omar Palermo 32
oportunidades... (este es el que tiene el "truco" del divide y vencerás...
En los casos 4 y 5 que muestre también el número de intentos que le ha llevado solucionarlo...
La pista para que el ordenador sepa si es menor o mayor es usar el MsgBox, pero como función:
If MsgBox("Mi número es: " & CStr(x) & Chr$(13) & "¿He acertado?", 4) = 6 Then
MsgBox "Lo he acertado en " & CStr(v) & " veces."
Exit Do
'...
De esta forma mostrará un cuadro de diálogo con dos opciones "SI" y "NO", el número 4 es el encargado de eso.
El valor devuelto será 6 si se pulsa en SI y 7 si se pulsa en NO.
Esto en VB4 se podría hacer así:
If MsgBox("Mi número es: " & CStr(x) & vbCrLf & "¿He acertado?", vbYesNo) = vbYes Then
MsgBox "Lo he acertado en " & CStr(v) & " veces."
Exit Do
'...
Con lo cual, aunque sea en inglés, es más intuitivo. Esto de los MsgBox lo veremos en una entrega "especial"
Soluciones de los ejercicios

Pues aquí están las soluciones, creo que esta forma de darlas será la mejor, ya que no tendrás que esperar a que
esté lista la siguiente entrega para saber si has conseguido resolver los problemillas/ejercicios con éxito...
El juego básico:
Dim n As Integer
Dim x As Integer
Randomize Timer
x = Int(Rnd * 100) + 1
Do
n = Val(InputBox$("Escribe un número del 1 al 100"))
If n = x Then Exit Do
If n < x Then
MsgBox "El número " & CStr(n) & " es menor."
Else
MsgBox "El número " & CStr(n) & " es mayor."
End If
Loop
MsgBox "Lo has acertado."
1. Comprobar que el número introducido en el InputBox esté entre 1 y 100, en caso de que no sea así,
volver a preguntar. Una de las soluciones, sin usar el GOTO:
Dim n As Integer
Dim x As Integer
Randomize Timer
x = Int(Rnd * 100) + 1
Do
Do
n = Val(InputBox$("Escribe un número del 1 al 100"))
Loop While n < 1 Or n > 100
If n = x Then Exit Do
If n < x Then
MsgBox "El número " & CStr(n) & " es menor."
Else
MsgBox "El número " & CStr(n) & " es mayor."
End If
Loop
MsgBox "Lo has acertado."
2. Si se escribe CERO mostrar el número que el VB había "pensado" y terminar.
Dim n As Integer
Dim x As Integer
Randomize Timer
x = Int(Rnd * 100) + 1
Do
Do
n = Val(InputBox$("Escribe un número del 1 al 100"))
If n = 0 Then
MsgBox "Mi número era el " & CStr(x)
Visual Basic 2 Año 2016 Omar Palermo 33
Unload Me
End
End If
Loop While n < 1 Or n > 100
If n = x Then Exit Do
If n < x Then
MsgBox "El número " & CStr(n) & " es menor."
Else
MsgBox "El número " & CStr(n) & " es mayor."
End If
Loop
MsgBox "Lo has acertado."
3. Cuando lo acertemos que nos indique en cuantos intentos lo hemos conseguido.
Dim n As Integer
Dim x As Integer
Dim v As Integer
Randomize Timer
x = Int(Rnd * 100) + 1
Do
Do
n = Val(InputBox$("Escribe un número del 1 al 100"))
If n = 0 Then
MsgBox "Mi número era el " & CStr(x)
'Si está en el Form_Load
'End
'Si está en un procedimiento
Exit Sub
End If
Loop While n < 1 Or n > 100
v=v+1
If n = x Then Exit Do
If n < x Then
MsgBox "El número " & CStr(n) & " es menor."
Else
MsgBox "El número " & CStr(n) & " es mayor."
End If
Loop
MsgBox "Lo has acertado en " & CStr(v) & " veces."
4. Ahora al revés, es decir: que nosotros pensemos un número del 1 al 100 y el VB intente adivinarlo, para
ello deberá mostrarnos un número y nosotros indicarle si lo ha acertado.
Dim x As Integer
Dim v As Integer
Randomize Timer
Do
x = Int(Rnd * 100) + 1
v=v+1
If MsgBox("Mi número es: " & CStr(x) & Chr$(13) & "¿He acertado?", 4) = 6 Then
MsgBox "Lo he acertado en " & CStr(v) & " veces."
Exit Do
End If
Loop
5. Otro igual, pero indicándole si nuestro número es Menor, Mayor o es correcto... habrá que darle las
mismas oportunidades... (este es el que tiene el "truco" del divide y vencerás...
Dim x As Integer
Dim v As Integer
Dim a As Integer
Dim z As Integer

a=1
z = 100
'En este caso no necesitamos números aleatorios
Do
Visual Basic 2 Año 2016 Omar Palermo 34
x = (z - a) / 2 + a
v=v+1
If MsgBox("Mi número es: " & CStr(x) & Chr$(13) & "¿He acertado?", 4) = 6 Then
MsgBox "Lo he acertado en " & CStr(v) & " veces."
Exit Do
Else
If MsgBox("Entonces... ¿ " & CStr(x) & " es mayor?", 4) = 6 Then
'El número del ordenador es mayor
'debe estar entre x-1 y a
z=x-1
Else
'El número del ordenador es menor
'debe estar entre x+1 y z
a=x+1
End If
End If
Loop

Arrays Multidimensionales
En algunas ocasiones hasta los arrays se quedan cortos... al menos los arrays simples o unidimensionales, (una
sola dimensión), si en el ejemplo de la octava entrega, el de los rascamientos, quisieras saber las veces que te
has rascado cada día del mes... tendríamos otra vez el problema, podríamos usar un array para cada día del mes:
Dia1(24), Dia2(24)... pero de nuevo tendríamos complicaciones para algunos cálculos...
Por suerte para nosotros, existe otra forma de usar los arrays.
En nuestro caso nos serviría el que los arrays tuviesen dos dimensiones, al estilo de una tabla con filas para cada
día del mes y columnas para cada una de las horas del día en cuestión, para hacer esto, dimensionaremos un
array de esta forma:
Dim Dias(31, 24) As Integer
Para guardar o recuperar un valor lo haremos de la misma forma que con un array simple, pero especificando dos
valores separados por una coma:
Dias(DiaMes, HoraDia) = 1
y por supuesto, podemos usarlo con bucles FOR:
For Dia = 1 To 31
For Hora = 1 To 24
RascadasMes = RascadasMes + Dias(Dia, Hora)
Next
Next
Después que estos dos bucles terminen, la variable RascadasMes tendrá el total de veces que nos hemos
rascado cada uno de los días de el mes que estamos procesando.
Si queremos almacenar las rascadas de cada día de cada mes de un año, No Problem!
Dim Meses(12, 31, 24) As Integer
De esta forma solucionaríamos en problema, ya que al añadir una tercera dimensión, podemos usar esta para
cada uno de los meses, por ejemplo el total de veces que nos hayamos rascado a las 22 horas del día 30 del mes
9 (septiembre), estaría en: Meses(9, 30, 22)
Reconozco que este ejemplo de las rascada no es útil, pero por lo menos hemos visto cómo usar los arrays.
Recuerda quelos arrays pueden ser de cualquier tipo: Integer, String, Double, etc.

¿Cuantos elementos tiene un array?


En algunas ocasiones podemos necesitar saber el número de elementos contenidos en un array, para estos casos
existen dos funciones, una para saber el índice menor y otra para saber el mayor.
Por ejemplo si tenemos este array: Horas(8 To 22)
El menor sería 8 y el mayor 22, para averiguarlo:
Menor = LBound(Horas)
Mayor = UBound(Horas)
Esta forma es para los arrays unidimensionales, para averiguar estos valores en arrays con más de una
dimensión, tendremos que especificar la dimensión de la que queremos averiguar ese valor menor o mayor, por
ejemplo, si tenemos Dias(1 To 31, 0 To 23)
MenorMes = LBound(Dias,1) 'Devolvería 1
MayorMes = Ubound(Dias, 1) 'Devolvería 31
MenorHora = LBound(Dias, 2) 'Devolvería 0
MayorHora = UBound(Dias, 2) 'Devolvería 23
Redimensionando arrays multidimensionales
Visual Basic 2 Año 2016 Omar Palermo 35
Veamos ahora cómo funciona el Redim y Redim Preserve con los arrays con varias dimensiones: igual
Sí, da lo mismo que el array tenga una o muchas dimensiones. Lo único que debemos saber es que no podemos
cambiar el número de dimensiones, aunque sí el número de elementos de cada una de las dimensiones.
Un ejemplo:
Tenemos inicialmente esta declaración: Dim Meses(1 To 6, 1 To 31, 0 To 23) As Integer
y necesitamos ampliar la primera dimensión de 6 a 12:
Redim Meses(1 To 12, 1 To 31, 0 To 23) o
Redim Preserve Meses(1 To 12, 1 To 31, 0 To 23) si queremos conservar los valores almacenados.
Lo que no podemos hacer es esto: Redim Meses(1 To 31, 0 To 23)
porque pasamos de tener tres dimensiones a pretender tener sólo dos y eso, no está permitido.
Ni al revés tampoco, es decir si tenemos un array con dos dimensiones y queremos que tenga tres.
Si queremos hacer esto último, tendremos que eliminar el primer array y volver a dimensionarlo con las
dimensiones que queramos tener:
Dim Dias(31, 24)
Erase Dias
Dim Dias(12, 31, 24)
El problema es que perdemos los datos... cosa que, en caso de necesidad, podríamos solucionar copiando los
datos a otra variable y volviendo a asignarla al nuevo array dimensionado...
Pero muchos de estos problemas se solucionan con las colecciones y el uso del tipo Variant, así como con los
objetos o clases definidas por nosotros... pero eso será más adelante... todavía hay muchas otras cosas
"esenciales" que aprender y conceptos que siempre debes tener en cuenta... que poco a poco estoy intentando
recalcar para que tu "coco" vaya asimilándolos... espero conseguirlo.

¿Cuantas dimensiones puede tener un array?


Si la mente no me falla, el número de dimensiones es 256. ¿Quién necesita tantas?
Los valores menor y mayor de los índices están comprendidos dentro del rango de un valor Integer del VB, es
decir entre -32768 y 32767 o sea 65536 valores o índices distintos. Esto lo comento como "curiosidad" pero
deberías comprobarlo en los manuales.

Unas cadenas, por favor


Ya he comentado en la octava entrega que los arrays también permiten asignar cadenas de caracteres, realmente
se pueden tener arrays de cualquier tipo de variables. Pero no mezcladas. Si un array se dimensiona del tipo
Integer sólo podremos almacenar valores numéricos enteros. Incluso cuando lo Redimensionemos deberá tener el
mismo tipo con el que en un principio lo habíamos dimensionado. Este inconveniente se solucionará en una
próxima entrega y con las colecciones.
Con lo que sabemos hasta ahora es con lo que vamos a trabajar. Y vamos a practicar un poco con los arrays de
caracteres, para ello vamos a crear un array de cadenas con caracteres aleatorios. No tiene ninguna utilidad, pero
servirá para uno de los ejercicios.
Dimensionaremos un array de 100 elementos, a cada uno de esos elementos asignarle entre 10 y 50 caracteres
comprendidos entre la A y la Z, recuerda que los códigos ASCII de la A es el 65 y la Z el 90.
Ahora os pondré una forma "fácil" de clasificar ese array, la parte de la asignación es la que tú tendrás que hacer.
'
Const MaxCadenas = 100
Dim cadena(1 To MaxCadenas) As String
Dim c As Integer
Dim sTmp As String
Dim i As Integer
Dim j As Integer

Randomize Timer
list1.Clear
'Asignar los valores
'...Escribe aquí tu código...

'Clasificar
For i = 1 To MaxCadenas
For j = 1 To i - 1
'para ordenar de forma descendente:
'If cadena(i) > cadena(j) Then
If cadena(i) < cadena(j) Then
'intercambiar los valores
sTmp = cadena(i)
cadena(i) = cadena(j)
Visual Basic 2 Año 2016 Omar Palermo 36
cadena(j) = sTmp
End If
Next
Next
list1.Clear
For i = 1 To MaxCadenas
list1.AddItem cadena(i)
Next
Creo que el procedimiento es lo suficientemente "simple" como para que lo entiendas... ¿verdad?
Lo que debes "observar" en este método es que cada uno de los elementos del bucle i se compara con todos los
anteriores, de forma que si alguno anterior es "mayor" se intercambien las posiciones...
Supón que en la posición cadena(1) tienes almacenado "HOLA" y en la posición 2 está la palabra "AMIGO"
La condición se cumplirá cuando la variable i valga 2 y j valga 1, quedándo por tanto en el orden correcto.
Lo que debes saber de las cadenas de caracteres es que cuando se hace una comparación el Visual Basic
comprueba los valores ASCII de las letas que componen la palabra, en este caso la letra A está antes que la H, así
que A es menor que H.
También deberás saber que los números están antes que las letras, por tanto si una cadena de caracteres
empieza por una cifra del 0 al 9, se ordenará antes que la "A" y que la "a" estará después que la Z
Si quieres saber los valores ASCII de los caracteres "más o menos" stándard, haz este bucle:
'Códigos ASCII
For i = 32 to 122
Debug.Print i; chr$(i)
Next

Ahora los ansiados ejercicios, (realmente ha sido cortita esta entrega ¿verdad?)
Para los ejercicios, usando este trozo para guardar números aleatorios en un array unidimensional, espero que no
tengas problemas para guardarlos en un array multidimensional.
T = Int(Rnd * 31) + 20 'Número de rascadas, T valdrá de 20 a 50
For i = 1 To T
H = Int(Rnd * 23) + 1 'H valdrá de 1 a 23
Horas(H) = Horas(H) + 1
Next
Los ejercicios usando este ejemplo:
1. Saber que hora tiene el valor mayor y a que hora empezastes a rascarte (es decir la primera hora del array
que contiene un valor)
2. Que hora fue la útima en que te arrascaste (no necista explicación...)
3. Modificar el ejemplo anterior para que el número de veces que te rascas valga (aleatoriamente) de 100 a
1000 y saber también cual de estas horas tiene el valor menor (en caso de que haya varios, sólo tienes
que averiguar uno de ellos)

Soluciones de la Décima Entrega.


Para los ejercicios, usando este trozo para guardar números aleatorios en un array unidimensional, espero que no
tengas problemas para guardarlos en un array multidimensional.
T = Int(Rnd * 31) + 20 'Número de rascadas, T valdrá de 20 a 50
For i = 1 To T
H = Int(Rnd * 23) + 1 'H valdrá de 1 a 23
Horas(H) = Horas(H) + 1
Next
Los ejercicios usando este ejemplo:
1. Saber que hora tiene el valor mayor y a que hora empezastes a rascarte (es decir la primera hora del array
que contiene un valor)
2. Que hora fue la útima en que te arrascaste (no necista explicación...)
3. Modificar el ejemplo anterior para que el número de veces que te rascas valga (aleatoriamente) de 100 a
1000 y saber también cual de estas horas tiene el valor menor (en caso de que haya varios, sólo tienes
que averiguar uno de ellos)
Para que no te compliques mucho la vida, decirte que con un par de líneas, puedes averiguar el mayor o el
menor... no sea que quieras hacer un mogollón de comparaciones.

Las soluciones:
Primero: He hecho un pequeño cambio al ejemplo que se usaría, para que también se incluya la HORA CERO,
esta sería una de las formas de conseguirlo:
Visual Basic 2 Año 2016 Omar Palermo 37

'Ejercicio 1
Dim T%, i%, H%, Horas%(0 To 23)
Randomize
T = Int(Rnd * 31) + 20 'Número de rascadas, T valdrá de 20 a 50
For i = 1 To T
H = Int(Rnd * 24) 'H valdrá de 0 a 23
Horas(H) = Horas(H) + 1
Next

Dim ValorMayor%, HoraValorMayor%, HoraInicioRascada%


'Este valor será para saber que no se ha asignado
Const NoAsignado = -1
ValorMayor = 0
HoraValorMayor = NoAsignado
HoraInicioRascada = NoAsignado
'Las horas van de 0 a 23
For H = 0 To 23
'Si esta hora tiene algún valor, hacer las comprobaciones
If Horas(H) Then
'
'===Para saber la primera hora de la rascada===
'
'Si la hora de inicio no se ha asignado
If HoraInicioRascada = NoAsignado Then
'Asignar esta hora
HoraInicioRascada = H
End If
'
'===Para saber la hora que tiene el valor mayor===
'
'Si el valor actual es mayor...
If Horas(H) > ValorMayor Then
ValorMayor = Horas(H)
HoraValorMayor = H
End If
End If
Next
'
'Para mostrar los valores:
'HoraValorMayor será la hora que tiene el valor mayor
'El valor mayor se puede conseguir así:
' ValorMayor
' Horas(HoraValorMayor) Esta forma dará error si no hay ninguna hora con el valor mayor
'
'HoraInicioRascada será la hora en la que empezastes a rascarte...
'
Lo único que hay que notar es lo siguiente:
Ya que las horas empezarán por CERO, debemos asignar un valor "inexistente" a los valores de las variables que
contendrán las horas de inicio y de mayor valor...
¿Por qué? Porque el Basic asigna automáticamente el valor cero a las variables numéricas y como estas variables
pueden tener un valor de CERO (0) a Veintitrés (23), el valor por defecto puede ser un valor válido... así que
asignándole -1 (menos uno), nos aseguramos que no tendrá un valor que pueda confundirnos... ¿lo captas? si no
es así... ya te enterarás cuando te ocurra... ;-)

El segundo: Usando la parte de asignación de las horas, es lo mismo que para saber la primera hora, pero
usando otra variable.
'Ejercicio 2
Dim UltimaHora%
'Este valor será para saber que no se ha asignado
Const NoAsignado = -1
UltimaHora = NoAsignado
Visual Basic 2 Año 2016 Omar Palermo 38
'Las horas van de 0 a 23
For H = 0 To 23
'Si esta hora tiene algún valor, hacer las comprobaciones
If Horas(H) Then
'
'===Para saber la ultima hora de la rascada===
'
'Si la hora actual es mayor que la asignada
If H > UltimaHora Then
'Asignar esta hora
UltimaHora = H
End If
End If
Next
Esta solución no tiene mayor inconveniente, una vez comprendida la primera solución.
El tercero: Para que el número aleatorio que se asigna esté entre 100 y 1000, cambia la asignación a T de esta
forma:
T = Int(Rnd * 901) + 100
Recuerda que Int(Rnd * 901) produce un valor que va desde 0 a 900 ambos inclusives, así que sumandole 100
tendremos un valor de 100 a 1000.
Para averiguar la hora con el menor valor, usaremos dos variables, una para guardar el valor menor y otra para
que "recuerde" a que hora ocurrió eso...
Aunque habrás comprobado que eso de averiguar el valor menor no es tan "lógico" como parece...
La explicación es que al asignar el valor CERO a una variable cuando se inicializa, este valor puede ser menor
que cualquiera que se haya asignado, este problema no existe si se asignan valores negativos, pero como ese no
es el caso...
Para solucionarlo, se pueden hacer varias cosas, aquí explico las dos que creo que cubren todas las posibilidades:
1. Si HoraValorMenor aún no se ha asignado, la primera hora que se compruebe, será la hora con el menor
valor.
2. Cuando es la "primera" hora que se comprueba, ese debe ser el valor menor hasta el momento.
Esto mismo se puede hacer para el valor mayor, si sabemos que pueden asignarse valores negativos... ya
que si asi fuera, al tener un valor cero, éste sería mayor que cualquier número negativo...
'Ejercicio 3
Dim T%, i%, H%, Horas%(0 To 23)
Randomize

T = Int(Rnd * 901) + 100 'Número de rascadas, T valdrá de 100 a 1000


For i = 1 To T
H = Int(Rnd * 24) 'H valdrá de 0 a 23
Horas(H) = Horas(H) + 1
Next
Dim ValorMenor%, HoraValorMenor%
'Este valor será para saber que no se ha asignado
Const NoAsignado = -1
ValorMenor = 0
HoraValorMenor = NoAsignado
'Las horas van de 0 a 23
For H = 0 To 23
'Si esta hora tiene algún valor, hacer las comprobaciones
If Horas(H) Then
'
'===Para saber la hora que tiene el valor menor===
'
'NOTA:
'Para que FUNCIONE, hay que hacer una de estas dos cosas:
'
'1- Si no se ha asignado el valor menor...
If HoraValorMenor = NoAsignado Then
ValorMenor = Horas(H)
HoraValorMenor = H
End If
'2- Si es el primer valor, será el menor...
Visual Basic 2 Año 2016 Omar Palermo 39
If H = 0 Then
ValorMenor = Horas(H)
HoraValorMenor = H
End If
'
'hasta que se demuestre lo contrario...
If Horas(H) < ValorMenor Then
ValorMenor = Horas(H)
HoraValorMenor = H
End If
End If
Next
Para Terminar: Bien, ya tenemos todas las soluciones, ahora pongamoslo todo junto y veamos para que sirve eso
de NoAsignado...
'Todos los ejercicios
Dim T%, i%, H%, Horas%(0 To 23)
Randomize
T = Int(Rnd * 901) + 100 'Número de rascadas, T valdrá de 100 a 1000
For i = 1 To T
H = Int(Rnd * 24) 'H valdrá de 0 a 23
Horas(H) = Horas(H) + 1
Next
Dim ValorMayor%, HoraValorMayor%, HoraInicioRascada%
Dim UltimaHora%
Dim ValorMenor%, HoraValorMenor%
'Este valor será para saber que no se ha asignado
Const NoAsignado = -1
ValorMayor = 0
HoraValorMayor = NoAsignado
HoraInicioRascada = NoAsignado
UltimaHora = NoAsignado
ValorMenor = 0
HoraValorMenor = NoAsignado
'Las horas van de 0 a 23
For H = 0 To 23
'Si esta hora tiene algún valor, hacer las comprobaciones
If Horas(H) Then
'
'===Para saber la primera hora de la rascada===
'
'Si la hora de inicio no se ha asignado
If HoraInicioRascada = NoAsignado Then
'Asignar esta hora
HoraInicioRascada = H
End If
'
'===Para saber la ultima hora de la rascada===
'
'Si la hora no se ha asignado
If H > UltimaHora Then
'Asignar esta hora
UltimaHora = H
End If
'
'===Para saber la hora que tiene el valor mayor===
'
'Si el valor actual es mayor...
If Horas(H) > ValorMayor Then
ValorMayor = Horas(H)
HoraValorMayor = H
End If
'===Para saber la hora que tiene el valor menor===
Visual Basic 2 Año 2016 Omar Palermo 40
'NOTA:
'Para que FUNCIONE, hay que hacer una de estas dos cosas:
'1- Si no se ha asignado el valor menor...
If HoraValorMenor = NoAsignado Then
ValorMenor = Horas(H)
HoraValorMenor = H
End If
'2- Si es el primer valor, será el menor...
If H = 0 Then
ValorMenor = Horas(H)
HoraValorMenor = H
End If
'hasta que se demuestre lo contrario...
If Horas(H) < ValorMenor Then
ValorMenor = Horas(H)
HoraValorMenor = H
End If
End If
Next
'Mostrar los datos:
Dim sTmp$
sTmp = "Estos son los valores:" & vbCrLf
If HoraInicioRascada <> NoAsignado Then
sTmp = sTmp & "Hora de inicio de rascada: " & CStr(HoraInicioRascada) & vbCrLf
End If
If UltimaHora <> NoAsignado Then
sTmp = sTmp & "Última hora de rascada: " & CStr(UltimaHora) & vbCrLf
End If
If HoraValorMenor <> NoAsignado Then
sTmp = sTmp & "La hora en la que empezastes a rascarte fue: " & CStr(HoraValorMenor) & vbCrLf
End If
If HoraValorMayor <> NoAsignado Then
sTmp = sTmp & "La hora en la que terminastes de rascarte fue: " & CStr(HoraValorMayor) &
vbCrLf
End If
MsgBox sTmp

Nuestras propias variables


Ya has visto prácticamente la totalidad de tipos de variables que Visual Basic soporta, también has visto cómo
agrupar variables para crear arrays.
Ahora voy a explicar cómo puedes crear tus propias variables. Realmente no es crear una variable, sino un tipo de
variable especial en el que se pueden agrupar variables de distintos tipos; es lo que en otros lenguajes se llaman
estructuras.
En Basic son tipos definidos por el usuario (TDU o UDT User Defined Type, que dirian los English-speaken esos)
En un UDT podemos incluir variables que estén relacionadas, realmente puedes incluir casi todo lo que se te
ocurra, pero no tendría mucho sentido incluir cosas que no tengan relación, vamos, digo yo.
Imaginate que quieres hacer una utilidad que dibuje puntos en la pantalla y necesitas tener la posición de, digamos
1000 puntos, una solución sería declarar dos arrays, una para la posición de la fila y otra para la columna de cada
punto:
Dim PuntoY(1000) As Integer, PuntoX(1000) As Integer
Cuando necesitemos dibujar el punto N en X,Y, haríamos algo como esto:
PuntoY(N) = y: PuntoX(N) = x
Y si lo que pretendemos es averiguar la posición del punto A, lo sabriamos así:
y = PuntoY(A): x = PuntoX(A)
Simplemente estariamos usando un array para la fila (PuntoY) y otro para la columna (PuntoX), para simplificar
todo esto, podemos crear un tipo en el cual tendríamos almacenado la posición de cada punto, para ello, hay que
hacer una declaración de la siguiente forma:
Type tPunto
X As Integer
Y As Integer
End Type
Visual Basic 2 Año 2016 Omar Palermo 41
cada vez que necesitemos una variable de este Nuevo tipo, tendremos que declararla como cualquier otra
variable:
Dim unPunto As tPunto
Mu bonito, pero ¿cómo asignamos los valores?
De una forma muy especial, para acceder a cada uno de los datos que puede almacenar nuestra variable
tendremos que especificar el nombre de la variable, un punto y a continuación la variable interna que nos
interese...
Veamoslo:
unPunto.X = 100
unPunto.Y = 20
Para saber el valor guardado en la X de nuestra variable lo sabriamos así: columna = unPunto.X
Siempre usando el punto después de la variable interna, esto puedes encontrartelo en algunos libros o manuales,
usando la expresión: "para acceder a un miembro de una estructura de datos definida por el usuario..." pero el
significado, al final, es el mismo...
Bien, ahora si queremos crear un array para guardar los mil puntos esos a los que me refería al principio:
Dim Puntos(1000) As tPunto
Para almacenar la posición del punto N:
Puntos(N).X = x: Puntos(N).Y = y
Y seguro que ahora sabrás como obtener la posición del punto A.
Pero también podemos almacenar el punto actual en una variable normal de este tipo y asignar ese valor a un
elemento del array:
Dim PuntoActual As tPunto
PuntoActual.X = un_valor: PuntoActual.Y = otro_valor
Puntos(N) = PuntoActual

Distintos tipos de variables en un tipo definido


Los tipos definidos, no sólo sirven para "mezclar" variables del mismo tipo, sino que puedes tener variables de
varios tipos, incluso variables de tipos definidos... Sí, un verdadero lío...
Espero que después de leerte esta entrega y con los ejemplos que veremos en las próximas, (cuando le toque el
turno al manejo de ficheros), se te aclararán las dudas.

Otro ejemplo clásico


Este es uno de los más usados para este tipo especial de variables y la verdad es que es también el más usado,
enseguida sabrás porqué.
Situación: Tener los datos de todos los colegas y otros que no lo son tanto.
Datos: Nombre y apellidos, dirección, teléfono fijo, teléfono móvil, dirección e-mail, URL y cualquier
otra cosa que se te ocurra.
El Tipo: Para un caso como este, (simplificando un poco), podríamos usar este tipo definido:
Type tColega
Nombre As String
Apellidos As String
Direccion As String
Poblacion As String
Edad As Integer
VecesQueLeHeMandadoUnMailYNoContesta As Long 'Esto por si soy yo 8-)
End Type
Fijate en el último "campo" del tipo, es una chorrada (aunque más de uno no pensará así), pero es para que veas
que se pueden usar nombres de variables "super-largos", ¡hasta 40 caracteres! Es decir, que si te gustó Mary
Poppins, podrías tener una variable que se llamara: MeGustaSupercalifragilisticoespialidoso.
Ya en serio, no es conveniente el uso de nombres tan largos, no hagas caso de la propaganda esa que te dicen
que es mejor usar nombres descriptivos, ya que ¡¡¡es una lata tener que escribirlos!!!
En caso de que te de el "punto" de escribir nombres largos, puedes hacerlo, incluso puedes usar más de 40 letras,
sólo que las primeras 40 tienen significado... es decir que si al supercali... ese le añades más letras, sólo
reconocerá las 40 primeras.
Por ejemplo:
SupercalifragilisticoespialidosoChitiChitiBangBangVolando
SupercalifragilisticoespialidosoChitiChitiBangBangParado
Para el VB será la variables (un momento que cuente las letras): SupercalifragilisticoespialidosoChitiChi
De todas formas sigo pensando que es algo tedioso eso de escribir nombres tan largos...
Otro caso de las variables, creo que este aún no lo hemos visto, es este:
Cuando vayamos a usar este tipo de variables para guardar los datos en ficheros (dentro de un par de entregas ya
los estarás usando), es conveniente "definir" la longitud máxima de las cadenas de caracteres, por ejemplo:
Visual Basic 2 Año 2016 Omar Palermo 42
Reservar 20 caracteres para el nombre ó 50 para la dirección, en este caso la declaración de las variables se
harían así:
Dim Nombre As String * 20
Dim Direccion As String * 50
Y si están en un tipo definido:
Type tFijos
Nombre As String * 20
Direccion As String * 50
End Type
La ventaja de hacerlo así: al tener una longitud fija, podemos acceder a cualquier registro haciendo unos
pequeños cálculos... aunque de esto se encarga de forma automática el propio Basic y como he dicho antes: lo
veremos más después.

Vamos con algunos ejemplos.


Ya tenemos definido el tipo tColega, si queremos usarlo sólo hay que DIMensionar una variable para que sea de
ese tipo:
Dim unColega As tColega
Y para guardar el nombre de ese colega:
unColega.Nombre = "Pepito"
Con los demás campos se haría igual.
Ahora, se nos presenta la situación de que tenemos, por poner un ejemplo, 50 colegas; así que vamos a reservar
espacio para todos ellos:
Dim misColegas(1 To 50) As tColega
Para almacenar el nombre del colega número uno:
misColegas(1).Nombre = "Maria de las Mercedes"
Para mostrarlos, simplemente hacemos un bucle que recorra este array y asunto concluido:
For i = 1 To 50
Print misColegas(i).Nombre, misColegas(i).Apellidos, ...etc.
Next
Que quieres imprimir de forma aleatoria uno de los 50 nombres, digamos para gastarle una inocentada, pues
haces esto:
Print misColegas(Int(Rnd*50)+1).Nombre
Ya sabes, si no lo sabias, ahora lo sabrás, que el índice de un array, el numerico ese que se pone dentro de los
paréntesis, puede ser cualquier expresión numérica, que de como resultado un valor que esté dentro de los límites
de la cantidad de variables que tiene ese array... Sí, es que si ese valor no está "dentro" de los elementos que
tienes dimensionados, te "regañará" el VB diciendote: Index Out of Range (osea: T'as pasao, colega)

Un alto en el camino.
Toma papel y lapiz, porque esto es una nueva instrucción.
Ya has visto en el ejemplo de imprimir los 50 nombres, que cada vez que accedes a uno de los campos (o
variables internas) del tipo definido, tienes que usar el nombre de la variable el punto y después el campo.
Pues a partir del VB4, este asunto se ha simplificado y no sólo para los tipos definidos, ya verás, en un futuro no
muy lejano, calculo que antes del año 2010, que se puede usar en todas las situaciones en las que "algo" tenga
otros "algos" dentro de él y haya que acceder por medio del punto... Si no lo captas, no te preocupes, ya te
enterarás bien...
La palabra mágica es: WITH
No te voy a hacer una presentación formal de esta instrucción, ya tienes el manual del VB o la ayuda y allí seguro
que estará bien "definida", vamos a ver cómo usarla en el ejemplo este que nos traemos entre manos:
For i = 1 To 50
With misColegas(i)
Print .Nombre, .Apellidos, .Direccion, ...etc.
End With
Next
¡Ves que fácil! Hasta he puesto otro de los campos...
De esta forma no tienes que repetir el nombre de la variable, el Visual ya sabe que te estás refiriendo a
misColegas(i), porque esa es la variable que has usado después de With.
Esto mismo se puede usar con cualquier objeto del VB, los tipos definidos no son objetos, pero se parecen, en
unas cuantas de miles de entregas más, te enterarás del porqué...
Por ejemplo para asignar varias de las propiedades de un TextBox llamado Text1:
With Text1
.SelStart = 0
.SelLength = Len(.Text)
End With
Visual Basic 2 Año 2016 Omar Palermo 43
Este mismo ejemplo sin With, como lo tendrían que hacer con el VB3, sería esto:
Text1.SelStart = 0
Text1.SelLength = Len(Text1.Text)
Como comprobarás, está más claro si se usa el With
Además, se pueden anidar varios Withs... unos dentro de otros, pero siempre el PUNTO hará referencia al último
que se ha puesto, esta situación ni la voy a "ejemplificar" ya que cuando le toque el turno, le tocará...
Vamos a continuar la entrega con un ejemplo.
Crea un nuevo proyecto, añade 6 labels y 6 textboxes, un par de commandbuttons...
¿Cómo? Que no sabes cómo hacerlo... hum!
Cuando inicias el VB, se crea un nuevo proyecto con un form por defecto, así que ese paso lo puedes conseguir
simplemente cargando el Visual Basic.
A la izquierda está la barra con los controles que se pueden usar, pulsa (doble click) en el que tiene la A, esto
situará una etiqueta llamada Label1, en el centro del form... sitúala en la esquina superior izquierda, para ello
pulsala con el botón izquierdo del ratón y arrastrala hasta arriba y a la izquierda... Pulsa otras cinco veces... y ve
colocándolas debajo de la anterior, es decir una debajo de otra...

Ahora debes pulsar el que está al lado de la etiqueta: (textbox) y haz la misma operación, pero los situa junto
a cada una de las etiquetas anteriores. Una vez terminado todo el proceso, deberás tener seis etiquetas y seis
cajas de texto.
Por último pulsa en el botón que está debajo de la caja de texto y los colocas en la parte inderior derecha, pulsa de
nuevo en el mismo objeto y lo pones justo al lado del anterior,
Hay una forma más rápida de hacerlo... y que además te permite, si quieres, crear arrays de controles.
Sería pulsando en el Label una vez, a continuación en el TextBox. Los seleccionas y le das a Edición/Copiar,
también con el botón derecho del ratón.
¿Cómo los seleccionas para poder copiarlo? Pulsa el Label, pulsa la tecla Control, dejando pulsada la tecla
control, pulsa en el TextBox, verás que se quedan los dos "resaltados", ahora suelta la tecla Control, en el menú
de Edición, selecciona Copiar (o Copy si tienes la edición inglesa).
Ya están copiados en la memoria del VB, ahora en el menú Edición selecciona Pegar (Paste en guiri), te mostrará
un mensaje de que si quieres crear un array del Label1, pulsa Si o No, dependiendo de que quieras crear ese
array o no, en el Label haz lo que quieras, pero cuando te pregunte si quieres crearlo del TextBox, dile que no...
Situa los nuevos controles debajo de los anteriores y repite la operación, pero en esta ocasión sólo tienes que
volver a pegar... ya que aún siguen copiados en memoria.
Ahora ocurrirán dos cosas, dependiendo de si le dijistes que SI o NO a la creación de arrays del Label, en caso de
haberle dicho SI, sólo preguntará si quieres crear un array de TextBox1; por otro lado, si le contestaste No, te
preguntará de nuevo si quieres crear el array de Label1 y después te interrogará sobre el TextBox1...
Repitelo hasta que tengas 6 controles de cada en el form y después haces lo de los botones...
Esto lo practicas unas 500.000 veces y ... 8-)
Un detalle: el texto que te mostrará será Label1 y Text1, pero los nombres de los controles serán diferentes, sino
me crees, ve pulsándo cada uno de ellos y busca la propiedad Name de la ventana esa que hay a la derecha, la
que pone Properties - Form1.
Lo que interesa es que tengas los controles mostrados en la figura anterior y que los TextBoxes tengan los
nombres Text1, Text2... hasta Text6, los botones deben llamarse Command1 y Command2.
Ahora abre el panel de código, para hacer esto... cosa que a estas alturas ya deberías saber, es pulsando en el
botón "View Code", ese que está en la ventana del proyecto, sí, esa... la de la esquina superior derecha.
falta
Con esto acabamos de declarar el tipo definido, una constante con el número máximo de colegas que por ahora
queremos tener, un array para almacenar los datos de esos colegas y una variable que irá llevando la cuenta de
los colegas que tenemos actualmente.
Volvamos al form, pulsa en el botón Command1 y en la ventana de propiedades busca la que pone Caption,
selecciona el texto Command1 y escribe esto: "Nuevo Colega", ahora pulsa en el Commad2 y cambia el caption
por "Mostrar".
En cada uno de los Labels, empezando por el de arriba, escribe el nombre de los campos de que se componen
nuestro tipo definido, en el último, puedes poner algo más corto... por ejemplo: Veces.
El siguiente código lo pones en el Form_Load, para que se ejecute cuando inicies el proyecto.
falta
Esto otro lo escribes en el Command1_Click:
falta
Y por último, escribe esto en el Command2_Click:
falta
Con esto, ya puedes escribir los datos correspondientes y después pulsas en Nuevo Colega, los datos escritos se
asignarán a cada uno de los campos del tipo definido, como estamos usando un array, hay que especificar el
número en el que queremos insertar esos datos, la variable Colega se va incrementando y en caso de que
Visual Basic 2 Año 2016 Omar Palermo 44
pulsemos en nuevo colega y ya tengamos el número máximo, nos mostrará un mensaje indicándonos que ya no
hay espacio para más.
El botón de mostrar los datos, lo que hace es que va mostrando cada uno de los colegas que tenemos... te los
tienes que ver todos... así que... prepara el cuerpo para los ejercicios de esta entrega, ahí van:
1. Poder modificar uno de los colegas.
Pista: Añade un nuevo TextBox, un nuevo CommandButton, en el caption del botón escribes: "Modificar" y
el número que introduzcas en el nuevo TextBox será el colega a modificar.
2. Mostrar los colegas a partir de un número determinado,
por ejemplo, si en ese TextBox escribes 5, mostrar desde el 5º hasta el último introducido, puedes usar el
mismo botón Mostrar, de forma que si pones 0 ó 1, te muestre todos.

Con esta entrega, empiezo la serie de manejo de datos almacenados en disco. Así que presta atención a esta y
las siguientes entregas, para que esto del manejo de la información almacenada en disco te sea fácil de usar.
Además de las bases de datos, que también veremos en este curso básico... o en las secuelas que pueda tener...
existe una forma de almacenar los datos que nuestra aplicación pueda manejar. Porque de qué serviría hacer, por
ejemplo, un programa tipo editor de textos si no pudiesemos almacenar lo que se ha escrito...
No, aún no te voy a explicar cómo hacer un editor de textos, antes hay que ver algunas cosillas más, pero todo
llega en esta vida, así que no desesperes...
El Basic maneja tres tipos de ficheros: secuenciales, aleatorios y binarios.
Cada uno tiene una serie de características en la forma de acceder a los datos.
El más básico y también el más empleado, es el secuencial; con este tipo de fichero, los datos se almacenan y
recuperan de forma secuencial, es decir: un dato después de otro...
El inconveniente que tiene esta forma de almacenar los datos, es que para recuperar lo que esté en la décima
posición, tenemos que leer los nueve anteriores...
Si tenemos que guardar, por ejemplo, el contenido de un array de strings, lo normal es que lo hagamos
secuencialmente, es decir primero el contenido de la primera variable del array, después la segunda, tercera, etc.,
hasta llegar a la última.
Para recuperar estos datos, actuaríamos de igual forma, pero asignando al array los datos leidos del disco.
Recuerda que para asignar una posición N, antes tendremos que leer las N-1 posiciones anteriores.
La ventaja de esta forma de almacenar los datos, es que la longitud de las cadenas, por ejemplo pueden ser
variables... esto, ahora te parecerá una cosa normal, pero lo entenderás cuando veamos los otros tipos de
accesos.
También permite que puedas almacenar distintos tipos de datos... no te preocupes, ya sabes que en estas
entregas todo está "demostrado"... más o menos, ya que la teoría está bien para los teóricos, pero para los torpes
como yo... lo mejor es la práctica... no es que quiera llamarte torpe... pero, así me siento menos solo... jé, jé.
El inconveniente es, como ya he repetido, que para acceder a un dato en concreto, se deben "leer" todos los
anteriores.
Esta inconveniencia del acceso secuencial se arregla usando el acceso aleatorio. Con este tipo de acceso, puedes
leer del fichero el dato almacenado en cualquier posición, sin tener que leer primero todos los anteriores... Pero,
no todo es perfecto... también tiene un "pequeño" inconveniente... que los datos guardados en un fichero aleatorio
deben ocupar el mismo espacio, osea que sean de la misma longitud... Si guardas cadenas de caracteres, todas
ocuparán el mismo espacio en el disco, aunque unas tengan más caracteres "válidos" que otras... También se
pueden mezclar números y cadenas de caracteres... pero eso tiene también sus "inconvenientes" o mejor dicho su
"truco" para poder usarlo sin armar un KAOS...
Ya que estamos con los distintos tipos de acceso, te diré así por encima de que va el tipo Binario, éste es un poco
especial y permite leer la información almacenada de la forma que queramos, todo dependerá de la longitud de la
variable que usemos para acceder al fichero... todo estas cosas quedarán explicadas y aclaradas en esta o en
próximas entregas...
Bien, ya sabes que tipos de ficheros puedes manejar con el Visual Basic, ahora vamos a ver como hacerlo, por
supuesto, con las instrucciones correspondientes para poder hacerlo, ya que de eso se trata... o es que
¿esperabas poder acceder a la información de los ficheros sin usar instrucciones del VB?
Cada vez que quieras abrir un fichero, tienes que usar un número de "canal" por el que VB nos suministrará la
información, este canal. El canal se indica por medio de un número de 1 a 255. Gracias a este número, el Basic se
comunica con el sistema operativo para acceder a los datos. El Basic nos facilita la tarea de conseguir ese
número, con idea de que no usemos una línea que esté en uso... La instrucción, en realidad es una función, para
conseguir un número de canal libre, es: Freefile. Esta función devuelve un número entero, el cual se almacenará
en una variable y así podremos usarlo para el manejo de los datos almacenados.
NumFic = Freefile
Una vez que conozcamos un canal por el que poder acceder, tendremos que abrirlo:
Open "Prueba.txt" For Output As NumFic
Con esta línea, abrimos el fichero Prueba.txt de forma secuencial para poder escribir en él.
Una vez que tenemos una "via" de comunicación, podremos escribir información usando una versión un poco
Visual Basic 2 Año 2016 Omar Palermo 45
maquillada de la instrucción Print... El maquillaje es el número de canal con el que podemos acceder al fichero:
Print #NumFic, "Lo que sea"
#NumFic es el número de fichero (o canal) por el que accedemos al fichero abierto y después de ese número,
usamos una coma y a continuación lo que queremos guardar en el fichero.
Cuando hayamos acabado de guardar cosas, tendremos que cerrar el fichero que hemos abierto, para poder
liberar ese canal abierto y así poder usarlo en otra ocasión, esto se consigue con el comando Close:
Close NumFic
Es importante esto de cerrar el fichero abierto, ya que en ese momento es cuando el Basic guarda la información
que aún tiene "temporalmente" almacenada en una memoria intermedia que usa para que el acceso a datos sea,
al menos en teoría, más rápido. A esta memoria intermedia se le llama "buffer". El VB la usa para ir guardando la
información que vamos a grabar físicamente en el disco, antes de grabarla, la guarda ahí y cuando está llena, la
escribe en el disco y la libera, esto se consigue con el close, para asegurarnos que todo lo que tenga que estar
guardao, realmente lo esté. El valor de este búfer para los ficheros secuenciales y aleatorios puede ser de 32767
bytes como máximo, antes con el Basic del DOS el valor por defecto era de 128 bytes y el máximo de 255
caracteres, pero esto hace tiempo que cambió y ahora incluso, (al menos en el acceso de 32 bits), aunque en la
ayuda no lo indique así, puede ser mayor que todo eso... Ya tendremos ocasión de comprobarlo.
Todo esto está muy bien, pero si quieres especificar esa longitud... ¿cómo y/o dónde se especifica?
Ahora sabrás cómo y dónde. Para ello vamos a ver cómo se usa al completo la orden OPEN y sus posibilidades
de uso.
Open RutaAcceso [For Modo] [Access acceso] [tipo de bloqueo] As [#]númerofichero [Len=longitudregistro]
Lo que está entre corchetes son parámetros opcionales.
Fijate en el detalle que FOR Modo está entre corchetes, esto significa que si no se especifica el modo, el Visual
Basic entiende que quieres acceder de forma aleatoria. La explicación de cada uno de estos parámetros los tienes
en la ayuda, así que si no quieres esperar a que los explique todos, vete a la ayuda y le echas un vistazo.
Yo empezaré a explicarte lo que ahora necesitas saber y poco a poco iremos viendo las distintas posibilidades...
Pero si no quieres esperar... ya sabes... echa mano del F1 y accede a la explicación de la ayuda o del manual...
Vamos a ver lo que nos interesa de esta instrucción:
El path completo, o a medias, de dónde queremos que se almacene el fichero o el lugar en el
que está almacenado.
RutaAcceso Por ejemplo: C:\Datos\Un directorio\Prueba.txt
.\Algo\Prueba.txt, siempre que en el directorio actual haya un directorio que se llame "Algo"
o simplemente Prueba.txt (esto le indicará que estará en el directorio actual)

 Output, para ficheros de salida, es decir para guardar los datos.


Si el fichero existe, lo borrará (sobreescribirá) y si no existe, lo creará.
 Input, para leer los datos de un fichero ya existente.
Modo  Append, como el Output, pero añadiendo la información al final del fichero, si este
ya existe.
 Random, para acceso aleatorio.
 Binary, para acceso binario.

Aquí se indica el número de fichero (canal) por el que accederemos a la información.


As NúmeroFichero El signo de número (#) es opcional. Y NúmeroFichero, puede ser una variable o una
constante.
Las otras opciones ya las veremos, ahora nos centraremos en las cosas que son más fáciles, siempre hay tiempo
para complicarse la vida, así que nos la complicaremos más adelante, cuando ya tengamos un poco de idea de
todo este follón...
RutaAcceso, a estas alturas deberías saber de que va todo esto del PATH, pero si no lo sabes, te lo explico por
encima: un path es una ruta de acceso a un fichero... ¿comor? Pues eso, si quieres guardar la información en el
disco, tendrás que saber en que parte del disco la quieres guardar, incluso en que disco quieres almacenarla. Y lo
más importante, cómo vas a llamar el sitio en el que se guardará. Esto es un poco como las variables, si quieres
tener distintas cosas en la memoria del Basic, usas distintos nombres de variables, pues lo mismo con los ficheros,
usando distintos nombres de ficehros puedes tener información diferente almacenada en el disco.
Para empezar, debes saber que tienes que usar un nombre en el que almacenar la información que quieres
"conservar", para después poder acceder a ella en el momento que la necesites.
La ventaja de esto con respecto a los nombres de las variables es que puedes usar distintas partes del disco para
guardar esa información, aunque el nombre "real" del fichero sea el mismo...
A ver, si quieres guardar los rascones esos que te dabas en las entregas anteriores, pudes decirle al Basic que
quieres usar un fichero que se llame: rascones. Pero suponte que quieres tener todos los rascones de todos los
meses del año almacenados en distintos ficheros, uno para cada mes. Podrías hacer algo como esto: darle a cada
fichero un nombre diferene o bien usar la "extensión" del fichero para cada uno de los meses...
Visual Basic 2 Año 2016 Omar Palermo 46
Por ejemplo: rascones.ene para enero, rascones.dic para los de diciembre... etc.
Y si quieres que esos datos se guarden en el disco A, pues sólo tienes que decirle que el fichero se llama:
A:\rascones.ene
Si tienes la intención de guardar cada grupo de ficheros en carpetas (directorios) diferentes, también puedes
indicarselo en la ruta esta de acceso: C:\Datos\A1998\rascones.ene
Por supuesto para poder hacer esto último debes tener un disco C (¿quién no lo tiene?), un directorio A1998 que
está dentro de otro llamado Datos que está a su vez en el directorio raíz del mencionado disco C.
En caso que no se especifique la ruta completa, el Visual Basic usará el directorio actual para acceder al fichero.
Debes saber que el visual creará el fichero indicado, pero si no existen los directorios o no puede tener acceso a
ellos, dará error y no abrirá el fichero. ¿Que error? El número 76: Path not found (No se ha encontrado la ruta de
acceso)
Hay más errores, muchos, pero estos ya te los irás encontrando y en su meomento veremos cómo poder
detectarlos.
Veremos también cómo crear las rutas esas de acceso, en caso de que no existan, para así asegurarnos que
existen antes de guardar la información en el disco... pero todo a su debido tiempo...
Ahora lo que vamos a ver es unos ejemplos de cómo guardar información y después poder "leerla", ya que esto es
lo más básico y lo que en principio debemos saber.
¿Cómo guardar la información?
Ya te he dicho antes de que con Print se puede guardar la información en el disco, veamos cómo:
Print #NumFic, Nombre
Print #NumFic, 125
También podemos guardar esta misma información así:
Print #NumFic, Nombre, 125
Es decir que si quieremos guardar varias cosas con una misma instrucción, lo haremos usando una coma como
separador. De esta forma cada cosa que esté separada se guardará en el disco en "líneas" distintas.
¿Cómo leer la información?
Para poder leer la información, además de abrir el archivo para lectura modo INPUT, hay que usar una de estas
instrucciones:
Input #NumFic, Variable
También con: Line Input #NumFic, variable.
La diferencia entre el Input y el Line Input la veremos dentro de un ratillo.
Antes tendremos que ver cómo acceder a ese nombre y a ese número que antes hemos guardado...
Input #NuFic, unNombre, unNumero
Un detalle que debes tener en cuenta es que si el Nombre que guardamos tiene alguna coma, puede que no
accedas a los datos como pretendías... Vamos a verlo con un ejemplo. Crea un nuevo proyecto en el VB y añade
dos botones (CommandButton), escribe este código y pruebas:
Private Sub Command1_Click()
Dim Nombre$, Num%
Dim NumFic%
Nombre = "Pérez, Pepito"
Num = 22
NumFic = FreeFile
Open "C:\Prueba.txt" For Output As NumFic
Print #NumFic, Nombre, Num
Close NumFic
End Sub
Private Sub Command2_Click()
Dim Cadena$, Numero%
Dim nF%
nF = FreeFile
Open "C:\Prueba.txt" For Input As nF
Input #nF, Cadena, Numero
Close nF
MsgBox "Cadena= " & Cadena & vbCrLf & _
"Número= " & Numero
End Sub
Ahora pulsa en F5 y dale primero al botón Command1, después le das al Command2 y verás que no te muestra lo
esperado.
En lugar de mostrar:
Cadena= Pérez, Pepito
Número= 22
Visual Basic 2 Año 2016 Omar Palermo 47
Te ha mostrado:
Cadena= Pérez
Número= 0
¿Que ha ocurrido?
En primer lugar, decirte que esto mismo con el Basic del MS-DOS hubiese dado un error, pero debido a como
maneja el VB las variables, se ha tragado lo que ha encontrado... ¿Que ha encontrado? Pues que tiene que
asignar a Cadena la "palabra" Pérez y a la variable Numero el "número" Pepito... que al no ser un número, le ha
dado el valor cero...
Esto es debido a que cuando INPUT lee los datos espera una coma o el final de línea para "distinguir" entre los
diferentes datos a asignar a las variables indicadas.
Vale, dirás, pongamoslo en distintas líneas y así los leerá correctamente:
Input #nF, Cadena
Input #nF, Numero
Pero con esto no lo solucionarás, pruebalo y verás que tengo razón.
¡De gente desconfiada está el mundo lleno! ...no te he dicho que daría el mismo resultado... HUM!
Bien, ¿cómo solucionarlo?
¿Lo has adivinado? Pues eso mismo, usando el Line Input... Pero con Line Input no se pueden especificar más de
una variable en la misma instrucción... así que ponlas en dos líneas.
Line Input #nF, Cadena
Line Input #nF, Numero
¡OPS! ¿Que ha ocurrido?
Si has pulsado simplemente F5, te habrá dado un error al pulsar en el segundo botón... Y si has pulsado
Control+F5, te habrá indicado, con el mismo error, que los tipos no coinciden, (si usas la versión inglesa: Type
Mismatch)
Esto es debido a que Line Input sólo puede leer cadenas de caracteres, mejor dicho sólo se pueden usar variables
de tipo string (cadena), ya que esta instrucción lee todo lo que hay en la línea actual del fichero abierto, hasta el
final de la línea.
¿Cómo solucionarlo?
Usando una variable intermedia o simplemente usando el INPUT normal para leer el número.
Veamos cómo sería de las dos formas:
Line Input #nF, Cadena
Input #nF, Numero
Dim sTmp$

Line Input #nF, Cadena


Line Input #nF, sTmp
Numero = Val(sTmp)
Ahora si que tendremos el resultado correcto:
Cadena= Pérez, Pepito
Número= 22
¡EXACTO! Tampoco nos ha mostrado esto...
¿Por qué?
Muy sencillo, realmente no es sencillo, sino que después de que me haya ocurrido como dos millones de veces,
resulta hasta lógico... 8-(
Si miras el contenido del ficehro C:\Prueba.txt, te darás cuenta de que el contenido de este fichero es:
Pérez, Pepito 22
Entre Pepito y el 22 hay un tabulador, chr$(9). Esto es debido a que Print x, y muestra los valores en distintas
"posiciones" de tabulación, lo mismo ocurre cuando se guarda en el disco...
Para solucionar todo esto y hacer que la cosa funcione bien, te aconsejo que cada dato lo guardes con distintas
instrucciones Print, de esta forma cada dato se guarda en distintas líneas del fichero.
Así que cambia el código del Command1, para que en lugar de un sólo Print, haya dos:

Print #NumFic, Nombre


Print #NumFic, Num
Ahora todo debe funcionar bien.
Vale, pruebalo si no te fias...
¿Tenía yo razón? Pues claro, ...ya que lo he comprobado antes... ;-)
Si no sabemos el tipo de datos que tenemos almacenado, lo mejor es usar la intrucción Line Input y así nos
curamos en salud, pero si sabemos que, por ejemplo, todos los datos son numéricos y se han almacenado sin
usar comas...
Mejor un ejemplo:
En este caso vamos a guardar en un array una serie de números aleatorios, los vamos a guardar en un fichero y
después los leeremos para asignarlos en otro array y los mostraremos en un label.
Visual Basic 2 Año 2016 Omar Palermo 48
Para hacerlo, crea un nuevo proyecto, el anterior lo puedes borrar ya que es de una inutilidad total.
Añade un Label que ocupe prácticamente todo el Form, salvo la parte de abajo, en la que pondrás dos botones.
Pega el código este que te pongo, pulsa F5 y primero pulsa en el Command1, para después pulsar en el
Command2
Private Sub Command1_Click()
Dim Numeros(1 To 10) As Integer
Dim i%
Dim nFic%
Randomize
'Asignamos los valores
For i = 1 To 10
Numeros(i) = Int(Rnd * 100) + 1
Next
'Abrimos el fichero
nFic = FreeFile
Open "C:\Prueba.txt" For Output As nFic
For i = 1 To 10
Print #nFic, Numeros(i)
Next
Close nFic
Label1 = "Números guardados en el disco"
End Sub

Private Sub Command2_Click()


Dim MasNumeros(1 To 10) As Integer
Dim i%
Dim nFic%

'Abrimos el fichero para leer los datos


nFic = FreeFile
Open "C:\Prueba.txt" For Input As nFic
For i = 1 To 10
Input #nFic, MasNumeros(i)
Next
Close nFic
'Asignamos estos números al label:
Label1 = ""
For i = 1 To 10
Label1 = Label1 & MasNumeros(i) & vbCrLf
Next
End Sub
Este es un ejemplo sencillo de cómo asignar datos a un array, guardarlos en el disco y después leerlos.
Y hasta aquí hemos llegado...
Como ejercicio, haz un programa que al pulsar en un botón, te pida diez nombres, los guarde en un fichero y
después pulsando en otro botón los muestre en un label.
No es necesario que uses un array para guardar los datos, pero podrías hacer dos versiones, con y sin un array.
Como pista te recordaré que la función InputBox puede servirte para esto de preguntar, ya que el valor que
devuelve es la cadena de caracteres introducida en la caja de diálogo que muestra.
Esto del InputBox ya lo vimos en la novena entrega, o en las soluciones, pero te explico brevemente cómo
funciona:
variable$=InputBox("Escribe un nombre")
¿Facil, verdad? Pues esa es toda la pista que te voy a dar.

Soluciones de la entrega Doce.


Como ejercicio, haz un programa que al pulsar en un botón, te pida diez nombres, los guarde en un fichero y
después pulsando en otro botón los muestre en un label.
No es necesario que uses un array para guardar los datos, pero podrías hacer dos versiones, con y sin un array.
Solución 1: Sin usar Array:
Private Sub Command1_Click()
Dim i%
Dim nFic%
Dim Nombre$
Visual Basic 2 Año 2016 Omar Palermo 49
'Solución 1, sin array
nFic = FreeFile
'Abrimos el fichero para almacenar los datos
Open "C:\Prueba.txt" For Output As nFic
For i = 1 To 10
Nombre = InputBox("Escribe el nombre número " & CStr(i))
Print #nFic, Nombre
Next
Close nFic
End Sub
Private Sub Command2_Click()
Dim i%
Dim nFic%
Dim Nombre$
'Solución 1, sin array
Label1 = ""
nFic = FreeFile
'Abrimos el fichero para leer los datos
Open "C:\Prueba.txt" For Input As nFic
For i = 1 To 10
Input #nFic, Nombre
Label1 = Label1 & Nombre & vbCrLf
Next
Close nFic
End Sub
Solución 2: Usando un Array:
Private Sub Command1_Click()
Dim i%
Dim nFic%
Dim losNombres(1 To 10) As String
'Solución 1, sin array
'Primero preguntamos los nombres
For i = 1 To 10
losNombres(i) = InputBox("Escribe el nombre número " & CStr(i))
Next
'Ahora los guardamos
nFic = FreeFile
Open "C:\Prueba.txt" For Output As nFic
For i = 1 To 10
Print #nFic, losNombres(i)
Next
Close nFic
End Sub
Private Sub Command2_Click()
Dim i%
Dim nFic%
Dim variosNombres(1 To 10) As String
'Solución 2, con array
nFic = FreeFile
Open "C:\Prueba.txt" For Input As nFic
'Leer los diez nombres
For i = 1 To 10
Line Input #nFic, variosNombres(i)
Next
Close nFic
'mostrar los nombres
Label1 = ""
For i = 1 To 10
Label1 = Label1 & variosNombres(i) & vbCrLf
Next
End Sub
Visual Basic 2 Año 2016 Omar Palermo 50
Fijate que con esta segunda forma, si usas un array a nivel de módulo o global, puedes mostrar los datos cuando
quieras, leyéndolos una vez y después mostrándolos en cualquier ocasión, hasta que asignes nuevos datos.
Lo mismo ocurre al pedir esos nombres y después guardándolos en el disco cuando quieras.
En el siguiente listado, puedes ver un ejemplo (simple) de esto que te digo.
Fijate que a la hora de leer los nombres, primero se comprueba si existe el fichero, esto ya lo vimos en la entrega
del IF...THEN
Option Explicit
Dim losNombres(1 To 10) As String
Private Sub cmdGuardar_Click()
Dim i%
Dim nFic%
'Ahora los guardamos
nFic = FreeFile
Open "C:\Prueba.txt" For Output As nFic
For i = 1 To 10
Print #nFic, losNombres(i)
Next
Close nFic
Label1 = "Datos guardados correctamente"
End Sub
Private Sub cmdLeer_Click()
Dim i%
Dim nFic%
'Nos aseguramos que exista el fichero:
If Len(Dir$("C:\Prueba.txt")) Then
nFic = FreeFile
Open "C:\Prueba.txt" For Input As nFic
'Leer los diez nombres
For i = 1 To 10
Line Input #nFic, losNombres(i)
Next
Close nFic
Label1 = "Datos leidos correctamente"
Else
Label1 = "No existe el fichero de nombres"
End If
End Sub
Private Sub cmdMostrar_Click()
Dim i%
'mostrar los nombres
Label1 = ""
For i = 1 To 10
Label1 = Label1 & losNombres(i) & vbCrLf
Next
End Sub
Private Sub cmdPreguntar_Click()
Dim i%
Label1 = ""
'Borramos el contenido anterior
For i = 1 To 10
losNombres(i) = ""
Next
'También se puede hacer así: (esto es más rápido)
'ReDim losNombres(1 To 10)
'Preguntamos los nombres
For i = 1 To 10
losNombres(i) = InputBox("Escribe el nombre número " & CStr(i))
Next
End Sub
Private Sub Form_Load()
Label1 = ""
End Sub
Visual Basic 2 Año 2016 Omar Palermo 51

En la entrega anterior ya vimos cómo guardar y recuperar información de un fichero; vamos a seguir con el tipo
secuencial, pero esta vez vamos a leer todo el contenido de una sola vez, para ello vamos a usar dos nuevas
funciones: LOF e INPUT.No te confundas con la función INPUT, ahora verás que no se usa de igual forma de
como usamos anteriormente la "instrucción", recuerda que esta nueva, es una función y las funciones siempre
devuelven un valor. La que antes usamos era una instrucción y las instrucciones hacen algo, pero no devuelven
valores... ya verás que esto mismo se puede aplicar a las funciones; ya que estamos en ello, y a título de
curiosidad, si no quieres usar el valor devuelto por una función, cosa que se hace muchas veces con las llamadas
al API de Windows, deberás hacerlo anteponiéndole a la función la instrucción CALL. Esto lo veremos cuando
empecemos con el API, que, aunque te parezca que es un tema avanzado, lo empezaremos a ver muy pronto.
Ahora veamos cómo usar esas dos funciones:
variable = LOF(#canal)
Esta función devuelve, en bytes, el tamaño del fichero abierto con el canal indicado dentro del paréntesis.
LOF es la abreviatura de: Length Of File (longitud del fichero).
Por tanto si queremos saber la longitud de un fichero, lo abrimos, asignamos a una variable el valor devuelto por
LOF y después hacemos lo que tengamos que hacer... Que sólo quieres averiguar la longitud, pues lo cierras y ya
está, por ejemplo:
Dim nFic As Integer
Dim sFic As String
Dim tamFic As Long
sFic = "C:\Autoexec.bat"
nFic = Freefile
Open sFic For Input As nFic
tamFic = Lof(nFic)
Close nFic
MsgBox "El tamaño de " & sFic & vbCrLf & "es de " & tamFic & " bytes"

La verdad es que si lo que pretendes es saber la longitud de un fichero, puedes usar la función FileLen, ésta se
usa poniendo el nombre del fichero entre los paréntesis y también devuelve el tamaño en bytes:
(Tanto una función como la otra devuelven un valor LONG)
Dim sFic As String
sFic = InputBox("Nombre del fichero:", "Mostrar tamaño", "C:\Autoexec.bat")
If Len(sFic) Then
MsgBox "El tamaño de " & sFic & vbCrLf & "es de " & FileLen(sFic) & " bytes"
End If
Este trozo de código te preguntará, (usando InputBox), el nombre de un fichero, por defecto te mostrará
C:\Autoexec.bat y si se ha escrito algo, mostrará el tamaño en bytes.
Fijate que el valor devuelto por una función no sólo se puede asignar a una variable, sino que también se puede
usar directamente. Si, ya sé que lo he dicho en otras ocasiones, pero lo repito para que no te quede duda.
Ahora veamos cómo "funciona" la función INPUT:
Esta función devuelve una cadena con el contenido de un fichero que esté abierto... realmente devuelve el número
de caracteres que le indiquemos y esos caracteres los tomará del fichero abierto con el "canal" indicado, veamos
cómo usarla:
cadena = Input$(numCar, #canal)
El signo dólar ($), lo uso para saber que estoy trabajando con una función que devuelve un valor de cadena...
La mayoría de este tipo de funciones del Visual Basic, devuelven indistintamente un valor variant o string, para
obligarle a que devuelva una cadena, debemos ponerle al final el signo $, ni que decir tiene que esto sólo es válido
para las funciones que devuelvan cadenas de caracteres, no para las que devuelvan otro tipo de datos... Pero no
te preocupes de este tema, ya que el VB se encarga de hacer lo que tenga que hacer para que obtengas lo que
tienes que obtener... ¡¡¡Que lio!!!
Ahora vamos a ver cómo podemos leer todo el contenido de un fichero y asignarlo en una variable de cadena:
Dim nFic As Integer
Dim sFic As String
Dim tamFic As Long
Dim sContenido As String
sFic = InputBox("Nombre del fichero:", "Mostrar fichero", "C:\Autoexec.bat")
If Len(Dir$(sFic)) Then
nFic = FreeFile
Open sFic For Input As nFic
tamFic = LOF(nFic)
sContenido = Input$(tamFic, nFic)
Close nFic
Visual Basic 2 Año 2016 Omar Palermo 52
MsgBox sContenido
End If
Fijate que uso un MsgBox para mostrarlo, en caso de que el contenido del fichero sea "demasiado" grande, el
botón Aceptar no se verá... tienes dos opciones: pulsar Intro o ESC, de esta forma quitarás el mensaje de la
pantalla, a pesar de que no tenga "visible" el botón Aceptar.
Un par de detalles, la función Input$() devuelve una cadena de caracteres, el tamaño máximo de caracteres que
admite estará delimitado por el sitema operativo y sobre todo por la versión del VB, en 32 bits este tamaño "casi"
no tiene límite, pero en 16 bits, será de 64 KB como máximo.
El otro detalle es que la comprobación que se hace para saber si existe el fichero, no es a prueba de "manazas".
Me explico: si el nombre que se indica en sFic no existe y/o el path indicado tampoco, no pasa nada, todo
funcionará bien; pero si le indicas la unidad A, o cualquier otra, cuando no hay disco insertado, la cosa deja de
funcionar y te mostrará un error.
Pero todo esto tiene solución, ahora mismo te diré cual es, pero antes voy a desglosarte el funcionamiento de
Len(Dir$(...))
Fijate que aquí se usan dos funciones:
Len(Cadena) Esta función devuelve el número de caracteres de la cadena indicada
Dir$(sFichero) Esta otra, lo que devuelve es el nombre del primer fichero que coincida con la
"especificación" indicada en sFichero, en caso de que no haya coincidencias,
devolverá una cadena vacía.
Por tanto, si no existe el fichero, Dir$ devuelve una cadena vacía y la longitud de una cadena vacía es CERO, así
que en la comparación, If Len(Dir$(sFic)) Then Visual Basic sustituirá Len(Dir$(sFic)) por el valor
devuelto, recuerda que para IF un cero significa FALSO y cualquier otro valor será VERDADERO, insisto en este
punto, ya explicado anteriormente, para que se te quede claro.
Lo que quizás no sepas es que en Dir$(sFic), sFic puede contener signos comodines (? y/o *), para indicar
cualquier tipo de fichero. Esto se suele usar también en las rutinas de búsqueda que se hacen en las bases de
datos, así que mejor que te vayas enterando cómo usarlo y "buscar" por ahí información, ya que aquí te voy a
explicar un poco el significado, para que lo puedas usar al indicar los ficheros.
Como "añadido", decirte que en el sistema de búsqueda del Windows 95, también puedes buscar ficheros o
contenidos, usando estos comodines.
 La interrogación (?) se usa para indicar que no nos importa el carácter que haya en esa posición.
 El asterisco (*) sirve para que nos devuelva todos los que tengan los caracteres anteriores a este signo,
pero que los restantes no los tenga en cuenta.
Ejemplos: (las pruebas se pueden hacer desde una ventana del MS-DOS, usando el comando DIR)
Dir Auto*.bat Mostrará todos los ficheros que empiecen por Auto y que la extensión sea .bat
Dir Auto* Todos los ficheros que empiecen por Auto, sin importar ni la extensión ni nada, ya que al
o Dir Auto*.* usar .* se indica que cualquier cosa es válida.
Es recomendable usar la segunda forma.
Dir A?t*.* Mostrará todos los ficheros que la primera letra sea A y la tercera T, como se usan *, esto
indica que nos da igual lo que haya después de la T, por tanto si tuviesemos ficheros
llamados: Arte.txt, Autoexec.bat, Automovil.doc, Arial.ttf, nos devolvería los tres primeros,
porque coinciden en lo indicado.
Una vez visto, por encima, esto de los signos comodines, vamos a ver cómo hacer nuestra forma de comprobar si
existe un ficheros, algo más fiable y segura.
Para ello, vamos a crear una función que se llame: Existe y esta función devolverá FALSO (cero) si el fichero no
existe y en caso de que exista, devolverá VERDADERO (-1)
Esta función podremos usarla de cualquiera de estas dos formas:
If Existe(unFichero) Then
'Si existe, hacer lo que corresponda
End If
If Not Existe(unFichero) Then
'El fichero en cuestión no existe
End If
Para poder "detectar", o interceptar, los posibles errores que se produzcan al intentar comprobar si existe el
fichero, usaremos esto:
On [Local] Error Resume Next
Con estas instrucciones, se le indica al Visual Basic que en caso de que se produzca un error, "pase" de ese error
y continue con la siguiente instrucción.
Esto está bien, pero... ¿cómo sabremos que se ha producido el error? ya que, si continua... pues... eso, no se
detiene...
Respuesta: Usando la función ERR.
Esta función devuelve el número del error que se haya producido, o cero si no se produjo error.
A partir del VB4, esta función se puede sustituir por la propiedad Number del objeto Err, por tanto Err.Number
Visual Basic 2 Año 2016 Omar Palermo 53
también nos dirá que número de error se ha producido, ten en cuenta que Number es la propiedad por defecto de
este objeto.
Pero como quiero que este curso básico sea lo más genérico posible y no se incline sólo por el VB4 o superior...
pues intentaré usar funciones que sean compatibles... aunque tampoco prometo nada, así que te recomiendo que
dejes de lado las versiones de 16 bits o al menos la versión 3 del VB, porque esta "consideración" que estoy
teniendo puede que cambie... de hecho seguramente pondré cosas que sólo estarán disponibles a partir de la
versión 4... y cuando la cosa vaya avanzando más, incluso sólo cosas de la versión 5... aunque para esas fechas
ya estará en el mercado el VB7, por lo menos...
El caso es que yo estoy acostumbrado a usar ERR, así que... eso es lo que hay... je, je.
Y ya sin más rodeos, veamos cómo quedaría la función Existe, si te fijas es casi igual que la que puse hace ya
algunas entregas, pero usando la detección de errores:
Private Function Existe(ByVal unFichero As String) As Boolean
On Local Error Resume Next
Existe = Len(Dir$(unFichero))
If Err Then
Existe = False
End If
Err = 0
On Local Error GoTo 0
End Function
Te explico cada una de las líneas:
Private Function Existe(ByVal unFichero As String) As Boolean
Esta es la declaración de la función, el Private es por si lo quieres usar en un form, o en cualquier otro módulo,
pero que sólo sea visible para el código de ese módulo.
Si quieres que esté visible en todo el proyecto, cosa recomendable para este tipo de funciones, cambia el Private
por Public y escribe el código en un módulo BAS. Así podrás usarlo en cualquier form o módulo que tengas en tu
proyecto.
Veamos porqué he declarado el parámetro de esta forma: Byval unFichero As String
Este es el parámetro que le pasamos a la función, es decir: el fichero o especificación que queremos comprobar si
existe. Lo de especificación es porque puedes usar esta función para saber si existen archivos que empiecen por
la A, asignando al parámetro los comodines que creas necesarios: If Existe("C:\A*.*") Then comprobará si en el
directorio raiz de la unidad C hay archivos que empiecen por la letra A.
El ByVal le indica al Visual que use una copia del parámetro, con lo cual evitaremos que el código de nuestra
función lo modifique; ya que al usar una copia, no tenemos acceso al original, esto también acelera el manejo de
nuestra función.
El As Boolean del final, es el tipo de dato que devolverá nuestra función, también se podría usar Integer, de esta
forma, si estás usando el VB3, podrás usar la función, sin mayor problema.
On Local Error Resume Next
Esta es la instrucción que pone en marcha la detección de errores.
Local es para indicarle al VB que sólo esté operativa dentro de esta función, sirve para que, en caso de que
externamente haya otro mecanismo de detección de errores, el que esté operativo sea el que acabamos de
declarar... cuando abandonemos la función seguirá funcionando la otra rutina que hubiera.
Existe = Len(Dir$(unFichero))
Asigna a Existe la cantidad de caracteres devueltos por la función DIR. Al ser del tipo Boolean, el VB
automáticamente asigna Verdadero o Falso.
En el caso de que la función se haya declarado como Integer, un cero indica que es falso y cualquier otro valor que
es verdadero, por tanto la cosa funcionará igualmente independientemente del tipo devuelto por esta función.
¿Seguro?
Veamos un ejemplo:
If Not Existe("algo.txt") Then
MsgBox "El fichero indicado no existe"
End If
Prueba esto cambiando el tipo de dato devuelto por la función Existe, cuando lo pongas como Integer, siempre te
dirá que no existe el fichero...
La explicación de porqué ocurre esto, ya lo vimos anteriormente, y es porque NOT Un_Número, devuelve un
número, no un cero... Y recuerda que, cuando Existe es del tipo Integer, devuelve el número de caracteres...
Si quieres que sólo devuelva 0 ó -1 para que sea igual que False/True, podrías hacer esto otro:
Existe = Len(Dir$(unFichero)) <> 0
De esta forma se evalua la expresión y en caso de que sea cierto que el resultado es distinto de cero, se asignará
un -1, en cualquier otro caso se asignará un cero y ahora si que actuará igual que si el tipo devuelto fuese
boolean.
If Err Then
Ahora comprobamos si se produjo algún error al intentar buscar el fichero en cuestión.
Visual Basic 2 Año 2016 Omar Palermo 54
En el supuesto de que se produzca un error, se pasará a la línea después del IF, asignando FALSE al valor que
devolverá la función. Como sabrás o te habrás imaginado, el valor que debe devolver una función se asigna al
nombre de la función, en otros lenguajes se usa RETURN valor, por ejemplo en C o en el JavaScript...
Por tanto, Existe = False, sólo se asignará cuando se produzca un error.
Err = 0
Esta es una de esas recomendaciones "obligatorias" que yo haría siempre que uses el Resume Next, en este caso
no es necesario porque el código de la función termina ahí, pero si hubiese más código, ese valor de error seguiría
asignado a Err y cualquier otra comparación posterior al objeto Err, podría "alterar" el buen funcionamiento de
nuestro programa.
(Recuerda que en VB4 o posterior, realmente se asigna a la propiedad Number del objeto Err)
On Local Error Goto 0
Esta última línea "libera" la detección de errores de esta función, en nuestro caso, tampoco es necesaria, ya que al
finalizar un procedimiento, cualquier rutina de detección de errores se elimina. Esto, al igual que lo dicho
anteriormente, sirve si queremos dejar de detectar errores en las siguientes líneas de nuestra rutina. Como ya no
hay más líneas de código, la verdad es que no es necesaria, pero suelo usarla siempre, costumbres que tiene uno
de saber cuando dejo de interceptar los errores.
Vamos a probarla.
Crea un nuevo proyecto, agrega un módulo bas.
Asegurate que tiene Option Explicit al principio, ya sabes que esto es para que cualquier error tipográfico al
escribir una variable, no obligará al VB a crearla, sino que nos avisará de que esa variable no existe, es decir, sirve
para obligarnos a declarar todas las variables que vayamos a usar.
Ahora copia y pega o escribela nuevamente, la declaración de la función Existe, asegurate que sea pública y no
privada.
Cierra el módulo y asegurate que el Form1 esté visible y en modo de diseño, es decir que se vea el Form. Añade
un Label, un TextBox y un CommandButton.
Modifica el caption del label para que contenga este texto: Fichero a comprobar:
En el caption del botón escribe: Comprobar.
Situa los controles para que tengan un aspecto "agradable" y escribe lo siguiente en el Command1_Click:
Private Sub Command1_Click()
If Existe(Text1.Text) Then
MsgBox "Si existe el fichero: " & Text1.Text
Else
MsgBox "NO EXISTE el fichero: " & Text1.Text
End If
End Sub
Prueba a escribir en el TextBox signos comodines para comprobar que todo funciona, por ejemplo: *.bas
Igualmente escribe algún nombre que sepas que no existe... y para salir de dudas, intenta acceder a un fichero de
la unidad A, pero sin poner un disco...

Voy a continuar con el tema del DIR$, para ello vamos a hacer un programilla que nos muestre todos los ficheros
que coincidan con la especificación indicada... ¿recuerdas lo de los comodines? Pues en eso nos vamos a enfocar
ahora, es decir, servirá para que te muestre todos los ficheros BAS de un directorio o lo que quieras...
Para ello vamos a usar un control que hasta ahora sólo lo hemos visto, pero sin entrar en detalles sobre él: el
ListBox.
También vamos a usar el DIR$ de otra forma... una de las varias disponibles...
Crea un nuevo proyecto, añade un ListBox, un Label, un TextBox y un CommandButton,
Haz dobleclick en el Command1 y escribe:
Private Sub Command1_Click()
Dim sTmp As String
On Local Error Resume Next
sTmp = Dir$(Text1.Text)
If Err = 0 Then
Do While Len(sTmp) 'Repetir mientras haya ficheros
List1.AddItem sTmp 'Lo añadimos a la lista
sTmp = Dir$ 'Asignar el siguiente fichero
Loop
End If
Err = 0
End Sub
Ejecuta el programa y escribe *.* en el TextBox, pulsa en el botón y te mostrará en el listbox todos los archivos del
directorio actual.
Vamos a ver que es lo que hace el código:
Visual Basic 2 Año 2016 Omar Palermo 55
sTmp = Dir$(Text1.Text)
Con esto, guardamos en sTmp el primer fichero que coincida con lo que hemos escrito en el Text1.
If Err = 0 Then
Si no se produce un error...
Do While Len(sTmp)
...se entra en el bucle, pero sólo si el contenido de sTmp no es una cadena vacía. Recuerda que DIR$ devuelve
una cadena vacía si no se ha encontrado un fichero que coincida con lo indicado...
List1.AddItem sTmp
Esto añade al ListBox el contenido de sTmp
sTmp = Dir$
Fíjate que DIR$ se usa sin indicarle nada más, úsalo de esta forma si quieres que siga comprobando si hay más
ficheros que coincidan con la especificación indicada la vez anterior que se le pasó un parámetro. Si el contenido
del TextBox tenía algún signo comodín, Dir$ devolverá el siguiente fichero que coincida, en caso de que no
queden más ficheros "coincidentes", devolverá una cadena vacía.
Loop
Repite el bucle si se cumple la condición que pusimos después de DO WHILE, es decir: continuará si la longitud,
número de caracteres, de sTmp NO ES CERO.
Prueba, sin cerrar el programa, con varias cosas, por ejemplo: *.vbp, *.bas, etc.
¿Que pasa?
Si estás indicando varias cosas que buscar, y las encuentra, te darás cuenta que el listbox se va llenando... es
decir, además de lo nuevo, que estará al final, sigue lo anterior...
¿Cómo solucionarlo?
Borrando el contenido del listbox.
Para borrar el contenido del listbox, usa esto:
List1.Clear
¿Dónde debo ponerlo?
En nuestro ejemplo, yo lo pondría justo después del On Local Error..., o antes, da igual, ya que lo que se pretende
es que se borre al hacer CLICK en el botón.
Por supuesto no lo pongas dentro del DO...LOOP, ya que no serviría para lo que queremos... puesto que se
borraría continuamente... Sí, ya sé que no eres tan torpe como para hacerlo, pero...
Un detalle que puede que sea simple, pero que en nuestro ejemplo nos ahorraría pulsaciones. Si al pulsar INTRO
se simulara el CLICK del botón, nos ahorraría el tener que "desplazarnos" a ese botón para que muestre lo que ha
encontrado y si queremos seguir mostrando más cosas, el tener que desplazarnos nuevamente al TextBox.
Para conseguir esto, todo lo que tenemos que hacer, es indicarle al VB que el botón sea un botón "por defecto".
Muestra el form, y pulsa una vez en el botón, te mostrará la ventana de propiedades de este control, busca la
propiedad DEFAULT y márcala como TRUE, verás que el botón ahora está remarcado con un borde negro.
Ejecuta de nuevo el programa, escribe cualquier cosa en el TEXT1 y pulsa INTRO, ahora te mostrará los ficheros
hallados, pero el cursor permanece en el TextBox, listo para poder escribir una nueva especificación...
Ahora vamos a los ejercicios:
1.- Modifica el ejemplo para que en lugar de guardar los ficheros hallados en un listbox, lo haga en un array.
2.- Una vez hecho esto, añade al listbox todos los ficheros hallados... mejor dicho, para que no hagas trampas,
añade al listbox el contenido del array, es decir todos y cada uno de los ficheros previamente asignados.
3.- Añade otro botón al form y al pulsar en él, que guarde en un fichero, (por ejemplo: hallados.txt), todos los
ficheros hallados, es decir los que estén en el array.
Creo que con esto, ya tienes para entretenerte un rato.
Como pista, ya sabes el tipo de pistas que doy..., te diré que la asignación al array, puedes hacerla de dos formas:
UNA: Usando un número máximo de ficheros a asignar.
DOS: Usando un número variable, es decir que se añadan sólo la cantidad de ficheros hallados.
Y si haces los ejercicios de las dos formas posibles, mejor aún.

Soluciones de la entrega Catorce.


Vamos a ver la solución al primero de los ejercicios de las dos formas que propuse, es decir usando un número fijo
de ficheros y uno variable.
En el caso del número fijo, he asignado un valor de 50, como ese valor está en una constante, sólo tendrás que
cambiar el valor de la constante para cambiar el número de ficheros, veamos el listado:
Private Sub Command1_Click()
Dim sTmp As String
Const MaxFicheros = 50
Dim sFicheros(1 To MaxFicheros) As String
Dim nFic As Integer
On Local Error Resume Next
sTmp = Dir$(Text1.Text)
Visual Basic 2 Año 2016 Omar Palermo 56
nFic = 0
If Err = 0 Then
Do While Len(sTmp) 'Repetir mientras haya ficheros 'sólo asignarlo si tenemos espacio
reservado
If nFic < MaxFicheros Then
nFic = nFic + 1
'Lo añadimos al array
sFicheros(nFic) = sTmp
End If
sTmp = Dir$ 'Asignar el siguiente fichero
Loop
End If
Err = 0
End Sub
Usando un número variable de ficheros.
En esta ocasión usaremos el Redim Preserve para hacer hueco en el array que guardará los nombres de los
archivos.
Private Sub Command1_Click()
Dim sTmp As String
Dim sFicheros() As String
Dim nFic As Integer
On Local Error Resume Next
sTmp = Dir$(Text1.Text)
nFic = 0
If Err = 0 Then
Do While Len(sTmp) 'Repetir mientras haya ficheros
nFic = nFic + 1 'Adecuar el tamaño del array a los ficheros leidos
ReDim Preserve sFicheros(nFic) 'Lo añadimos al array
sFicheros(nFic) = sTmp
sTmp = Dir$ 'Asignar el siguiente fichero
Loop
End If
Err = 0
End Sub
En el segundo ejercicio, hay que guardar el contenido del array, en este caso, el array debe estar declarado a nivel
de módulo, ya que un array declarado dentro de un procedimiento es local a ese procedimiento y por tanto no
estará disponible fuera de el.
Si no lo haces así cada uno de los arrays que uses (y dimensiones) en cada SUB será sólo visible en ese
procedimiento...
Por tanto el Dim sFicheros() As String debes ponerlo en la parte de las declaraciones del form.
Este código deberás agregarlo después de asignar todos los ficheros al array, justo después del Loop, para que
esté dentro del IF que comprueba que no se haya producido error..
Dim i As Integer
'Guardar el contenido del array
Open "prueba.txt" For Output As 1
For i = 1 To nFic
Print #1, sFicheros(i)
Next
Close 1
Fijate que no he usado el Freefile para "buscar" un canal libre. En lugar de eso he usado el número 1. Te lo digo
por dos razones, la primera es para que no lo confundas con la letra L minúscula y la segunda es para que sepas
que se pueden usar constantes, aunque no te lo recomiendo, pero como en este caso, se con toda seguridad de
que mi aplicación no tiene abierto ningún otro fichero, puedo permitirme el lujo de hacerlo así, de forma directa.
El tercer ejercicio, no debería tener mayor problema, todo lo que hay que hacer es un bucle que asigne al listbox
cada uno de los ficheros del array:
List1.Clear
'Agregarle cada uno de los ficheros del array
For i = 1 To nFic
List1.AddItem sFicheros(i)
Next
Este código añadelo justo después de guardar los datos en el disco, aunque también puedes ponerlo después,
siempre que esté después del Loop, cualquier sitio es bueno.
Visual Basic 2 Año 2016 Omar Palermo 57
Fijate que a pesar de que selecciones distintos tipos de ficheros, sin cerrar el programa, por supuesto, estos no se
incrementan en la lista, no sólo por el List1.Clear, sino porque al hacer Redim Preserve el número de elementos
del array se adapta al valor de nFic y este valor empieza siempre por cero, así que siempre se tendrá en el array el
número correcto de ficheros.
La asignación que hago para ponerlo a cero, no es necesaria, ya que cuando se dimensiona una varible numérica,
ésta variable contiene inicialmente un valor cero.
Pero imaginate que no haces esa asignación o que quieres asegurarte que el contenido del array se "libere" antes
de empezar a asignarle datos... para ello tendrías que usar: ERASE sFicheros, con esta instrucción borramos el
contenido del array. En el caso de que el número de elementos del array fuese fijo, a lo que se llama un array
estático, simplemente se borraría el contenido del array, pero seguiría existiendo el array con las 50 "dimensiones"
creadas. Si, por el contrario, el array es dinámico, es decir que podemos cambiar el tamaño del mismo, lo que
hacemos es "eliminarlo" de la memoria, por tanto necesitaremos dimensionarlo (o REdimensionarlo) para poder
usarlo nuevamente.

En esta entrega vamos a preparar una pequeña utilidad que nos permitirá editar un fichero de texto y guardar los
cambios en el disco; no le pidas peras al olmo que no te las dará, así que no creas que vamos a "programar" un
verdadero editor... eso puede que sea más adelante... supongo...
Realmente el acceso secuencial va a pintar poco en esto que vamos a hacer, pero nos servirá para ir "tomándole"
cariño a unos controles que usaremos en el 99.9% de nuestras aplicaciones.
Para esta tarea, crea un nuevo proyecto y añade un label, dos textbox y dos commandbutton.
El Textbox grande (Text2) tiene la propiedad Multiline a True, de esta forma se irá mostrando todo el contenido
conforme lo vayamos rellenando.
Los nombres de los otros controles serán Label1, Text1, cmdAbrir para el botón de Abrir y cmdGuardar para el de
guardar.
El problema de los textbox es que no soportan más de 64KB, aunque en teoría si, pero en la práctica lo que
soportan son unos 32000 caracteres, que en la versión de 32 bits suponen esos 64KB, por si no lo sabes en
Windows de 32 bits cada carácter está representado por dos bytes.
Para solventar ese "pequeño" inconveniente, vamos a dar por hecho de que sólo admite 32000 caracteres, de esta
forma también nos servirá el programa en VB de 16 bits.
A este proyecto hay que agregarle un módulo BAS que tenga la función Existe que vimos en la entrega trece, si
no quieres añadir un nuevo módulo, simplemente copia y pega esa función en este formulario.
Si la función la usas desde un módulo BAS, debe ser pública, si la pegas en este formulario, puede ser privada. En
el formulario también puede ser pública, pero lo que nos interesa es que pueda accederse desde el form, así que
no es necesario declararla de esa forma.
Ahora añade el siguiente código para el botón abrir:
Private Sub cmdAbrir_Click()
'Abrir
Dim i As Long, tamFic As Long
If Existe(Text1.Text) Then
Text2.Text = ""
i = FreeFile
Open Text1.Text For Input As i
tamFic = LOF(i)
Text2.Text = Input$(tamFic, i)
Close i
End If
End Sub
Esta rutina no está hecha a prueba de fallos, pero no te preocupes que ya lo harás... ¿como ejercicio?
¡efectivamente!
Este es el código del botón guardar:
Private Sub cmdGuardar_Click()
'Guardar
Dim i As Long
i = FreeFile
Open Text1.Text For Output As i
Print #i, Text2.Text
Close i
End Sub
Fíjate que código más corto... ¡así da gusto hacer programas!
Aunque tampoco está preparado para cualquier impedimento...
Vamos a añadirle una serie de mejoras, entre ellas el que avise, al guardar, si es que vamos a sobreescribir un
fichero existente.
Visual Basic 2 Año 2016 Omar Palermo 58
Otra mejora sería comprobar si se ha modificado el contenido del Text2 y avisar antes de abrir un nuevo fichero.
La tercera mejora que se me ocurre es comprobar que no se abra un fichero con más caracteres de los
"permitidos"... aunque este lo dejaré para que lo hagas tú.
La razón de poner estas mejoras por separado, es decir, contártelo antes de hacerlo, es para darte la oportunidad
de que lo hagas por tu cuenta... Ahora no recuerdo si tienes la base suficiente para hacerlo, pero... podría ser...
compruébalo por tu cuenta.
Antes de darte la solución a dos de estas tres mejoras, voy a contarte.
Cuando usamos un textbox multiline, es decir en el que podemos escribir varias líneas de texto, el Visual nos da
la posibilidad de poder usarlo de varias formas, si no le decimos nada, conforme vayamos escribiendo, se irá
mostrando el texto en la siguiente línea, a esto se le llama wordrap o algo así, que viene a significar que no se
corten las palabras al cambiar de línea... para hacer esto mismo en el BASIC normalito del MS-DOS, había que
crear una rutina para comprobar cuando había que mandar una palabra a la siguiente línea... dejemos los viejos
tiempos y continuemos con los nuevos...
Pero si lo que quieres es que cada línea escrita se quede en la misma línea hasta que pulses intro, deberás
indicárselo al Visual Basic, diciéndole que sólo añada al textbox un scroll horizontal, pruébalo y decide...
¿Cómo añadirle los scrollbars... no pienses que tienes que usar los controles que el Visual tiene para eso, sólo
debes modificar la propiedad ScrollBars del control Text2. Tienes varias opciones:
0- None Ningún scrollbar
1- Horizontal Sólo el scroll horizontal, para cambiar de línea debes pulsar Intro
Sólo el scroll vertical, esto es como sin scrolls, pero nos permite navegar hacia
2- Vertical
abajo.
3- Both Ambos scrolls, pues eso, una mezcla de los dos.
Si los compruebas, verás al asignar a esta propiedad el valor 0 y 2, el resultado es el mismo, al menos en lo que
se refiere a la hora de escribir en él, ya que el resultado visual es diferente; a lo que me refiero es que el texto se
ajustará automáticamente haya o no un intro para separar cada línea, la diferencia, es que con el scroll vertical,
podemos navegar fácilmente hacia la parte no visible del texto escrito.
Cuando especificamos el Scroll horizontal, tanto con el valor 1, como con el 3, ya te darás cuenta de que cada
línea está separada, siempre que hayas pulsado Intro para cambiar de línea. También puedes usar Shift+Intro o
Control+Intro para efectuar un cambio de línea.
Para comprobarlo, haz el textbox más pequeño, al menos en anchura, por ejemplo ajústalo al tamaño del Text1 y
escribe varias líneas, pulsando en algunas Intro y otras escribiendo bastante texto... Luego decide que tipo de
scroll prefieres.
Una cosa que debes saber es que esta propiedad es de sólo lectura, al menos en tiempo de ejecución, osea que
no puedes cambiar que scrolls deben mostrarse una vez que el programa está en funcionamiento.
Me imagino que te habrás fijado que en el Notepad (Bloc de Notas) que incluye el Windows, existe una opción
para ajustar las líneas automáticamente, es decir para usar los dos scrolls o sólo el vertical.
¿Cómo se consigue este efecto si no se puede cambiar la propiedad ScrollBars?
Pues... usando dos TextBoxes, uno de ellos con la propiedad ScrollBars a 2 (Vertical) y el otro asignando el valor 3
(Both)
Para probarlo, deberás crear un array del Text2, para ello, cópialo y vuelve a pegarlo, te preguntará si quieres
tener una matriz de este control, contéstale que sí. Al Text2(0), el que tiene el valor Index igual a CERO, asígnale
la propiedad ScrollBars a 2 y al otro, el valor 3. Sitúalos en la misma posición del form, para que uno esté encima
del otro, no te preocupes de cual está encima, eso lo controlaremos nosotros.
Añade un nuevo botón, escribe en el Caption: Ajustar líneas y dale el nombre cmdAjustar.
Declara una variable a nivel de módulo para saber cual es el TextBox que está activo:
Dim QueText2 As Integer
En el Form_Load escribe esto:
Text2(0).Zorder para que se ponga encima del otro TextBox.
En el evento Click del nuevo botón añade este código:
Private Sub cmdAjustar_Click()
Dim QueText2Ant As Integer
'Valor del TextBox actual
QueText2Ant = QueText2
'Nuevo valor
QueText2 = QueText2 + 1
'si nos pasamos... volvemos al principio
If QueText2 > 1 Then QueText2 = 0
'asignar al nuevo textbox el contenido
Text2(QueText2).Text = Text2(QueText2Ant).Text
'Lo hacemos visible
Text2(QueText2).ZOrder
Visual Basic 2 Año 2016 Omar Palermo 59
'borramos el contenido anterior
Text2(QueText2Ant).Text = ""
End Sub
Ahora tendrás que cambiar el código usado para leer y escribir, simplemente cambia el Text2.Text por
Text2(QueText2).Text y asunto arreglado, ya que el text que estará visible es el que indica esa variable.
Ejecuta el programa, escribe algo en el textBox, preferiblemente alguna línea larga y pulsa Intro para crear otras,
pulsa en el botón de ajustar líneas y verás el efecto.
Si quieres tener esta posibilidad en este programa, deberás recordar de cambiar cualquier uso de Text2 por
Text2(QueText2), ya que si no lo haces, el Visual Basic se encargará de recordártelo...
Bueno, creo que el rollito este ha sido más largo de lo que tenía previsto, pero espero que haya valido la pena.
Vamos a ver las soluciones a las mejoras... las soluciones las voy a dar suponiendo que no tienes esta posibilidad
de usar los dos TextBoxes para el ajuste de línea, es que sino, me va a romper todo el esquema que tenía previsto
y no es plan...
Para saber cuando se va a sobreescribir el fichero, lo que hay que hacer es comprobar primero si ese fichero ya
existe y después de comprobarlo, avisar con un MsgBox si se quiere sobre-escribir, en caso negativo simplemente
no se guarda el contenido del TextBox en el fichero.
Vamos a ver el código necesario, este deberá estar en el botón de Guardar... (elemental mi querido Watson)
Private Sub cmdGuardar_Click()
'Guardar
Dim i As Long
Dim SobreEscribir As Boolean
'Se asigna el valor Verdadero, por si no existe
SobreEscribir = True
'Si ya existe, preguntar
If Existe(Text1.Text) Then
If MsgBox("ATENCIÓN, el fichero ya existe." & vbCrLf & _
"¿Quieres sobreescribirlo?", vbQuestion + vbYesNo) = vbNo Then
'Hemos contestado que no, así que...
SobreEscribir = False
End If
End If
'Si no existe o se quiere sobreescribir...
If SobreEscribir Then
i = FreeFile
Open Text1.Text For Output As i
Print #i, Text2.Text
Close i
End If
End Sub
Fíjate en el MsgBox, al usar & _ es para que el VB pueda mostrar en líneas distintas lo que se debería escribir en
una misma.
Después usamos vbQuestion para que muestre la interrogación y vbYesNo es para que nos muestre los botones
SI / NO.
Si pulsamos en Si, no se cumple la condición y si pulsamos en NO, si que se cumple, por tanto asignamos un
valor falso a la variable SobreEscribir para que en la siguiente comparación no se cumpla y no se guarde el
contenido del Text2.
Al principio le he dado el valor VERDADERO a la variable que decide si se debe sobrescribir o no, esto lo he
hecho porque si no existe el fichero, no nos preguntará y si no le damos de "arrancada" el valor Verdadero, nunca
lo tendrá, ya que la única asignación que hacemos es la darle un valor FALSO, en caso de que no queramos
guardar el contenido.
Como habrás podido comprobar, para poder guardarlo con otro nombre, deberás escribir ese nombre en el Text1.
Para la segunda mejora, necesitaremos una variable a nivel de módulo, así que añade esta declaración en la
sección de las declaraciones generales del formulario:
Dim Modificado As Boolean
Como recordarás de la segunda entrega, en los TextBoxes hay un evento, CHANGE, que se "dispara" cada vez
que cambia el contenido de la propiedad Text de estos controles. Usaremos este evento para saber cuando se ha
cambiado el contenido del Texto escrito.
Añade este código al formulario:
Private Sub Text2_Change()
Modificado = True
End Sub
Visual Basic 2 Año 2016 Omar Palermo 60
De esta forma, cada vez que se cambie el contenido de este control, se cambiará también el valor de esa variable
y así podremos saber si se ha cambiado o no.
Esto lo comprobaremos en el procedimiento Abrir, de forma que si se ha modificado, nos pregunte si queremos
guardarlo antes de abrir uno nuevo. El código, más o menos sería algo así:
If Modificado Then
If MsgBox("El texto se ha modificado..." & vbCrLf & _
"¿Quieres guardarlo?", vbQuestion + vbYesNo) = vbYes Then
'Guardarlo
'...
End If
End If
Realmente no sería tan simple. Ahora lo veremos al completo.
Con esto de comprobar si está modificado se nos presentan dos problemas:
El primero es que al no conservar el nombre del fichero abierto anteriormente, o con el que acabamos de guardar
lo que hayamos escrito antes de intentar abrir otro, no sabremos con que nombre guardarlo, ya que al usar el
contendido del Text1, éste puede cambiar y seguramente no conseguiríamos nuestro objetivo.
Por suerte, la solución a este inconveniente es tan simple como la de usar una variable, a nivel de módulo, para
guardar el nombre del fichero abierto o guardado por última vez.
Fíjate que uso variables a nivel de módulo para algunas cosas, de esta forma estas variables, como ya deberías
saber, estarán disponibles en cualquier parte del módulo actual, en este caso: el formulario.
Añade esta declaración en la parte general de las declaraciones del formulario:
Dim sFichero As String
Ahora modifica el código para que podamos usar esta variable:
Private Sub cmdAbrir_Click()
'Abrir
Dim i As Long, tamFic As Long
Dim sTmp As String
If Modificado Then
If MsgBox("El texto se ha modificado..." & vbCrLf & _
"¿Quieres guardarlo?", vbQuestion + vbYesNo) = vbYes Then
'Conservar el nombre actual
sTmp = Text1.Text
'y asignar el anterior
Text1.Text = sFichero
'guardarlo...
cmdGuardar_Click
'Volvemos a dejar el Text1 como estaba
Text1.Text = sTmp
End If
End If
'Asignamos el nombre del fichero
sFichero = Text1.Text
If Existe(sFichero) Then
Text2.Text = ""
i = FreeFile
Open sFichero For Input As i
tamFic = LOF(i)
Text2.Text = Input$(tamFic, i)
Close i
End If
End Sub
Fíjate en las asignaciones que hay que hacer antes de guardar el contenido del Text2, esto es necesario, ya que
en ese evento se usa el contenido del Text1, para saber en que fichero se debe guardar.
Bien, ya tenemos una parte resuelta, la otra es que una vez que la variable Modificado ha tomado el valor TRUE
no lo suelta.
Este valor debería de dejar de valer verdadero cuando lo guardemos, así que añade al final del procedimiento que
guarda el fichero, esta asignación:
Modificado = False
Realmente deberás ponerla después del Close i y dentro de la comparación que decide si se debe guardar o no el
contenido del Text2.
Visual Basic 2 Año 2016 Omar Palermo 61
También tendremos que asignar en este procedimiento el valor de la variable sFichero, para que al asignarse en el
evento Abrir su valor al TextBox, éste tome el que tuviera esa variable cuando se guardó un fichero.
Esto deberás hacerlo una vez que se guarde el fichero, es decir, si no hemos cancelado la grabación.
sFichero = Text1.Text
Aunque pueda parecer que realmente no tiene sentido el uso de esta variable, ya que tanto en Guardar como en
Abrir se le asigna el contenido del Text1, lo tiene en el caso de querer abrir uno nuevo, antes de haber guardado
los cambios, si no se tuviera esta variable, no conservaríamos el nombre del fichero, antes de haber modificado el
contenido del Text1 para asignar un nuevo nombre...
Aunque parte de este "come-coco" se solucionaría con el uso de un cuadro de diálogo que preguntara el nombre
del fichero a abrir o a guardar, de esta forma no sería necesario dejar siempre un TextBox para que se pueda
escribir el nombre.
Aún así, necesitaríamos una variable para conservar el nombre del fichero...
Bien, vamos a probar esto... a ver si funciona bien...
Como podrás comprobar, no está todo lo "refinado" que quisiéramos... Después de abrir un fichero y sin haber
modificado el contenido del Text2, intenta abrir otro, te dirá que el texto se ha modificado...
¿Por qué ocurre esto?
Cuando asignamos al Text2 el contenido del fichero, estamos "cambiándolo", por tanto se produce el evento
Change y se asigna el valor de la variable Modificado, así que también tendremos que asignar un valor FALSE a
esta variable después de abrir el fichero y asignarlo al Text2, es decir al final del procedimiento Abrir.
De esta forma sólo se activará la "alarma" cuando realmente se modifique el texto.
Este valor asignarlo justo después de cerrar el fichero o después de haber asignado al Text2 el contenido del
fichero.
Para finalizar, un par de cosillas para mejorar.
Cuando un form se abre, cosa que ocurre automáticamente al iniciarse este programa, se produce el evento Load,
este ahora no nos interesa, el que nos interesa es el que se produce cuando el form se cierra, cosa que también
ocurre al cerrar la aplicación, es decir el evento Unload.
En este evento también se puede poner una comprobación para que, en caso de no haber guardado el contenido
del Text2, tengamos la oportunidad de poder guardarlo antes de cerrar definitivamente el form.
Así que, añade este código en el evento Form_Unload:
Private Sub Form_Unload(Cancel As Integer)
If Modificado Then
If MsgBox("El texto se ha modificado..." & vbCrLf & _
"¿Quieres guardarlo?", vbQuestion + vbYesNo) = vbYes Then
'Asignar el anterior
Text1.Text = sFichero
'guardarlo...
cmdGuardar_Click
End If
End If
Set Form1 = Nothing
End Sub
Aunque en el caso de que no hayamos usado ningún nombre, el contenido de sFichero, no sea adecuado, así que
también podríamos tener un valor por defecto en esta variable, que sería el que se mostrase al iniciarse el
programa, por tanto vamos a añadir un valor por defecto a esta variable y al Text1, esto lo haremos en el evento
Form_Load:
Private Sub Form_Load()
sFichero = "Prueba15.txt"
Text1.Text = sFichero
End Sub
Fíjate que al final del Form_Unload he puesto Set Form1 = Nothing, esto se suele usar cada vez que
descarguemos un formulario, para asegurarnos que se libere la memoria que pudiera estar ocupando... de esto ya
veremos más cuando nos topemos con las clases y la creación de objetos... piensa que cada control y formulario
es un objeto y cuando un objeto no se usa, debemos indicarle al Visual que se deshaga de él... Pero esto lo
veremos en otra ocasión.

Ahora los ejercicios:


1.- Como ya he comentado, los TextBoxes no soportan todos los caracteres que quisiéramos, para redondear,
digamos que soportan 32000. Esto es casi cierto en VB de 16 bits, realmente admiten 32767 (32 KB).
En 32 bits en teoría soportan 64 KB, pero como el entorno de 32 bits usa caracteres de 2 bytes, estos 64 kas se
quedan en 32.
Para 16 bits, existe una función del API de Windows que permite asignar 64 KB a un TextBox, en 32 bits no tiene
ningún efecto ya que, como he repetido más de una vez... admite 64 KB.
Visual Basic 2 Año 2016 Omar Palermo 62
Para no liarte más de lo que ya puedas estar, vamos a dar por sentado que sólo se pueden asignar a un TextBox
32000 caracteres, que en 32 bits no es lo mismo que 32000 bytes... (je, que me gusta liar al personal)
Lo que tienes que hacer es que a la hora de abrir un fichero, compruebes que no tenga más de 32000 caracteres y
en caso de que el fichero los tenga, no abrirlo.
2.- En este segundo ejercicio, lo que vas a hacer es leer sólo 32000 caracteres del fichero que tenga más de
esos... así al menos podrás editar esa parte de los ficheros grandes, cosa que no es recomendable, sobre todo si
lo guardas, ya que podrías "cargarte" un fichero que puede ser necesario... El que avisa...
Aunque, para curarte en salud, podrías impedir que se guardase un fichero del cual no se hayan leído todos los
caracteres que tuviese... por tanto, un tercer ejercicio:
3.- Si el fichero abierto tiene más de 32000 caracteres, leer sólo esta cantidad y usar un "flag" para impedir que se
guarde...
Soluciones de la entrega Quince.

'Esta es la parte que se encarga de abrirlo, el código anterior lo he omitido


'Asignamos el nombre del fichero
sFichero = Text1.Text
If Existe(sFichero) Then
Text2.Text = ""
i = FreeFile
Open sFichero For Input As i
tamFic = LOF(i)
If tamFic <= 32000 Then
Text2.Text = Input$(tamFic, i)
End If
Close i
'Una vez abierto, no está modificado
Modificado = False
End If
En este caso, lo único que hay que hacer es comprobar si el tamaño es menor o igual al máximo indicado, si es
así, abrir el fichero, por tanto, sólo tendremos que poner la parte que asigna al Text2 el contenido del fichero,
dentro de la comparación.
2.- En caso de que sea mayor de 32000 caracteres, leer sólo los 32000 primeros.
La solución es casi como la anterior, bueno, casi, es decir tendremos que hacer una comparación y en caso del
que sea cierta, asignar a la variable que indica el número de caracteres a leer el valor máximo que queremos.
'Asignamos el nombre del fichero
sFichero = Text1.Text
If Existe(sFichero) Then
Text2.Text = ""
i = FreeFile
Open sFichero For Input As i
tamFic = LOF(i)
If tamFic > 32000 Then
tamFic = 32000
End If
Text2.Text = Input$(tamFic, i)
Close i
'Una vez abierto, no está modificado
Modificado = False
End If
Por tanto comprobamos si el contenido de tamFic es mayor de 32000, en caso de ser cierto, se cumple la
condición, asignamos este valor a esa variable, de esta forma sólo se leerán esos caracteres.
3.- Si el tamaño es mayor de 32000 no poder guardarlo (y como extra: no poder editarlo)
Para conseguir esto, debemos aprovechar el código usado en la segunda solución, de forma que si es mayor de
32000, podamos asignar un flag, para saber que no se debe guardar.
También podríamos evitar que se modificase el contenido del TextBox, esto se consigue asignando la propiedad
Locked a True. De esta forma no se podrá escribir en el TextBox. Por supuesto que en caso contrario se debería
permitir la escritura, ahora veremos cómo hacer estas cosas.
Veamos las cosas por partes:
'Asignamos el nombre del fichero
sFichero = Text1.Text
If Existe(sFichero) Then
Text2.Text = ""
Visual Basic 2 Año 2016 Omar Palermo 63
i = FreeFile
Open sFichero For Input As i
tamFic = LOF(i) 'Nos aseguramos que se pueda editar
Text2.Locked = False
Text2.ForeColor = vbWindowText
If tamFic > 32000 Then
tamFic = 32000 'Si el tamaño no es el leido, 'no permitir escribir
Text2.Locked = True 'si además cambiamos el color... mejor
Text2.ForeColor = vbGrayText
End If
Text2.Text = Input$(tamFic, i)
Close i
'Una vez abierto, no está modificado
Modificado = False
En esta primera solución, aún no evitamos que se pueda guardar, pero al menos cambiamos el color del texto y
evitamos que se pueda modificar el contenido, para ello he cambiado el valor de la propiedad ForeColor, usando
dos constantes predefinidas, la primera es para asignar el color normal del texto y la segunda es para asignar el
color Gris cuando no podamos escribir en el TextBox, la ventaja de usar estas constantes, al menos con el VB5, es
que si cambias los colores, usando el panel de control de Windows, estos se usan de forma automática al
asignarlos con estas constantes. Ahora pasemos a ver cómo evitar que se guarde, cuando se abra parcialmente
un fichero. Para ello necesitaremos otra variable a nivel de módulo:
Dim PoderGuardar As Boolean
Con esta indicaremos que podemos o no guardar el contenido del TextBox.
Se asignará al abrir el fichero y se comprobará al guardarlo.
Veamos al completo los procedimientos de Abrir y Guardar:
Private Sub cmdAbrir_Click()
'Abrir
Dim i As Long, tamFic As Long
Dim sTmp As String
If Modificado Then
If MsgBox("El texto se ha modificado..." & vbCrLf & _
"¿Quieres guardarlo?", vbQuestion + vbYesNo) = vbYes Then
'Conservar el nombre actual
sTmp = Text1.Text
'y asignar el anterior
Text1.Text = sFichero
'guardarlo...
cmdGuardar_Click
'Volvemos a dejar el Text1 como estaba
Text1.Text = sTmp
End If
End If
'Asignamos el nombre del fichero
sFichero = Text1.Text
If Existe(sFichero) Then
Text2.Text = ""
i = FreeFile
Open sFichero For Input As i
tamFic = LOF(i) 'Nos aseguramos que se pueda editar
Text2.Locked = False
Text2.ForeColor = vbWindowText 'y que se pueda guardar
PoderGuardar = True
If tamFic > 32000 Then
tamFic = 32000 'Si el tamaño no es el leido, no permitir escribir
Text2.Locked = True
'si además cambiamos el color... mejor
Text2.ForeColor = vbGrayText 'No permitir que se guarde
PoderGuardar = False
End If
Text2.Text = Input$(tamFic, i)
Close i
'Una vez abierto, no está modificado
Visual Basic 2 Año 2016 Omar Palermo 64
Modificado = False
End If
End Sub
Private Sub cmdGuardar_Click()
'Guardar
Dim i As Long
Dim SobreEscribir As Boolean 'Sólo se ejecuta el código si se puede guardar
If PoderGuardar Then
'Se asigna el valor Verdadero, por si no existe
SobreEscribir = True
'Si ya existe, preguntar
If Existe(Text1.Text) Then
If MsgBox("ATENCIÓN, el fichero ya existe." & vbCrLf & _
"¿Quieres sobreescribirlo?", vbQuestion + vbYesNo) = vbNo Then
'Hemos contestado que no, así que...
SobreEscribir = False
End If
End If
'Si no existe o se quiere sobreescribir...
If SobreEscribir Then
i = FreeFile
Open Text1.Text For Output As i
Print #i, Text2.Text
Close i
'Ya hemos guardado las modificaciones
Modificado = False
sFichero = Text1.Text
End If
End If
End Sub

Bueno, ya vale... vamos al tema de la presente entrega: Los ficheros aleatorios (Random)
Pues eso, ahora le toca el turno a los ficheros aleatorios. Ya te comenté que este tipo de fichero se suele usar
cuando todos los datos que incluye tienen la misma longitud. Normalmente a cada uno de los datos guardados se
les llama registro. Todos los registros tienen la misma longitud, o al menos deberían tenerla... no, no es una
contradicción, pero si quieres que funcione bien, deben ser iguales, porque el sistema que usa el VB para acceder
a cada uno de los registros es una simple operación:
Posición_Actual = (Número_Registro -1) * Tamaño_del_Registro + 1
Pero no te asustes... tú no tienes que hacer ningún cálculo... de eso se encarga el Visual Basic.
Para acceder a los datos de los ficheros abiertos como Random, vienen como anillo al dedo, (si es que el anillo te
encaja bien), los tipos definidos por el usuario, aunque cualquier cadena serviría para esta tarea.
Antes de empezar a manejar ficheros de acceso aleatorio, es importante planear lo que vamos a almacenar en
ellos; siempre se debe planear, pero en esta ocasión es un poco más importante, más que nada porque, como ya
he dicho, tenemos la obligación de saber la longitud de los datos, (al menos de cada uno de los registros), a
almacenar. Ya que esa longitud, (la de cada registro), debemos especificarla a la hora de abrir el fichero.
Un pequeño detalle antes de continuar, cuando abrimos un fichero secuencial para lectura (Input), el Visual Basic
o el sistema operativo, sólo nos permite leer datos de ese fichero, es decir: no podemos leer y escribir al mismo
tiempo. Igualmente ocurre cuando lo abrimos para escritura (Output), sólo que en esta ocasión sólo podemos
escribir y no leer. Sin embargo, con los ficheros abiertos como aleatorios (Random) o binarios (Binary), una vez
que el fichero esté abierto, podemos leer o escribir indistintamente. Alguna ventaja teniamos que tener... no iban a
ser todo "obligaciones".
Sigamos con lo nuestro...
Veamos cómo, por fin, usar estos ficheros:
Para verlo... nada mejor que con un ejemplo:
Primero definimos un tipo de datos, en este caso vamos a guardar el nombre, edad y la cuenta de email de unos
colegas, por tanto emplearemos un tipo definido con estos tres campos, veamos:
Private Type t_colegas
Nombre As String * 30
Edad As Integer
email As String * 50
End Type
Declaramos una variable de este "nuevo" tipo de datos, que será la que usaremos para acceder al fichero:
Visual Basic 2 Año 2016 Omar Palermo 65
Dim unColega As t_colegas
Y abrimos el fichero:
Open "miscolegas.dat" For Random As nFic Len = Len(unColega)
El Len(unColega), le está indicando al VB que la longitud de cada registro es la que tenga ese tipo de datos.
No siempre hay que hacerlo así, podríamos asignar a una variable esa longitud y usarla para abrir el fichero:
lenColega = Len(unColega)
Open "miscolegas.dat" For Random As nFic Len = lenColega
Lo que quiero dejar claro es que al Visual Basic no le importa con qué variable accedemos al fichero, sino que
longitud tendrá esa variable, o si lo prefieres, cual será la longitud de cada registro.
Por tanto, si sabemos que nuestro tipo de datos, (o lo que es lo mismo: cada registro), tiene 82 bytes de longitud,
podríamos usar un string con esa longitud para acceder al fichero en cuentión y...

Esto..., cuando quieras leer el contenido de un registro en particular del fichero abierto, usa esta instrucción:
Get #canal, num_registro, variable_de_datos
Para escribir usa esta otra:
Put #canal, num_registro, variable_de_datos
Es decir GET para leer y PUT para escribir, en esto los ingleses lo tienen más fácil, ya que al menos a ellos esas
dos palabras tienen algo de sentido...
Después de cualquiera de estas instrucciones se indica el número de fichero (#canal), seguido por el número de
registro al que queremos acceder, (num_registro), y por último la variable, (variable_de_datos), que usamos
para leer, (GET), los datos del disco o almacenarlos en él (PUT)
Un detalle que tienes que tener siempre presente al definir un tipo de datos para acceder a los ficheros aleatorios:
asegurate de que todas las cadenas contenidas en ese tipo, sean de longitud fija; lo mismo si dentro del tipo usas
arrays, estos deben ser de índices constantes, es decir que estén definidos al crear el tipo.
Todas estas "precauciones" se consiguen con datos "estáticos", es decir que permanecen sin cambios, al menos
en lo que a tamaño se refiere. Los "dinámicos" son los que pueden cambiar "dinámicamente" de tamaño.
Aclaremos estos puntos antes de continuar.
Una variable declarada de esta forma:
Dim cadenaEstatica As String * 50
Siempre tendrá 50 caracteres, incluso si hacemos esta asignación:
cadenaEstatica = ""
Con esto no eliminamos las 50 celdas de memoria, sino que la rellenamos de espacios...
Prueba con este código:
Crea un nuevo proyecto y añade esto en el Form_Load:
Private Sub Form_Load()

Dim cadenaEstatica As String * 50

Show

cadenaEstatica = "Hola"
Print cadenaEstatica & "Pepe"
cadenaEstatica = ""
Print cadenaEstatica & "Pepe"
Print
Print "Longitud de la cadena:"; Len(cadenaEstatica)
Print "Asc(cadenaEstatica) ="; Asc(cadenaEstatica)
End Sub
El resultado sería: una cadena estática (o de longitud fija), siempre tendrá el número de caracteres que le
indicamos al declararla.
Sin embargo una cadena dinámica sólo tendrá los caracteres que le asignemos.
Prueba este código:
Private Sub Form_Load()
Dim cadenaDinamica As String

Show

cadenaDinamica = "Hola"
Print cadenaDinamica & "Pepe"
cadenaDinamica = ""
Print cadenaDinamica & "Pepe"
Print
Print "Longitud de la cadena:"; Len(cadenaDinamica)
Visual Basic 2 Año 2016 Omar Palermo 66
Print "Asc(cadenaDinamica) ="; Asc(cadenaDinamica)

End Sub
Habrás obtenido algo parecido a esto: "Illegal Function Call", este error lo da al intentar conseguir el código ASCII
de la cadena... pero como la cadena está vacía... pues no hay nada que obtener, por tanto: ¡¡¡Error!!!
Aparte del error, te puedes fijar que "HolaPepe" sale junto y que la longitud de la variable es cero.
Cuando asignamos una cadena vacía a un string dinámico, lo dejamos "seco", es decir sin nada en el interior...
Lo mismo ocurre con los arrays, ya sean de caracteres o numéricos.
Los estáticos siempre conservan el número de elementos, incluso cuando se eliminan, (al menos eso es lo que
parece), con Erase. Aunque esto ya lo vimos, no está de más recordarlo: Al "eliminar" con Erase un array
dinámico, lo eliminamos de la memoria, pero en los estáticos, simplemente ponemos el contenido a cero, (o a
cadenas vacias si son de cadenas dinámicas).
Pruebalo:
Dim arrayEstaticoInteger(1 To 10) As Integer
Dim arrayEstaticoString(1 To 5) As String

Dim arrayDinamicoInteger() As Integer


Dim arrayDinamicoString() As String
Cuando se declaran los arrays con el número de elementos que va a contener, siempre con constantes, estos
arrays son estáticos, porque siempre contendrán ese número de elementos.
Sin embargo los dinámicos, se declaran sin decirles cuantos elementos van a contener y después se usa Redim o
Redim Preserve para cambiar el número de elementos.
Veamos un ejemplo de un tipo definido con un array estático:
Private Type t_colegas2
Nombre As String * 30
Edad As Integer
email(1 To 3) As String * 50
End Type
Aquí hemos definido un "campo" email con un array de tres elementos.
Recuerda que aunque los tipos definidos permitan tanto cadenas de longitud variable como arrays dinámicos, si
ese tipo se va a usar para acceder a datos de un fichero aleatorio, su longitud siempre tiene que ser fija y coincidir
con la longitud que se indicó a la hora de abrir ese fichero... si no tienes esta "precaución"... no accederás con
"fiabilidad" a los datos contenidos en ese fichero... después no digas que no te advertí...
De todas formas, vamos a comprobarlo...
Escribe este código en un form:
'Este código en las declaraciones del form
Option Explicit

Private Type t_colegas


Nombre As String * 30
Edad As Integer
email As String * 50
End Type
Dim unColega As t_colegas

Private Type t_colegaDinamico


Nombre As String
Edad As Integer
email As String
End Type
Dim unColegaDinamico As t_colegaDinamico

Private Sub Form_Load()


Dim nFic As Integer

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

'Asignamos los datos


With unColega
.Nombre = "Pepe"
.Edad = 22
.email = "pepe22@mundo.net"
Visual Basic 2 Año 2016 Omar Palermo 67
End With
Put #nFic, 1, unColega

'ahora lo hacemos con el colega dinámico


With unColegaDinamico
.Nombre = unColega.Nombre
.Edad = unColega.Edad
.email = unColega.email
End With
'Aquí dará error:
Put #nFic, 2, unColegaDinamico
Close

End Sub
Ejecuta el programa y te encontrarás con un "bonito" error número: 59 Bad Record Length o La longitud de registro
es incorrecta, si usas la versión castellana del Visual Basic. Lo que nos quiere decir este error es que no puedes
grabar datos de una longitud diferente a la especificada a la hora de abrir el fichero.
Pero... ¿por qué? si, aparentemente, la longitud es la misma...
Pues eso, que sólo es en apariencia... si en la ventana "Inmediato" escribes esto, saldrás de dudas:
Print len(uncolega); len(uncolegadinamico)
El resultado será este:
82 10
En el primer caso, el del coleguilla normal, lalongitud es la esperada: 82
Pero en nuestro amigo dinámico, la longitud es 10, independientemente de que "en teoría" tenga almacenados
esos 82 caracteres del "estático". Lo que ocurre es que al ser dinámico, el contenido de las variables de caracteres
se almacenan en otro sitio y lo único que hay en esas variables es un "puntero" a la dirección de memoria en la
que están los datos almacenados... al menos ahora sabemos que una variable de cadena de caracteres tiene una
longitud de 4 bytes... creo que eso ya lo vimos en la entrega número dos o tres... dónde hablamos de lo que ocupa
cada variable... los enteros (Integer) ocupan 2 bytes, independientemente de cómo esté declarada la variable... lo
mismo ocurre con los otros datos numéricos.
Como te decía, si sabemos que la longitud del registro es de 82 caracteres, podemos definir una cadena de esa
longitud y usarla para acceder a los datos.
Probemos esto otro:
Dim unaCadena As String * 82

'...
unaCadena = "Prueba de una cadena"
Put #nFic, 2, unaCadena
'...
Ahora si que funcionará.
Pero si esa variable está definida como String normal, no funcionará...
Resumiendo: para acceder a los registros de un fichero abierto como Random, las variables usadas deben tener
definida la longitud igual que el tamaño especificado a la hora de abrirlo.
Un poco de complicación: Vamos a ver un ejemplo, "ilustrativo" más que práctico.
Ya he comentado que con una cadena de longitud fija podemos acceder a un registro. Ahora comprobarás lo
"cómodo" que es usar los tipos definidos. Pero este ejemplo lo pongo para que sepas cómo se almacenan los
datos numéricos en una cadena que se va a usar para guardar datos en un fichero de acceso aleatorio.
Hemos "comprobado" que un Integer se almacena en dos bytes... por tanto lo que se guarda en el disco es
precisamente eso: dos bytes. Cuando se usa un tipo definido, no te tienes que preocupar de cómo se almacena,
simplemente asignas el valor a la variable numérica y del resto se ocupa el VB.
Viendo el ejemplo anterior, es fácil almacenar un entero, si usamos un tipo definido, pero ¿cómo lo haremos si la
variable en la que se almacenará los datos es una cadena de longitud fija?
Para ello debemos "desglosar" los datos en distintas partes:
Para el Nombre usaremos los primeros 30 bytes de la cadena, para la edad los dos siguientes, es decir: bytes 31 y
32, para el email desde la posición 33 hasta la 82, osea desde la 33 con 50 bytes de longitud.
Si escribes esto:
Dim unaCadena As String * 82
Dim elNombre As String * 30
Dim laEdad As String * 2
Dim elEmail As String * 50

Dim nFic As Integer


Dim unaCadena As String * 82
Visual Basic 2 Año 2016 Omar Palermo 68
Show

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

elNombre = "Pepe de 23 años"


laEdad = 23
elEmail = "Pepe23@mundo.net"

unaCadena = elNombre & laEdad & elEmail


Put #nFic, 2, unaCadena

Close nFic
No conseguirás el resultado esperado... en lugar de 23 años, tendrá: ¡¡¡ 13106 !!! Dudo mucho que llegue a
conseguir esa edad... ni aún siendo un gnomo...
Si queremos guardar un número debemos convertirlo antes... el problema es que "NO HAY FUNCIONES DE
CONVERSIÓN"
Antes si que las había... cuando digo antes, me refiero al Basic del MS-DOS.
Para cada tipo de dato numérico existían un par de funciones, una para convertir el número en una cadena y el
otro para convertir la cadena en un número...
Y no me estoy refiriendo a las funciones que convierten los números en letras y las cadenas en números... aunque
pueda parecerte una contradicción... no es lo mismo convertir un número en una cadena normal que en una
cadena para almacenar en el disco como si de un número se tratase...
A saber, STR o CSTR convienten un número en una cadena, es decir que si el número es 23, lo convierten en "23"
en el caso de CSTR y en " 23" si se usa STR, fíjate en el espacio que hay antes del 2 en el segundo caso.
Pero cuando se debe convertir un número en una cadena de 2 bytes... la cosa cambia...
Y no te confundas... no pienses que si usas CSTR lo tienes solucionado... porque en el ejemplo este de "23" está
claro... pero y si el número es 1998? Tendriamos una cadena de 4 caracteres...
Como te decía, en los tiempos del MS-DOS, existían las funciones MKI$ y CVI para convertir los números enteros;
MKL$ y CVL para los enteros largos, MKS$ y CVS para los "singles" y MKD$ y CVD para los Double. No busques
estas funciones... porque no están...
Aunque podemos fabricarnoslas... pero, como no es plan de "romperse" el coco con una chorradilla de estas...
sólo vamos a usar el ejemplo de los números enteros.
¿Cómo se usarían?
En el ejemplo anterior hariamos esto:
elNombre = "Pepe de 23 años"
laEdad = Mki(23)
elEmail = "Pepe23@mundo.net"

unaCadena = elNombre & laEdad & elEmail


Put #nFic, 2, unaCadena
La función se haría así:
Private Function Mki(ByVal unInteger As Integer) As String
Dim sLoByte As String
Dim sHiByte As String
Dim iChar As Integer

iChar = unInteger \ 256


sHiByte = Chr$(iChar)
iChar = unInteger Mod 256
sLoByte = Chr$(iChar)
'Devolvemos el valor
Mki = sLoByte & sHiByte
End Function
Que complicación ¿verdad?
Pues como estas... muchas más... pero eso antes, en el MS-DOS... desde que hay "mogollón" de espacio de
almacenamiento... he perdido de vista muchas de estas cosas de "manejo" de números a "bajo nivel"...
Pero antes había que ahorrar cuantos bytes se pudieran...
La cuestión es que se divide el número en dos partes, la alta que se consigue dividiendo el entero entre 256 y la
baja, que es el resto que queda... después de haberlo dividido por 256... que de eso es de lo que se encarga el
MOD...
Visual Basic 2 Año 2016 Omar Palermo 69
Después se convierten esos valores en dos bytes usando el CHR$... se unen... y sin necesidad de agitarlo...
conseguimos el "batido" que queremos... por tanto, lo asignamos al valor que devolverá la función... (recuerda que
una función devuelve un valor cuando se asigna ese valor a su nombre...)
Bien... un lio... ¿verdad?
Pues ahora más lios...
¿Cómo se asigna esto al revés? Es decir: ¿Cómo se convierte una cadena de 2 bytes en un entero?
Con los tipos definidos no hay que hacer nada... ya sabes, que hace la conversión automáticamente.
Pero si usamos una cadena de longitud fija... ya es otro cantar...
Vamos a ver la declaración de la función "equivalente" al CVI del viejo Basic del MS-DOS:
Private Function Cvi(ByVal unaCadena As String) As Integer
Dim sLoByte As String
Dim sHiByte As String

sLoByte = Left$(unaCadena, 1)
sHiByte = Right$(unaCadena, 1)

'Devolvemos el valor
Cvi = Asc(sLoByte) + Asc(sHiByte) * 256
End Function
No voy a entrar en detalles, así que paso a un ejemplo "completo" para ver estos resultados.
Si vas a comprobar cómo "trabajan" alguna de estas funciones usando "puntos de interrupción", es decir que
quieres que el VB se detenga en una línea determinada, (para ello deberás posicionarte en la línea, que no debe
ser un comentario, y pulsar F9, al llegar a esa línea el Visual se detiene), deberás cambiar la propiedad
Autoredraw del form y ponerla a TRUE, de esta forma el contenido del Form (el que se imprime dentro del
Form_Load), se mantiene...
'Esto escribelo en el Form_Load
Dim nFic As Integer
Dim unaCadena As String * 82
Dim laEdad23 As Integer

Show
laEdad23 = 23

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

'Asignamos los datos


With unColega
.Nombre = "Pepe"
.Edad = 22
.email = "pepe22@mundo.net"
End With
Put #nFic, 1, unColega

Dim elNombre As String * 30


Dim laEdad As String * 2
Dim elEmail As String * 50

elNombre = "Pepe de 23 años"


laEdad = Mki(laEdad23)
elEmail = "Pepe23@mundo.net"

unaCadena = elNombre & laEdad & elEmail


Put #nFic, 2, unaCadena

Get #nFic, 1, unColega


With unColega
Print
Print "Colega 1:"
Print .Nombre
Print .Edad
Print .email
Visual Basic 2 Año 2016 Omar Palermo 70
End With

'Si se lee así, no hay problemas


Get #nFic, 2, unColega
With unColega
Print
Print "Colega 2:"
Print .Nombre
Print .Edad
Print .email
End With

'De esta manera está la cosa más complicada...


Get #nFic, 2, unaCadena
Print
Print "Colega 2:"
Print Mid$(unaCadena, 1, 30)
Print Cvi(Mid$(unaCadena, 31, 2))
Print Mid$(unaCadena, 33, 50)
Observa que aunque el segundo dato se haya guardado usando una cadena de longitud fija, (unaCadena), se
puede leer tanto con un tipo definido, si es de la misma longitud, claro, como con la cadena de longitud fija.
El problema es que tenemos que "desglosar" el contenido de esa cadena... Recuerda que te comenté que cada
uno de los "campos" del registro ocupa un espacio determinado... osea que tienen una longitud fija... por eso hay
que usar el Mid$, para tomar cada uno de los "trozos" de esa cadena.
El Mid$ funciona de la siguiente forma:
Mid$ (cadena, posición_de_inicio, numero_de_caracteres)
Lo que significa que devolverá el número de caracteres indicados por numero_de_caracteres empezando por la
posición: posición_de_inicio. En caso de que no se especifique el número de caracteres, devolverá toda la
cadena desde la posición indicada hasta el final de la misma.
Y ya que estamos con estas funciones... voy a explcarte las dos usadas en la función CVI:
Left$ y Right$
El Left$ toma de una cadena los caracteres que se le indiquen, empezando por la izquierda.
Right$ hace lo mismo, pero empieza a contar desde la derecha.
Unos ejemplos:
Left$("Hola Mundo", 4) devolverá "Hola", es decir los 4 primeros caracteres.
Right$("Hola Mundo", 4) delvolverá "undo", porque son los cuatro caracteres que hay a la derecha...
El Left$ se puede sustituir por Mid$ de la siguiente forma:
Mid$("Hola Mundo", 1, 4) es decir: empezando por el primer caracter, dame cuatro...
Sin embargo esto otro:
Mid$("Hola Mundo", 4) nos dará: "a Mundo", o lo que es lo mismo: desde la posición cuarta, todo lo que haya en
la cadena.
Y si quieres imprimir cada uno de los caracteres de una cadena, prueba esto:
Dim i As Integer
Dim unaCadena As String

unaCadena = "Hola Mundo"


For i = 1 To Len(unaCadena)
Print Mid$(unaCadena, i, 1)
Next
Lo que se hace aquí es un bucle para cada uno de los caracteres de la cadena, esto se sabe con LEN, que
devuelve la longitud de una cadena y con el Mid$, vamos tomando todos los caracteres de uno en uno. Ya que le
decimos que nos muestre el caracter que está en la posición i... sólo muestra uno, porque esa es la cantidad que
le indicamos que nos devuelva...
¡UF! Creo que algunas veces se me va la olla... y se me olvidan cosas que... creyendo que he explicado... las
uso...
En fin... la cuestión es que ahora sabes unas instrucciones nuevas...
Y ya que estamos...
Veamos cómo se puede usar también MID$, lo que ocurre es que esta "función" también es una "instrucción", al
igual que ocurría con INPUT, según se use delante o detrás del signo igual, se convierte en función o en
instrucción.
Ya sabes que una función devuelve un valor y se puede usar en una expresión, pero una instrucción "hace" algo y
no devuelve ningún valor ni se puede usar en una expresión.
En el ejemplo de guardar los registros, se usaron tres variables para almacenar los datos en el fichero:
Visual Basic 2 Año 2016 Omar Palermo 71
Dim elNombre As String * 30
Dim laEdad As String * 2
Dim elEmail As String * 50

elNombre = "Pepe de 23 años"


laEdad = Mki(laEdad23)
elEmail = "Pepe23@mundo.net"

unaCadena = elNombre & laEdad & elEmail


Put #nFic, 2, unaCadena
Pues esto mismo se puede hacer de esta otra forma:
Mid$(unaCadena, 1, 30) = "Prueba de una cadena"
Mid$(unaCadena, 31, 2) = Mki(laEdad23)
Mid$(unaCadena, 33, 50) = "pepe23@mundo.net"

Put #nFic, 2, unaCadena


Es decir, al igual que la función con el mismo nombre, la instrucción MID$ sustituye en "unaCadena" los caracteres
indicados por el inicio y la longitud por los que están después del signo igual...
Creo que me he pasado... bueno, esto al final viene bien... ya que así tenemos otra entrega "medio" preparada...
ya que de la que tenía prevista de 7 páginas "manuscritas" sólo he usado 2 y poco más...
Para completar esta "extraña" entrega... extraña por "no prevista"... vamos a "rematarla" con unos ejercicios, que
en este caso serán de manejo de cadenas con las funciones que acabamos de ver...
Los ejercicios:
1- Haz que se muestren los caracteres de una cadena al revés.
Por ejemplo, si la cadena es "Hola Mundo", se deberá imprimir: "odnuM aloH"
Que es útil si queremos hacer carteles para que se vean al derecho al mirar por el retrovisor del coche... (por decir
algo)
2- Usando esta misma cadena, es decir "Hola Mundo", dividela en las dos palabras.
Se supone que deberá servirte para cualquier frase, así que no hagas nada "fijo".
Lo que tienes que hacer es devolver en una cadena todos los caracteres hasta el primer espacio y el resto en otra
cadena.
Para esto, si quieres, puedes usar el Left$ y el Mid$... al menos para la primera palabra.
Soluciones de la entrega Dieciséis.

1- Haz que se muestren los caracteres de una cadena al revés.


Dim unaCadena As String
Dim i As Integer

unaCadena = "Hola Mundo"

Cls

For i = Len(unaCadena) To 1 Step -1


Print Mid$(unaCadena, i, 1);
Next
Print
Lo que se hace es un bucle "al revés", es decir: empezando por el último caracter... avanzando hasta el primero.
De eso se encarga el STEP -1

2- Usando esta misma cadena, es decir "Hola Mundo", dividela en las dos palabras.
'
Dim unaCadena As String
Dim i As Integer
Dim palabra1 As String
Dim palabra2 As String

unaCadena = "Hola Mundo"

Cls

For i = 1 To Len(unaCadena)
If Mid$(unaCadena, i, 1) = " " Then
palabra1 = Mid$(unaCadena, 1, i)
Visual Basic 2 Año 2016 Omar Palermo 72
'También así:
'palabra1 = Left$(unaCadena, i)

palabra2 = Mid$(unaCadena, i + 1)

'Realmente deberíamos abandonar el bucle


'Por la tremenda:
'Exit For
'Terminando el bucle:
'i = Len(unaCadena)
End If
Next
Print palabra1
Print palabra2
En este segundo ejemplo, si no abandonamos el bucle, bien usando EXIT FOR, (algunos me odiarán por esto),
bien asignando a la variable i el valor máximo que puede tener: Len(unaCadena); en el caso de que la variable
tenga más de un espacio, se asignará a palabra2 la última palabra y a palabra1 todo lo anterior. Si se abandona
el bucle, en ese mismo caso: palabra1 tendrá la primera palabra y palabra2 todo el resto.
Para comprobarlo, cambia la asignación por esta otra:
unaCadena = "Hola Mundo desastroso"
Y comprueba lo que te digo...

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.
Visual Basic 2 Año 2016 Omar Palermo 73
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 anteriomente... 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
"vacios" 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)
Visual Basic 2 Año 2016 Omar Palermo 74
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)
Visual Basic 2 Año 2016 Omar Palermo 75
'...
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 supesto, queremos que los registros que haya sigan conservando los datos que tuviesen... sino, ¿que clase de
rutina conversora sería?
Acuerdate 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 oido 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í:
falta
Soluciones de la entrega Dieciocho.

Vamos a ver las soluciones a los ejercicios de la entrega dieciocho:


El primero era crear una utilidad para convertir un fichero de un tipo a otro. Una solución sería esta:
Private Type t_Colega
Nombre As String * 30
Edad As Integer
email As String * 50
End Type

Private Type t_Colega2


Nombre As String * 30
Edad As Integer
email As String * 50
URL As String * 128
End Type
Visual Basic 2 Año 2016 Omar Palermo 76

Private Sub cmdConvertir_Click()


Dim unColega As t_Colega, unColega2 As t_Colega2
Dim nFic As Long, nFic2 As Long
Dim numColegas As Long
Dim i As Long

'abrir el fichero original


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

'abrir el fichero de destino


nFic2 = FreeFile
Open "colegas2.dat" For Random As nFic2 Len = Len(unColega2)

numColegas = LOF(nFic) \ Len(unColega)

For i = 1 To numColegas
'leer el registro
Get #nFic, i, unColega
'Asignar los nombres del nuevo tipo
With unColega
unColega2.Nombre = .Nombre
unColega2.Edad = .Edad
unColega2.email = .email
unColega2.URL = ""
End With
'Guardar el nuevo registro
Put #nFic2, i, unColega2
Next

Close nFic2
Close nFic

'Si quieres eliminar el fichero anterior y cambiarle el nombre


'hazlo después de cerrar los ficheros

End Sub
Este es el listado completo del segundo ejercicio:
'------------------------------------------------------------------
'Ejercicio de la entrega 18 (26/Abr/98)
'------------------------------------------------------------------
Option Explicit
'Tipo para usar en el fichero
Private Type t_Colega
Nombre As String * 30
Edad As Integer
email As String * 50
End Type
'Esta variable se usará para acceder a los datos
Dim m_unColega As t_Colega
'Número de registros del fichero
Dim m_numColegas As Long
'Número del colega actual, usado cuando se edita, etc.
Dim m_elColega As Long
'Esta variable guardará el fichero a usar
Dim m_sFicColegas As String
'Esta se usará como FLAG para saber si hemos cambiado
'el registro actual
Dim m_Modificado As Boolean

Private Sub cmdGuardar_Click()


Dim nFic As Long
Visual Basic 2 Año 2016 Omar Palermo 77
'Sólo si el número del colega es el indicado en Text4
'de esta forma sólo se guardará cuando se pulse en
'Nuevo o en Leer
If m_elColega = Val(Text4) Then
nFic = FreeFile
Open m_sFicColegas For Random As nFic Len = Len(m_unColega)
With m_unColega
.Nombre = Text1
.Edad = Val(Text2)
.email = Text3
End With
'Guardar los datos en el disco
Put #nFic, m_elColega, m_unColega
Close nFic
'Ajustar el número de colegas
m_numColegas = CuantosColegas()
m_Modificado = False
'Posicionar el cursor en el número de registro
Text4.SetFocus
End If
End Sub
Private Sub cmdLeer_Click()
Dim nFic As Long
'No se comprueba si se ha modificado el registro actual
'esto habría que tenerlo en cuenta... lo he dejado preparado
'con la variable m_Modificado
'Te dejo que hagas las comparaciones pertinentes...
'...
'Sólo leer si no se está añadiendo uno nuevo
If m_elColega <= m_numColegas Then
m_elColega = Val(Text4)
'Pero que no se lea un valor "no válido"
If m_elColega > 0 And m_elColega <= m_numColegas Then
nFic = FreeFile
Open m_sFicColegas For Random As nFic Len = Len(m_unColega)
'leer ese registro
Get #nFic, m_elColega, m_unColega
'quitarle los espacios "extras", ya que al ser
'de longitud fija, los espacios en blanco también
'se mostrarán en la caja de texto
'Para comprobarlo, quita el Trim$ y verás lo que
'ocurre cuando el nombre tiene menos caracteres...
With m_unColega
Text1 = Trim$(.Nombre)
Text2 = .Edad
Text3 = Trim$(.email)
End With
Close nFic
m_Modificado = False
Text1.SetFocus
Else
'si el número no es válido...
Text4.SetFocus
m_elColega = 0
End If
End If
End Sub
Private Sub cmdNuevo_Click()
'¿Comprobar si se ha modificado?
'...
'Añadir un nuevo colega,
'sólo si no se está introduciendo uno nuevo
If m_elColega <> m_numColegas + 1 Then
m_elColega = m_numColegas + 1
Visual Basic 2 Año 2016 Omar Palermo 78
Text4 = m_elColega
'Limpiar el contenido de las cajas de texto
Text1 = ""
Text2 = ""
Text3 = ""
'Limpiar también la variable el registro actual,
'aunque realmente no es necesario...
With m_unColega
.Nombre = ""
.Edad = 0
.email = ""
End With
m_Modificado = False
'Posicionar el cursor en el campo del nombre
Text1.SetFocus
End If
End Sub
Private Sub Form_Load()
'asignamos el path del fichero de colegas:
m_sFicColegas = App.Path & "\Colegas.dat"
'Esta asignación fallará si el path es el directorio raiz
'por tanto se debería comprobar de esta forma:
If Right$(App.Path, 1) = "\" Then
m_sFicColegas = App.Path & "Colegas.dat"
Else
m_sFicColegas = App.Path & "\Colegas.dat"
End If
'También de esta otra forma... algo menos "clara"
m_sFicColegas = App.Path & _
IIf(Right$(App.Path, 1) = "\", "", "\") & _
"Colegas.dat"
'Inicialmente leer el número de registros
'lo pongo en una función para usarlo cuando se necesite,
'sin tener que repetir el proceso, aunque corto, pero...
m_numColegas = CuantosColegas()
'Borrar el contenido de los TextBox
Text1 = ""
Text2 = ""
Text3 = ""
Text4 = ""
'Para empezar no se ha modificado
m_Modificado = False
End Sub
Private Function CuantosColegas() As Long
'Esta función se encarga de informarnos del número de registros
'que tiene el fichero
'Usarlo sólo cuando queremos saber esta información y
'no necesitamos mantener el fichero abierto

'si no existe el fichero, se producirá un error


On Local Error Resume Next

CuantosColegas = FileLen(m_sFicColegas) \ Len(m_unColega)


If Err Then
CuantosColegas = 0
End If
Label1(4) = "Número de colegas:" & CuantosColegas
Err = 0
End Function
Private Sub Form_Unload(Cancel As Integer)
'Por si se quedó o estaba el fichero abierto...
Close

Set Form1 = Nothing


Visual Basic 2 Año 2016 Omar Palermo 79
End Sub
Private Sub Text1_Change()
'Si en lugar de usar tres TextBox distintos se usara un array
'sería más cómodo, ya que sólo se pondrá esta asignación
'en un sólo evento Change.
'
m_Modificado = True
End Sub
Private Sub Text2_Change()
m_Modificado = True
End Sub
Private Sub Text3_Change()
m_Modificado = True
End Sub

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 idicamos 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 quisieramos 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 podiamos 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 leidos 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 conbertir 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:
Visual Basic 2 Año 2016 Omar Palermo 80
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
Visual Basic 2 Año 2016 Omar Palermo 81
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 leido 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
Visual Basic 2 Año 2016 Omar Palermo 82
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...
Visual Basic 2 Año 2016 Omar Palermo 83
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 leido un código de fin de fichero, precisamente antes de que se acbe 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 conociamos 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
Visual Basic 2 Año 2016 Omar Palermo 84
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:

falta
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.
Soluciones de la entrega Diecinueve.
'------------------------------------------------------------------
'Ejercicio para la entrega 19 (24/Jun/98)
'------------------------------------------------------------------
Option Explicit

Private Sub Form_Load()


Dim i As Integer

'Para probar uso el fichero de colegas.dat 'el tamaño de cada campo era: 30, 2, 50
'Private Type t_Colega
' Nombre As String * 30
' Edad As Integer
' email As String * 50
'End Type

txtOrigen = "colegas.dat"
'Crear los controles de destino '(empezamos por UNO porque el control CERO ya está creado)
Visual Basic 2 Año 2016 Omar Palermo 85
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 & ":"
'Ajustar el TabIndex, (se supone que ya estaban por orden)
lblDest(i).TabIndex = txtDestTam(i - 1).TabIndex + 1
.TabIndex = lblDest(i).TabIndex + 1
End With
Next 'Borrar el contenido de los TextBoxes
For i = 0 To 9
txtTam(i).Text = ""
txtDestTam(i).Text = ""
Next
End Sub

Private Sub cmdConvertir_Click() 'Variables para los nombres y números de ficheros


Dim nFic As Long, nFic2 As Long
Dim sFic As String, sFic2 As String 'Estos arrays controlarán los tamaños de cada
campo
Dim aOrigen() As Long
Dim aDestino() As Long 'Número de campos en cada fichero
Dim nOrigen As Integer
Dim nDestino As Integer 'Tamaños de los registros
Dim tOrigen As Integer
Dim tDestino As Integer 'Las cadenas que contendrán los datos
Dim sOrigen As String
Dim sDestino As String 'Número de registros del fichero de origen
Dim numReg As Integer
Dim tamFic As Long 'Para usos generales
Dim i As Long, j As Long
Dim posReg As Long
Dim sTmp As String 'Antes de hacer nada, comprobamos que exista el fichero de origen
sFic = Trim$(txtOrigen)
If Len(Dir$(sFic)) = 0 Then
MsgBox "¡ATENCIÓN! No existe el fichero de origen."
txtOrigen.SetFocus
Exit Sub
End If 'Asignamos el nombre del fichero de destino
sFic2 = Trim$(txtDestino)

'Se asignarán los tamaños de cada registro, se dejará 'de comprobar cuando el contenido del
textbox sea cero.
'Si se usara un TextBox con el número de campos, la cosa 'sería más fácil de controlar, pero... '
'Empezamos por el origen
For i = 0 To 9
If Val(txtTam(i)) = 0 Then 'ya no hay nada más que comprobar
Exit For
Else
nOrigen = nOrigen + 1
ReDim Preserve aOrigen(nOrigen) 'asignamos el tamaño del campo nOrigen
aOrigen(nOrigen) = Val(txtTam(i)) 'ajustamos el tamaño total del registro
tOrigen = tOrigen + aOrigen(nOrigen)
End If
Next 'Ahora comprobamos el destino
For i = 0 To 9
If Val(txtDestTam(i)) = 0 Then 'ya no hay nada más que comprobar
Exit For
Visual Basic 2 Año 2016 Omar Palermo 86
Else
nDestino = nDestino + 1
ReDim Preserve aDestino(nDestino) 'asignamos el tamaño del campo nDestino
aDestino(nDestino) = Val(txtDestTam(i)) 'ajustamos el tamaño total del
registro
tDestino = tDestino + aDestino(nDestino)
End If
Next
'Ya tenemos la información suficiente, 'Por si da error al acceder a los ficheros
On Local Error GoTo ErrorConvertir 'Abrimos los ficheros en modo binario
nFic = FreeFile
Open sFic For Binary As nFic 'Averiguar el número de registros de este fichero
tamFic = LOF(nFic)
numReg = tamFic \ tOrigen
'Comprobar que el tamaño especificado concuerda con el fichero 'Si el número de registros
multiplicado por el tamaño de cada registro es diferente al tamaño del fichero...
If numReg * tOrigen <> tamFic Then
MsgBox "Los tamaños especificados en los campos de origen" & vbCrLf & _
"no concuerdan con el tamaño del fichero.", vbCritical, "Convertir
ficheros"
Close
txtTam(0).SetFocus
Exit Sub
End If 'Abrimos el fichero de destino
nFic2 = FreeFile
Open sFic2 For Binary As nFic2 'Preparamos la cadena que contendrá los datos de origen
'esta no cambiará de tamaño
sOrigen = Space$(tOrigen) 'Hacemos un bucle para todos los registros de origen
For j = 1 To numReg
Get nFic, , sOrigen
'La cadena de destino se formará con el tamaño de los campos de origen más el tamaño de los
nuevos campos, 'si el número de campos de destino es diferente, 'simplemente se rellenará la
cadena con espacios
sDestino = ""
'Esta variable contendrá la posición dentro del registro 'del campo que se esté procesando
posReg = 1
For i = 1 To nOrigen 'Tomamos el contenido del campo actual
sTmp = Mid$(sOrigen,posReg,aOrigen(i)) 'Asigno este campo y lo rellenamos de
espacios
sTmp = Left$(sTmp & Space$(aDestino(i)), aDestino(i))
sDestino = sDestino & sTmp 'ajusto el tamaño de la posición dentro del registro de
origen
posReg = posReg + aOrigen(i)
Next
'Ahora hay que rellenar la cadena de destino con espacios 'suficientes hasta completar el
número de caracteres 'que se han especificado.
''El TRUCO está en añadirle a la cadena de destino la 'cantidad de caracteres totales y sólo
quedarnos
'con esa cantidad, de esta forma nos aseguramos que tendremos la cantidad que necesitamos
tener...
sDestino = Left$(sDestino & Space$(tDestino), tDestino) 'Lo guardamos
Put nFic2, , sDestino
Next 'Se acabó de convertir, cerramos los ficheros
Close
'Guardamos la información de los formatos usados: ''Uso un formato standard INI para que se
pueda leer de forma 'fácil, incluso usando el ejemplo de la entrega 20

nFic = FreeFile
Open "Convertir.ini" For Output As nFic 'Datos de origen:
Print #nFic, "[Datos de Origen]"
Print #nFic, "Fichero=" & sFic
Print #nFic, "Número de campos=" & nOrigen
Visual Basic 2 Año 2016 Omar Palermo 87
For i = 1 To nOrigen
Print #nFic, "Tamaño Campo" & CStr(i) & "=" & aOrigen(i)
Next
Print #nFic, "" 'Datos de destino:
Print #nFic, "[Datos de Destino]"
Print #nFic, "Fichero=" & sFic2
Print #nFic, "Número de campos=" & nDestino
For i = 1 To nDestino
Print #nFic, "Tamaño Campo" & CStr(i) & "=" & aDestino(i)
Next
Close 'Avisamos de que todo acabó bien
MsgBox "Se ha convertido el fichero de forma satisfactoria," & vbCrLf & _
"La información de los datos convertidos está en: Convertir.ini", _
vbInformation, "Convertir ficheros."
SalirConvertir:
Close
Exit Sub
ErrorConvertir:
MsgBox "Se ha producido el siguiente error:" & vbCrLf & _
Err.Number & " " & Err.Description, vbCritical, "Convertir ficheros"
Resume SalirConvertir
End Sub
El contenido del fichero "Convertir.ini" de la prueba que he hecho, sería el siguiente:
[Datos de Origen]
Fichero=colegas.dat
Número de campos=3
Tamaño Campo1=30
Tamaño Campo2=2
Tamaño Campo3=50

[Datos de Destino]
Fichero=colegas2.dat
Número de campos=4
Tamaño Campo1=40
Tamaño Campo2=2
Tamaño Campo3=50
Tamaño Campo4=128

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á...
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.
Visual Basic 2 Año 2016 Omar Palermo 88
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:
falta
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
Visual Basic 2 Año 2016 Omar Palermo 89
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
Visual Basic 2 Año 2016 Omar Palermo 90
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
Visual Basic 2 Año 2016 Omar Palermo 91
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.
Soluciones de la entrega Veinte.

El primero:
'Ejemplos del curso básico, ejemplo de Seek (26/May/98) Solución a los ejercicios de la entrega
20
Option Explicit

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
Private nSecciones As Integer

Private sFic As String


Private Sub cmdLeerContenido_Click() 'Leemos el contenido de la sección seleccionada en el
list
Dim sCadena As String
Dim nItem As Long
Dim i As Integer
nItem = List1.ListIndex
If nItem >= 0 Then 'borramos el contenido del Text1
Text1 = ""
With aSecciones(nItem + 1)
For i = 1 To .NumClaves
sCadena = .Contenidos(i).Clave & "=" & .Contenidos(i).Contenido
Text1 = Text1 & sCadena & vbCrLf
Next
End With
End If
End Sub

Private Sub cmdLeerSecciones_Click()


Dim nFic As Integer
Visual Basic 2 Año 2016 Omar Palermo 92
Dim sCadena As String
Dim Posicion As Long
Dim nClaves As Integer
Dim i As Integer '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 'Si es una sección
If Left$(sCadena, 1) = "[" Then
nClaves = 0 'incrementamos el número de secciones
nSecciones = nSecciones + 1 'redimensionamos el array
ReDim Preserve aSecciones(nSecciones) 'asignamos los valores al array
aSecciones(nSecciones).Nombre = sCadena 'añadimos esta sección a la lista
List1.AddItem sCadena 'ahora leemos el contenido del fichero hasta encontrar
[
Do While Not EOF(nFic)
Line Input #nFic, sCadena
If Left$(sCadena, 1) = "[" Then 'nada más que leer restablecemos la
posición anterior
Seek nFic, Posicion
Exit Do
Else
Posicion = Seek(nFic) 'Posición del signo igual
i = InStr(sCadena, "=")
If i Then
nClaves = nClaves + 1
ReDim Preserve aSecciones(nSecciones).Contenidos(nClaves)
With aSecciones(nSecciones)
.NumClaves = nClaves 'La clave estará antes del signo
igual
.Contenidos(nClaves).Clave = Trim$(Left$(sCadena, i - 1))
'el contenido de la clave después del signo
.Contenidos(nClaves).Contenido = Mid$(sCadena, i + 1)
End With
End If
End If
Loop
End If
Loop
Close nFic

If nSecciones Then '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 Form_Load()


Dim nFic As Integer '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, ""
Visual Basic 2 Año 2016 Omar Palermo 93
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"
Close
cmdLeerSecciones_Click
End Sub

Private Sub List1_DblClick()


cmdLeerContenido_Click
End Sub
El segundo:
'Ejemplos del curso básico, ejemplo de Seek (26/May/98) Solución a los ejercicios de la entrega
20'
Option Explicit

Private Type tSecciones


Nombre As String
NumClaves As Integer
Contenidos() As Long
End Type
Private aSecciones() As tSecciones
Private nSecciones As Integer

Private sFic As String

Private Sub cmdLeerContenido_Click() 'Leemos el contenido de la sección seleccionada en


el list
Dim nFic As Integer
Dim sCadena As String
Dim nItem As Long
Dim i As Integer

nItem = List1.ListIndex
If nItem >= 0 Then
nFic = FreeFile
Open sFic For Input As nFic 'borramos el contenido del Text1
Text1 = ""
With aSecciones(nItem + 1)
For i = 1 To .NumClaves 'Nos posicionamos en el sitio que nos
interesa
Seek nFic, aSecciones(nItem + 1).Contenidos(i)
Line Input #nFic, sCadena
Text1 = Text1 & sCadena & vbCrLf
Next
End With
Close nFic
End If
End Sub

Private Sub cmdLeerSecciones_Click()


Dim nFic As Integer
Dim sCadena As String
Dim Posicion As Long
Dim nClaves As Integer
Dim i As Integer 'Borramos el contenido del ListBox
List1.Clear 'Leemos las secciones disponibles
nFic = FreeFile
Visual Basic 2 Año 2016 Omar Palermo 94
Open sFic For Input As nFic
Do While Not EOF(nFic)
Line Input #nFic, sCadena 'Si es una sección
If Left$(sCadena, 1) = "[" Then
nClaves = 0 'incrementamos el número de secciones
nSecciones = nSecciones + 1 'redimensionamos el array
ReDim Preserve aSecciones(nSecciones) 'asignamos los valores al array
aSecciones(nSecciones).Nombre = sCadena 'añadimos esta sección a la lista
List1.AddItem sCadena 'ahora leemos el contenido del fichero hasta encontrar [
Do While Not EOF(nFic) 'En las claves nos interesa saber la posición
'antes de empezar a leerlas
Posicion = Seek(nFic)
Line Input #nFic, sCadena
If Left$(sCadena, 1) = "[" Then 'nada más que leer
'restablecemos la posición
anterior
Seek nFic, Posicion
Exit Do
Else
i = InStr(sCadena, "=") 'Si es una clave tendrá el signo igual
If i Then
nClaves = nClaves + 1
ReDim Preserve aSecciones(nSecciones).Contenidos(nClaves)
With aSecciones(nSecciones)
.NumClaves = nClaves
.Contenidos(nClaves) = Posicion
End With
End If
End If
Loop
End If
Loop
Close nFic

If nSecciones Then '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 Form_Load()


Dim nFic As Integer '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]"
Visual Basic 2 Año 2016 Omar Palermo 95
Print #nFic, "Fichero creado el día=26/May/1998"
Close
cmdLeerSecciones_Click
End Sub
Private Sub List1_DblClick()
cmdLeerContenido_Click
End Sub

Potrebbero piacerti anche