Las pilas en Small Basic

Abril 30, 2009

Bienvenidos nuevamente a otra entrega sobre el lenguaje Small Basic, en esta ocasión hablaremos sobre la librería Stack, cuya misión es ofrecernos la opción de manejar pilas en nuestros programas. Una pila, para el que no lo sepa, es una estructura de datos similar a una lista de valores, la diferencia radica en que solo podemos meter nuevos elementos por el final de esta y al sacarlos solo podemos sacar el último. Al principio uno puede no ver la utilidad de las pilas, pero con el paso del tiempo al final se hacen indispensables y es de agradecer que estén implementadas en Small Basic. Introducido el concepto, pasemos a indicar cuales son las operaciones que soporta la librería Stack:

  • GetCount(nombre): Devuelve el número de elementos almacenados en una pila.
  • PopValue(nombre): Saca y devuelve un elemento de una pila.
  • PushValue(nombre, valor): Introduce un elemento en una pila.

Nuevamente como ya indicamos en los arrays, hay que tener en cuenta que el nombre que identifica a la pila será convertido a una cadena, con lo que ello implica. Para verlo mejor aquí tenemos un ejemplo de lo que acabo de decir.

Stack.PushValue(0, "42")
aux = Stack.PopValue("0")
TextWindow.WriteLine(aux)

En este otro ejemplo se puede ver el funcionamiento de todas las operaciones de la librería. Lo que hacemos es calcular las potencias de dos con la librería Math, de la que hablaremos próximamente, y mostramos los números a medida que los vamos sacando para ver como parece que vamos hacia atrás.

'----------------------------------------------------
' Prueba con pilas y las potencias de 2
'----------------------------------------------------
TextWindow.Title = "Prueba con pilas"

endl = Text.GetCharacter(13)        '\n
endl = endl + Text.GetCharacter(10) '\r

TextWindow.Write("Los metemos: ")
For i = 0 To 10
  valor = Math.Power(2, i)
  Stack.PushValue("PotenciasDeDos", valor)
  TextWindow.Write(valor + " ")
EndFor
TextWindow.WriteLine(endl)

aux = "Hay " + Stack.GetCount("PotenciasDeDos")
aux = aux + " valores en la pila." + endl
TextWindow.WriteLine(aux)

TextWindow.Write("Y los sacamos: ")
For i = 0 To 10
  valor = Stack.PopValue("PotenciasDeDos")
  TextWindow.Write(valor + " ")
EndFor

TextWindow.WriteLine(endl)

Y por último el golpe maestro y definitivo para el lenguaje Small Basic, que a pesar de sus múltiples limitaciones ello no nos impide para que podamos realizar algoritmos recursivos (subrutinas que se llaman a si mismas sin que ello implique un bucle infinito). Para ello vamos a poner de ejemplo la función factorial:

'----------------------------------------------------
' Prueba del cálculo del factorial con pilas
'----------------------------------------------------
TextWindow.Title = "Cálculo del factorial con pilas"
For i = 0 To 28
  Array.SetValue("MostrarFactorial", "n", i)
  MostrarFactorial()
EndFor

'----------------------------------------------------
' MostrarFactorial(n)
Sub MostrarFactorial
  ' Obtenemos el parametro "n".
  n = Array.GetValue("MostrarFactorial", "n")
  ' Salvamos el parametro antes de la llamada.
  Stack.PushValue("MostrarFactorial", n)
  ' Pasamos el parametro a la función.
  Array.SetValue("Factorial", "n", n)
  ' Invocamos a la función.
  Factorial()
  ' Recuperamos el parametro tras la llamada.
  n = Stack.PopValue("MostrarFactorial")
  ' Mostramos el resultado por pantalla.
  aux = "Factorial(" + n + ") = " + r
  TextWindow.WriteLine(aux)
EndSub

'----------------------------------------------------
' Factorial(n) = x * Factorial(n - 1)
Sub Factorial
  ' Obtenemos el parametro "n".
  n = Array.GetValue("Factorial", "n")
  ' Comprobamos que "n" esté en unos límites.
  If n > 27 Then ' Error por desbordamiento.
    r = 0 ' Devolvemos 0.
  ElseIf n > 1 Then ' Caso recursivo.
    ' Salvamos el parametro "n".
    Stack.PushValue("Factorial", n)
    ' Pasamos el parametro a la función.
    Array.SetValue("Factorial", "n", n - 1)
    ' Invocamos a la función.
    Factorial()
    ' Recuperamos el parametro "n".
    n = Stack.PopValue("Factorial")
    ' Calculamos el resultado a devolver.
    r = r * n
  Else ' Caso base.
    r = 1 ' Devolvemos 1.
  EndIf
EndSub

Como podemos ver Small Basic a pesar de sus limitaciones aparentes, nos permite cierta flexibilidad como realizar subrutinas recursivas. El problema es que nos podemos volver un poco locos a la hora de asignar valores a las variables, porque un solo descuido y el programa se pone a hacer cosas muy raras. Pero ahí está la opción para poder usarla. Como veis las pilas pueden ser bastante útiles si uno se lo propone.

Y con esto ya hemos terminado la sección de la API de Small Basic dedicada a las estructuras de datos. En el próximo artículo posiblemente explique un poco más el tema de cuando una cadena es convertida a número, que yo creo que es un tema bastante peliagudo y confuso. Luego seguiré hablando de las librerías del sistema que faltan (Math, File y Sound), antes de ponerme a hablar de las librerías que utilizan la ventana de windows normal. Mientras tanto ánimo y que paséis un buen puente.


Los arrays en Small Basic

Abril 30, 2009

Y ahora algo muy interesante, los arrays, que están dentro de la librería Array, quién lo hubiera sospechado, ¿eh? Como ya hemos visto las variables en Small Basic solo almacenan números y cadenas (y estas últimas al almacenarlas con la asignación en las variables o al usar el operador +, se comprueba primero si se pueden convertir a un número), pero en una aplicación, o mismamente en un juego, vamos a necesitar muchas veces formas de almacenar datos en tablas para tenerlos organizados de alguna forma. Para ello existe la librería Array, que contiene las siguientes operaciones:

  • ContainsIndex(variable, índice): Comprueba si existe un índice en un array, algo que puede ser útil para determinar si hemos inicializado o no una zona del array con un valor o no. Si el índice tiene asignado un valor devolverá “True”, sino ha sido inicializado o ha sido borrado, devolverá “False”.
  • ContainsValue(variable, valor): Comprueba si existe un determinado valor en alguno de los índices de un array. Si existe el valor en el array devolverá “True”, sino devolverá “False”.
  • GetItemCount(variable): Devuelve el número de elementos almacenados en un array.
  • Deprecated: GetValue(nombre, índice): Devuelve un valor almacenado en un índice de un array. Si no existe el índice lo que devuelve es una cadena vacía.
  • Deprecated: SetValue(nombre, índice, valor): Inicializa o cambia el valor almacenado en un índice de un array.

(Nota: Deprecated significa que no se debe usar ya esa operación por quedarse anticuada o porque ya no se le da soporte. En este caso el lenguaje ha avanzado de versión y ya no se recomienda usar esas funciones.)

Hay que tener una cosa muy presente a la hora de manejar arrays y que es muy importante. Tanto el nombre, como el índice que le pasemos a estas operaciones, serán convertidos a cadenas internamente en caso de pasarle un número. Es decir que yo puedo hacer lo siguiente:

Array.SetValue(0, 0, 101)
aux = Array.GetValue("0", "0")
TextWindow.WriteLine(aux)

Ahí lo que hacemos es crear un array cuyo nombre es el número cero y en la posición cero asignar el valor 101. Sin embargo internamente coge los valores numéricos que le hayamos pasado para el nombre y el índice, y los transforma en cadenas, por lo que en realidad hemos creado un array cuyo nombre es la cadena “0″ y hemos asignado el valor 101 en el índice “0″. Puede resultar un poco lioso al principio, pero todo es acostumbrarse, como con los punteros en C/C++.

El caso es que lo mejor para descubrir como funciona algo es realizar toda clase de experimentos con ello y probar todas las posibles combinaciones que se nos ocurran para ver qué es lo que funciona y que no funciona. Pero antes de ponerse a lo loco, aquí tenéis un ejemplo ilustrativo de como utilizar los arrays:

'----------------------------------------------------
' Prueba con arrays
'----------------------------------------------------
TextWindow.Title = "Prueba con arrays"

endl = Text.GetCharacter(13)        '\n
endl = endl + Text.GetCharacter(10) '\r

'----------------------------------------------------
' Meter valores en un array
TextWindow.WriteLine("1) Metemos valores:")
TextWindow.Write(endl)

Teléfonos["Pepe"] = "555-1234"
Teléfonos["Juan"] = "555-1243"
Teléfonos["Jose"] = "555-1423"

Edad["Pepe"] = 37
Edad["Juan"] = 42
Edad["Jose"] = 101

Números["0"] = "Zero"
Números["1"] = "Ichi"
Números["2"] = "Ni"
Números["3"] = "San"
Números["4"] = "Shi"
Números["5"] = "Go"
Números["6"] = "Roku"
Números["7"] = "Nana"
Números["8"] = "Shichi"
Números["9"] = "Kyu"
Números["10"] = "Jû"

Array.SetValue("Binario", "00", "000")
Array.SetValue("Binario", "01", "001")
Array.SetValue("Binario", "02", "010")
Array.SetValue("Binario", "03", "011")
Array.SetValue("Binario", "04", "100")
Array.SetValue("Binario", "05", "101")

'----------------------------------------------------
' Obtener valores de un array
TextWindow.WriteLine("2) Mostramos los valores:")
TextWindow.Write(endl)

Sub MostrarPersona
 aux = nombre + ": "
 aux = aux + Edad[nombre]
 aux = aux + ", "
 aux = aux + Teléfonos[nombre]
 TextWindow.WriteLine(aux)
EndSub

TextWindow.WriteLine("Listado de datos:")

nombre = "Pepe"
MostrarPersona()

nombre = "Juan"
MostrarPersona()

nombre = "Jose"
MostrarPersona()

TextWindow.Write(endl)

TextWindow.WriteLine("Los números en japonés:")
For i = 0 To 10
 TextWindow.Write(Números[i] + " ")
EndFor
TextWindow.Write(endl)

PauseAndClear()

'----------------------------------------------------
' Problemas al obtener valores de un array
TextWindow.WriteLine("3) Problemas al mostrar:")

TextWindow.WriteLine("Los números en binario:")
For i = 0 To 5
 aux = Array.GetValue("Binario", i)
 TextWindow.Write(aux + " ")
EndFor
TextWindow.Write(endl)
TextWindow.Write("Pero no ha salido nada, snif... ")
TextWindow.Write("porque los índices no son los ")
TextWindow.WriteLine("correctos." + endl)

TextWindow.WriteLine("Los números en binario:")
For i = 0 To 5
 i = Text.Append("0", i)
 aux = Array.GetValue("binario", i)
 TextWindow.Write(aux + " ")
EndFor
TextWindow.Write(endl)
TextWindow.Write("Pero no ha salido nada, snif... ")
TextWindow.Write("porque el nombre del array es ")
TextWindow.WriteLine("incorrecto." + endl)

TextWindow.WriteLine("Los números en binario:")
For i = 0 To 5
 i = Text.Append("0", i)
 aux = Array.GetValue("Binario", i)
 TextWindow.Write(aux + " ")
EndFor
TextWindow.Write(endl)
TextWindow.WriteLine("Ahora sí sale todo bien ^_^")

PauseAndClear()

'----------------------------------------------------
' Obtener información de un array
TextWindow.WriteLine("4) Informaciones varias:")
TextWindow.Write(endl)

Sub ExisteÍndice
 existe = Array.ContainsIndex(variable, índice)
 aux = "¿Existe en " + nombre + " el índice "
 aux = aux + índice + "? "
 TextWindow.Write(aux)
 MostrarExiste()
 TextWindow.Write(endl)
EndSub

nombre = "Números"
variable = Números
índice = 5
ExisteÍndice()

nombre = "Números"
variable = Números
índice = "8"
ExisteÍndice()

nombre = "Números"
variable = Números
índice = 14
ExisteÍndice()

TextWindow.Write(endl)

Sub ContieneValor
 existe = Array.ContainsValue(variable, valor)
 aux = "¿Existe en " + nombre + " el valor "
 aux = aux + valor + "? "
 TextWindow.Write(aux)
 MostrarExiste()
 TextWindow.Write(endl)
EndSub

nombre = "Edad"
variable = Edad
valor = 42
ContieneValor()

nombre = "Edad"
variable = Edad
valor = 64
ContieneValor()

nombre = "Edad"
variable = Edad
valor = "101"
ContieneValor()

TextWindow.Write(endl)

Sub MostrarTamaño
 tamaño = Array.GetItemCount(variable)
 aux = "El array " + nombre + " tiene "
 aux = aux + tamaño + " elementos."
 TextWindow.WriteLine(aux)
EndSub

nombre = "Teléfonos"
variable = Teléfonos
MostrarTamaño()

nombre = "Números"
variable = Números
MostrarTamaño()

PauseAndClear()

'----------------------------------------------------
' Borrar valores en un array
TextWindow.WriteLine("5) Borramos valores:")

Sub MostrarTrasBorrar
 aux = "Borrando el índice " + índice
 TextWindow.WriteLine(aux)
 variable[índice] = ""
 MostrarTamaño()
EndSub

nombre = "Números"
variable = Números
MostrarTamaño()

índice = "1"
MostrarTrasBorrar()
índice = "3"
MostrarTrasBorrar()
índice = "5"
MostrarTrasBorrar()
índice = "7"
MostrarTrasBorrar()
índice = "9"
MostrarTrasBorrar()
índice = "11"
MostrarTrasBorrar()
índice = "02"
MostrarTrasBorrar()

TextWindow.Write(endl)
TextWindow.WriteLine("Los números pares en japonés:")
For i = 0 To 10 Step 2
 TextWindow.Write(Números[i] + " ")
EndFor
TextWindow.WriteLine(endl)

'----------------------------------------------------
' Subrutinas de apoyo
Sub MostrarExiste
 colorAux = TextWindow.ForegroundColor
 If existe Then
 TextWindow.ForegroundColor = "Green"
 TextWindow.Write("Verdadero")
 Else
 TextWindow.ForegroundColor = "Red"
 TextWindow.Write("Falso")
 EndIf
 TextWindow.ForegroundColor = colorAux
EndSub

Sub PauseAndClear
 TextWindow.Write(endl)
 TextWindow.Pause()
 TextWindow.Clear()
EndSub

En el próximo artículo hablaremos sobre las pilas y su uso en un lenguaje como Small Basic, también es posible que después de hablar de las pilas haga algunas anotaciones sobre el tema de las cadenas y su conversión a número o no, dependiendo de diversos factores.


Manipulando cadenas con Small Basic

Abril 30, 2009

Y lo prometido es deuda, así que en esta nueva entrega sobre el lenguaje Small Basic voy a hablar sobre las estructuras de datos soportadas en el lenguaje, en particular sobre las cadenas. La librería para manejar las cadenas es Text. y esta tiene las siguientes doce operaciones:

  • Append(texto1, texto2): Sirve para concatenar dos variables o valores literales, sean números y/o cadenas. El resultado que devolverá será una cadena concatenada. Es una función bastante útil para evitar dejar al lenguaje que trate a aquellas cadenas que contienen un número representado, que sean convertidas en números, en vez de tratarlas como cadenas.
  • ConvertToLowerCase(texto): Devuelve una copia en minúsculas de la cadena que le hemos pasado.
  • ConvertToUpperCase(texto): Devuelve una copia en mayúsculas de la cadena que le hemos pasado.
  • EndsWith(texto, subtexto): Devuelve “True” cuando la cadena que le hemos pasado en texto termina igual que la segunda cadena que le hemos pasado en subtexto.
  • GetCharacter(códigoUnicode): Devuelve para un código unicode dado el caracter que lo representa. Es algo que puede ser útil para obtener cadenas con valores de caracteres que no se pueden escribir dentro de una cadena, por ejemplo el salto de linea, las comillas o la tabulación.
  • GetCharacterCode(letra): Hace el paso inverso a la anterior función, toma una letra y devuelve su código unicode.
  • GetIndexOf(texto, subtexto): Devuelve la posición de una subcadena que esté dentro de la cadena que le hemos pasado en texto. Si no encontrara ninguna coincidencia con el patrón, devolverá como resultado el 0, porque las cadenas empiezan a numerarse sus posiciones a partir del 1 en adelante.
  • GetLength(texto): Devuelve el tamaño en caracteres de una cadena.
  • GetSubText(texto, comienzo, longitud): Devuelve una subcadena de la cadena que le hemos pasado en texto. Esta subcadena empieza en la posición indicada en comienzo y termina en la posición comienzo+longitud.
  • GetSubTextToEnd(texto, comienzo): Igual que la anterior, pero toma siempre como longitud lo que queda de la cadena a partir de la posición indicada en comienzo.
  • IsSubText(texto, subtexto): Devuelve “True” cuando la cadena que le hemos pasado en texto contiene en su interior, en alguna posición, la segunda cadena que le hemos pasado en subtexto.
  • StartsWith(texto, subtexto): Devuelve “True” cuando la cadena que le hemos pasado en texto empieza igual que la segunda cadena que le hemos pasado en subtexto.

A parte de conocer esas operaciones hay que tener en cuenta también que hay que ser muy cuidadoso al tratar de concatenar cadenas con cadenas o con números, ya que como he señalado antes Small Basic las tratará como números si en su interior se encuentra representado un número. Por cierto que la cadena vacía o llena de espacios es representada como el número cero, por lo que podría fallar si queremos juntar un montón de números separados por espacios. También hay que saber que los valores booleanos verdadero y falso vienen representados como las cadenas “True” y “False”. Y recordad siempre que las cadenas empiezan en la posición 1 y no en la 0 como es habitual en otros lenguajes de programación.

Para ir finalizando aquí teneis un ejemplo sobre el manejo de cadenas, donde podeis ver cosas como el problema con los números, podeis ver como se manejan las busquedas de patrones, de subcadenas y de conversión de caracteres.

'----------------------------------------------------
' Prueba con cadenas
'----------------------------------------------------
TextWindow.Title = "Prueba con cadenas"

'----------------------------------------------------
' Obtenemos caracteres especiales
comillas = Text.GetCharacter(34)                '\"
saltolinea = Text.GetCharacter(13)              '\n
saltolinea = saltolinea + Text.GetCharacter(10) '\r
tabulacion = Text.GetCharacter(9)               '\t

'----------------------------------------------------
' Primero concatenar cadenas
TextWindow.WriteLine("***Concatenemos cadenas***")
aux1 = "¡HoLa "
aux2 = "MuNdO!"
aux3 = Text.Append(aux1, aux2)
TextWindow.WriteLine(aux3)
TextWindow.WriteLine("1000" + "1")
TextWindow.WriteLine(Text.Append(1000, 1))
TextWindow.WriteLine(Text.Append("1000", "1"))
aux = Text.GetLength(aux3)
TextWindow.WriteLine("Tamaño aux3 = " + aux)
TextWindow.Write(saltolinea)

'----------------------------------------------------
' Segundo pasar a minúsculas y mayúsculas
TextWindow.WriteLine("***Conversiones***")
aux = Text.ConvertToLowerCase(aux3)
TextWindow.WriteLine(comillas + aux + comillas)
aux = Text.ConvertToUpperCase(aux3)
TextWindow.WriteLine(comillas + aux + comillas)
TextWindow.Write(saltolinea)

PauseAndClear()

'----------------------------------------------------
' Tercero varias busquedas positivas
TextWindow.WriteLine("***Busquedas positivas***")
aux3 = "¡Hola mundo!"
aux = Text.StartsWith(aux3, "¡Ho")
TextWindow.WriteLine("Busqueda = " + aux)
aux = Text.EndsWith(aux3, "do!")
TextWindow.WriteLine("Busqueda = " + aux)
aux = Text.IsSubText(aux3, "a m")
TextWindow.WriteLine("Busqueda = " + aux)
aux = Text.GetIndexOf(aux3, "a m")
TextWindow.WriteLine("Busqueda = " + aux)
TextWindow.Write(saltolinea)

'----------------------------------------------------
' Tercero varias busquedas negativas
TextWindow.WriteLine("***Busquedas negativas***")
aux = Text.StartsWith(aux3, "H")
TextWindow.WriteLine("Busqueda = " + aux)
aux = Text.EndsWith(aux3, "o")
TextWindow.WriteLine("Busqueda = " + aux)
aux = Text.IsSubText(aux3, ".")
TextWindow.WriteLine("Busqueda = " + aux)
aux = Text.GetIndexOf(aux3, ".")
TextWindow.WriteLine("Busqueda = " + aux)
TextWindow.Write(saltolinea)

PauseAndClear()

'----------------------------------------------------
' Tercero varias busquedas negativas
TextWindow.WriteLine("***Tomando subcadenas***")
aux3 = "Hola Basic."
For i = 1 To Text.GetLength(aux3) - 1
  aux = Text.GetSubText(aux3, 1, i)
  TextWindow.WriteLine(aux)
EndFor
For i = 1 To Text.GetLength(aux3)
  aux = Text.GetSubTextToEnd(aux3, i)
  TextWindow.WriteLine(aux)
EndFor
TextWindow.Write(saltolinea)

PauseAndClear()

'----------------------------------------------------
' Cuarto jugando con caracteres
TextWindow.WriteLine("***Usando caracteres***")
aux3 = "Violence is the last refuge of the incompetent"
For i = 1 To Text.GetLength(aux3)
  aux = Text.GetSubText(aux3, i, 1)
  code = Text.GetCharacterCode(aux)
  aux = Text.Append(code, " ")
  TextWindow.Write(aux)
EndFor
TextWindow.WriteLine(saltolinea)

For i = 0 To 25
  code = Text.GetCharacterCode("a") + i
  aux = Text.GetCharacter(code) + " "
  TextWindow.Write(aux)
EndFor
TextWindow.WriteLine(saltolinea)

'----------------------------------------------------
' Subrutinas de apoyo
Sub PauseAndClear
  TextWindow.Pause()
  TextWindow.Clear()
EndSub

En el próximo artículo, seguramente siga hablando de las estructuras de datos, ya sea para hablar de los arrays o de las pilas. Mientras tanto que lo paseis bien programando.


El tiempo y la ejecución en Small Basic

Abril 29, 2009

Para continuar con un poco más de Small Basic vamos a hablar de tres librerías: Program, Clock y Timer. No hay que olvidar que de momento Small Basic es un lenguaje en desarrollo y está en estado beta, por lo que existe la posibilidad de que ocurran fallos inesperados. Comento eso porque en el propio foro de ayuda algunas personas se quejaban de que la librería Timer a veces fallaba, pero al menos los dos ejemplos que encontraréis en este artículo os puedo garantizar que a mi me han funcionado.

En fin, pasando ya al tema en sí, la librería Program sirve para controlar aspectos relacionados con la ejecución del programa. Para ello tenemos dos operaciones y también una propiedad:

  • Delay(milisegundos): Retrasa la ejecución del programa una cantidad determinada de milisegundos.
  • End(): Termina la ejecución del programa.
  • Directory: Permite obtener el directorio actual donde se está ejecutando la aplicación.

Y para poder comprenderlo un poco mejor aquí tenéis un ejemplo de las tres cosas:

'----------------------------------------------------
' Prueba del manejo de la ejecución
'----------------------------------------------------
TextWindow.Title = "Prueba del manejo de la ejecución"
TextWindow.WriteLine("Vamos a obtener el directorio del programa")
TextWindow.Write("Recibiendo información")
For i = 1 To 42
  Program.Delay(80)
  TextWindow.Write(".")
EndFor
TextWindow.WriteLine("")
TextWindow.WriteLine("")

TextWindow.WriteLine("El directorio es:")
TextWindow.WriteLine(Program.Directory)
TextWindow.WriteLine("")

While "true"
  TextWindow.Write("Mete un 0 para salir: ")
  aux = TextWindow.ReadNumber()
  TextWindow.WriteLine("Has metido un: " + aux)
  TextWindow.WriteLine("")
  If aux = 0 Then
    TextWindow.Pause()
    Program.End()
  EndIf
EndWhile

La siguiente librería que trataremos en este artículo es Clock, que básicamente sirve para acceder a la fecha y hora del reloj del sistema. Tiene diez propiedades, que son:

  • Date: Permite obtener la fecha del sistema.
  • Time: Permite obtener la hora del sistema.
  • Year: Permite obtener el año.
  • Month: Permite obtener el mes.
  • Day: Permite obtener el día.
  • WeekDay: Permite obtener el día de la semana.
  • Hour: Permite obtener la hora actual.
  • Minute: Permite obtener el minuto actual.
  • Second: Permite obtener el segundo actual.
  • Millisecond: Permite obtener el milisegundo actual.

Luego tenemos la librería Timer, que nos facilita la opción de poder repetir un fragmento de código, de nuestra aplicación, cada cierto intervalo de tiempo de forma indefinida. Por lo que nosotros indicaremos cuanto dura el intervalo de repetición y cada vez que pase esa cantidad fijada de tiempo, se lanzará un evento (este evento suele ser denominado como “Tick”), que Timer atrapará e invocará a la subrutina asociada a dicho evento. Esta librería tiene dos propiedades y dos operaciones:

  • Interval: Permite obtener y cambiar el intervalo de repetición en milisegundos. Con ello podemos indicar con qué frecuencia será invocado el evento “Tick”. El valor que le pasemos tiene que estar en el siguiente rango: de 10 a 100000000.
  • Tick: Permite cambiar la subrutina que será invocada cada vez que se produzca el evento “Tick”.
  • Pause(): Deja la librería en modo pausa, por lo que no se producirán más eventos “Tick” mientras estemos en ese modo.
  • Resume(): Quita el modo pausa, permitiendo volver a generar eventos “Tick” de nuevo.

Y sabiendo todo lo que he expuesto en lo anterior, nos podemos hacer un bonito reloj como el que aparece en el siguiente ejemplo:

'----------------------------------------------------
' Prueba con el manejo del tiempo
'----------------------------------------------------
TextWindow.Title = "Prueba con el manejo del tiempo"
TextWindow.WriteLine("Formato estándar.")
TextWindow.WriteLine("Fecha: " + Clock.Date)
TextWindow.WriteLine("Hora:  " + Clock.Time)
TextWindow.WriteLine("")

TextWindow.WriteLine("Formato del usuario.")
TextWindow.Write("Fecha: ")
MostrarFechaCompleta()
TextWindow.Write("Hora:  ")
MostrarHoraCompleta()
TextWindow.WriteLine("")

Timer.Pause()
Timer.Tick = MostrarReloj
Timer.Interval = 100
Timer.Resume()

TextWindow.CursorLeft = 0
TextWindow.CursorTop = 22
TextWindow.Write("Pulse una tecla para salir...")
TextWindow.PauseWithoutMessage()
Program.End()

Sub MostrarFechaCompleta
  aux = Clock.Year + "-"
  aux = aux + Clock.Month + "-"
  aux = aux + Clock.Day + " "
  aux = aux + Clock.WeekDay
  TextWindow.WriteLine(aux)
EndSub

Sub MostrarHoraCompleta
  aux = Clock.Hour + ":"
  aux = aux + Clock.Minute + ":"
  aux = aux + Clock.Second + "."
  aux = aux + Clock.Millisecond
  TextWindow.WriteLine(aux)
EndSub

Sub MostrarReloj
  TextWindow.CursorLeft = 30
  TextWindow.CursorTop = 10
  MostrarHoraCompleta()
EndSub

En el próximo artículo lo más seguro es que comente alguna de las librerías para manejar estructuras de datos, posiblemente primero hable sobre manejar cadenas. Mientras tanto que lo paséis bien trasteando con los ejemplos.


Los argumentos en Small Basic

Abril 29, 2009

Hoy empezaré hablando de como se utilizan los argumentos de la aplicación bajo Small Basic. Para ello tenemos la librería Arguments en el lenguaje. Esta sólo tiene una propiedad y un método, que son:

  • Count: Permite obtener el número de argumentos, de línea de comando, pasados al programa.
  • GetArgument(índice): Devuelve el argumento indicado por el índice que le demos. Hay que tener en cuenta que los argumentos del programa empiezan a numerarse desde el número 1 en adelante.

Así que sabiendo esto podemos manejar ya aplicaciones que puedan recibir argumentos de entrada, como ocurre con muchos de los programas que usamos diariamente. Y para poder verlo mejor, aquí teneis un ejemplo ilustrativo:

'----------------------------------------------------
' Prueba con los argumentos
'----------------------------------------------------
TextWindow.Title = "Prueba con los argumentos"
TextWindow.WriteLine("Has metido " + Arguments.Count + " argumentos.")

' Si se han pasado argumentos al programa, los mostramos por pantalla.
If Arguments.Count > 0 Then
  TextWindow.WriteLine("Lista de argumentos:")
  tab = "    "
  For i = 1 To Arguments.Count
    TextWindow.WriteLine(tab + Arguments.GetArgument(i))
  EndFor
EndIf

TextWindow.WriteLine("")

Aplicaciones en modo consola con Small Basic

Abril 28, 2009

Hoy voy a hablar sobre la librería para manejar el modo consola en el lenguaje Small Basic. A parte de haber dejado un ejemplo de código al final del artículo, también lo he colgado en la web de microsoft para subir código, la dirección es esta. Como bien puse en el anterior comentario sobre las APIs, la librería para manejar el modo consola es TextWindow. Dentro de esta librería tenemos las siguientes propiedades:

  • Title: Permite obtener y cambiar el título de la ventana.
  • Left: Permite obtener y cambiar la coordenada X de la ventana.
  • Top: Permite obtener y cambiar la coordenada Y de la ventana.
  • CursorLeft: Permite obtener y cambiar la columna actual del cursor.
  • CursorTop: Permite obtener y cambiar la fila actual del cursor.
  • BackgroundColor: Permite obtener y cambiar el color de fondo del texto que escribamos en la ventana.
  • ForegroundColor: Permite obtener y cambiar el color del texto que escribamos en la ventana.

Al final de la guía de introducción al Small Basic que hay en la página del proyecto, aparece una lista de los colores que soporta actualmente las APIs del lenguaje. Sin embargo para el modo consola, como es tradicional, solo soporta 16 colores, siendo los agraciados los siguientes: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White.

Hay que tener en cuenta que si cambiamos el color de fondo, no se va a cambiar el fondo automáticamente. Se actualizará el color cuando borremos la pantalla entera o escribamos texto sobre esta. Por lo que vamos a echar un vistazo a las operaciones de la librería para saber qué podemos hacer:

  • Show(): Muestra la ventana en modo consola, para permitir la interacción con ella.
  • Hide(): Esconde la ventana, imposibilitando la interacción con ella.
  • Clear(): Limpia el contenido de la pantalla, usando el color de fondo actual.
  • Write(dato): Escribe en pantalla una cadena, número o el resultado de una expresión. Al contrario que WriteLine, esta operación no termina realizando un salto de línea, por lo que lo siguiente que vayamos a escribir en la ventana, salvo que cambiemos la posición del cursor, será escrito justo detrás de lo que acabamos de mostrar.
  • WriteLine(dato): Escribe en pantalla una cadena, número o el resultado de una expresión. Al terminar de escribir en pantalla el texto, se producirá un salto de línea, de ese modo lo siguiente que se vaya a mostrar por pantalla se situará en el inicio de la siguiente línea de la consola.
  • Read(): Lee una línea de texto introducida por teclado en la ventana y la devuelve. Esta función no terminará hasta que el usuario pulse la tecla ENTER.
  • ReadNumber(): Lee un número introducido por teclado en la ventana y lo devuelve. Esta función no terminará hasta que el usuario pulse la tecla ENTER.
  • Pause(): Muestra un mensaje y espera a que el usuario pulse una tecla, para poder continuar la ejecución del programa.
  • PauseIfVisible(): Hace lo mismo que Pause() con la salvedad de que comprueba si la ventana no está escondida, pues en caso de estarlo no realizaría la pausa.
  • PauseWithoutMessage(): Hace lo mismo que Pause() con la salvedad de no mostrar el mensaje por pantalla, para dar la oportunidad al programador de ser él, quien determine qué mensaje va a preceder a la pausa.

Y sabiendo esto ya podríamos echar un vistazo al programa de ejemplo que he colgado, pasando por alto las primeras 4 líneas de código que utilizan la librería de manejo de texto y que ya explicaremos en el futuro. Tan solo hay que saber, que esas líneas declaran 3 variables para contener 3 caracteres especiales que no tienen representación en las cadenas de Small Basic, salvo usando una variable como intermediaria. Pero ya hablaremos de ello más adelante. El ejemplo juega un poco con la posición del cursor, la de la ventana, los colores, las pausas y la entrada por teclado.

'----------------------------------------------------
' Prueba del modo consola con Small Basic
'----------------------------------------------------

comillas = Text.GetCharacter(34)                '\"
saltolinea = Text.GetCharacter(13)              '\n
saltolinea = saltolinea + Text.GetCharacter(10) '\r
tabulacion = Text.GetCharacter(9)               '\t

'----------------------------------------------------
' Prueba del hola mundo
'----------------------------------------------------
TextWindow.Title = "Prueba en modo consola"
TextWindow.BackgroundColor = "Black"
TextWindow.ForegroundColor = "Green"
TextWindow.CursorLeft = 10
TextWindow.CursorTop = 1
TextWindow.Write(comillas + "Hola mundo" + comillas)

TextWindow.ForegroundColor = "White"
TextWindow.CursorLeft = 0
TextWindow.CursorTop = 3
MostrarLeftTop()

TextWindow.Write("Mueva la ventana de la consola y ")
TextWindow.Write("pulse una tecla para continuar...")
TextWindow.WriteLine(saltolinea)
TextWindow.PauseWithoutMessage()

MostrarLeftTop()
TextWindow.Pause()

Sub MostrarLeftTop
  TextWindow.WriteLine("Left: " + TextWindow.Left)
  TextWindow.WriteLine("Top:  " + TextWindow.Top)
  TextWindow.WriteLine("")
EndSub

'----------------------------------------------------
' Prueba de colores en el modo consola
'----------------------------------------------------
TextWindow.Clear()
TextWindow.Write("Introduce una cadena: ")
cadena = TextWindow.Read()

TextWindow.CursorTop = 2
TextWindow.Write("Colores soportados en este modo:")
TextWindow.CursorTop = 4
TextWindow.CursorLeft = 0

color = "Black"
MostrarCadenaConColor()
color = "DarkBlue"
MostrarCadenaConColor()
color = "DarkGreen"
MostrarCadenaConColor()
color = "DarkCyan"
MostrarCadenaConColor()
color = "DarkRed"
MostrarCadenaConColor()
color = "DarkMagenta"
MostrarCadenaConColor()
color = "DarkYellow"
MostrarCadenaConColor()
color = "Gray"
MostrarCadenaConColor()
color = "DarkGray"
MostrarCadenaConColor()
color = "Blue"
MostrarCadenaConColor()
color = "Green"
MostrarCadenaConColor()
color = "Cyan"
MostrarCadenaConColor()
color = "Red"
MostrarCadenaConColor()
color = "Magenta"
MostrarCadenaConColor()
color = "Yellow"
MostrarCadenaConColor()
color = "White"
MostrarCadenaConColor()

TextWindow.WriteLine("")
TextWindow.Pause()

Sub MostrarCadenaConColor
  TextWindow.ForegroundColor = "White"
  TextWindow.Write(tabulacion + color + ": ")
  TextWindow.ForegroundColor = color
  TextWindow.CursorLeft = 21
  TextWindow.WriteLine(cadena)
EndSub

'----------------------------------------------------
' Prueba de la entrada de números
'----------------------------------------------------
TextWindow.Clear()
TextWindow.Write("Vamos a cambiar la posición de ")
TextWindow.WriteLine("la ventana." + saltolinea)

TextWindow.Write("Introduce la x: ")
x = TextWindow.ReadNumber()
TextWindow.Write("Introduce la y: ")
y = TextWindow.ReadNumber()

TextWindow.Left = x
TextWindow.Top = y

TextWindow.Write(saltolinea + "Y eso es todo por ")
TextWindow.WriteLine("ahora..." + saltolinea)

Y eso es todo por hoy respecto al manejo de la consola con Small Basic. El próximo día comentaré posiblemente el funcionamiento de algunas de las librerías del sistema o de manejo de estructuras de datos.


Introducción a la API de Small Basic

Abril 27, 2009

Dentro del lenguaje Small Basic, en su versión 0.4, tenemos una API con la que poder trabajar. Los principales apartados de esta API son los siguientes conjuntos de “librerías”, por llamarlas de algún modo:

  • Librerías del sistema: Arguments, Clock, File, Math, Program, Sound, Timer.
  • Librerías para manejar estructuras de datos: Array, Stack, Text.
  • Librerías para manejar la consola: TextWindow.
  • Librerías para manejar la ventana: GraphicsWindow, ImageList, Mouse, Shapes, Turtle.
  • Librerías para manejar servicios: Desktop, Dictionary, Flickr, Network.

Las librerías del sistema podríamos decir que abarcan operaciones útiles que podemos llegar a necesitar en nuestros programas, ya sean aplicaciones en modo ventana o modo consola:

  • Arguments: Esta librería sirve para obtener los argumentos recibidos al iniciar la ejecución del programa.
  • Clock: Esta librería nos permite acceder al reloj del sistema.
  • File: Esta librería permite realizar diferentes operaciones con ficheros y directorios.
  • Math: Esta librería ofrece múltiples operaciones matemáticas.
  • Program: Esta librería contiene operaciones para controlar la ejecución del programa.
  • Sound: Esta librería sirve para manejar ficheros de sonido, aunque también contiene algunos sonidos asociados a ella.
  • Timer: Esta librería permite configurar nuestra aplicación para que cada cierto intervalo de tiempo salte un evento y se ejecute alguna rutina en particular que nosotros queramos.

Las librerías para manejar estructuras de datos sirven para dotar a este lenguaje mutilado de mecanismos suficientes como para trabajar con arrays con la librería Array, pilas con la librería Stack y cadenas con la librería Text. De esta forma podemos tener tipos de datos un tanto más complejos que un simple número en nuestra aplicación, sin embargo esto no está exento de limitaciones y complicaciones.

La única librería para manejar el modo consola de texto es TextWindow. Con ella podemos modificar las propiedades de la ventana de la consola (posición, colores), cambiar la posición del cursor, escribir texto, leer desde el teclado o realizar pausas.

Las librerías para manejar el modo ventana se diferencian de la anterior ya que con ellas pasaremos a trabajar con imagenes en 2D, en vez de trabajar solo con texto:

  • GraphicsWindow: Es el “equivalente” a TextWindow para el modo ventana. Encapsula las principales operaciones que podemos realizar sobre una ventana.
  • ImageList: Con esta librería podemos almacenar imagenes en memoria, que luego podremos mandar a pintar en la ventana con la anterior librería.
  • Mouse: Esta librería nos permite acceder al ratón y sus propiedades.
  • Shapes: Esta librería sirve para crear y modificar formas tales como imagenes, líneas, elipses, rectángulos o triángulos.
  • Turtle: Esta librería representa un “lápiz” con el que dibujar en la ventana. Antiguamente antes de que la interfaz gráfica de usuario fuera lo normal, para pintar en una pantalla apenas había medios y un mecanismo de aquella época más o menos “barato” y sencillo era la tortuga.

Por último tenemos las librerías para manejar una serie de servicios disponibles en el lenguaje, que algunos tienen que ver con elementos del ordenador y otros con elementos accesibles a través de internet:

  • Desktop: Esta librería nos permite interactuar mínimamente con el escritorio del sistema operativo.
  • Dictionary: Esta librería nos permite obtener acceso a un diccionario on-line de inglés y francés.
  • Flickr: Esta librería nos permite acceder al servicio de fotos de Flickr para obtener imágenes y guardarlas en nuestro disco duro.
  • Network: Esta librería nos da la capacidad de acceder a contenido almacenado en la web.

Y esto es todo por el momento, si teneis curiosidad dentro del IDE de Small Basic a medida que vais escribiendo se muestran todas las funciones y variables de las librerías, con una descripción en inglés que es bastante aclaratoria de lo que hace. Con eso podeis investigar una pizca y programar alguna cosilla con el lenguaje.


Manejando ficheros excel desde C#

Abril 27, 2009

En las últimas semanas he estado trabajando con C# y una de las cosas que he tenido que hacer fue manejar ficheros Excel. Hay dos formas para trabajar con ellos, usando el interop de Microsoft o tratando el fichero como una base de datos con ADO.NET, el problema es que con lo último no podemos borrar cosas, con lo que nos encontramos con un problema un tanto estrambótico. Así que para hacer lo que yo quería hacer tuve a mi pesar que usar interop.

Lo primero que hay que hacer es añadir la referencia a la librería COM “Microsoft Excel 11.0 Object Library”, que corresponde más o menos al Office 2003. Una vez hemos configurado nuestro proyecto para poder invocar la aplicación Excel desde nuestro programa el código es el siguiente:

// A library to handle excel files in a simple way.
// Copyright (C) 2009  Gorka Suárez García
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see .
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Office.Interop.Excel;

namespace Excel {
    /// <summary>
    /// This class is used to handle an excel file to write and read from it.
    /// Author: Gorka Suárez García
    /// </summary>
    public class ExcelHandler {
        /// <summary>
        /// The excel application instance.
        /// </summary>
        private ApplicationClass app;

        /// <summary>
        /// The excel book.
        /// </summary>
        private Workbook book;

        /// <summary>
        /// The path of the excel file.
        /// </summary>
        private string path;

        /// <summary>
        /// Constructs a new ExcelHandler object.
        /// </summary>
        public ExcelHandler() {
            this.app = null;
            this.book = null;
            this.path = null;
        }

        /// <summary>
        /// Destroys the ExcelHandler object.
        /// </summary>
        ~ExcelHandler() {
            if (this.app != null) {
                this.app.Quit();
            }
        }

        /// <summary>
        /// Opens an excel file.
        /// </summary>
        /// <param name="path">The file to open.</param>
        public void Open(string path) {
            this.path = path;

            this.app = new ApplicationClass();
            this.app.Visible = false;
            this.app.ScreenUpdating = false;
            this.app.DisplayAlerts = false;

            this.book = this.app.Workbooks.Open(this.path, Missing.Value, Missing.Value, Missing.Value,
                                                Missing.Value, Missing.Value, Missing.Value, Missing.Value,
                                                Missing.Value, Missing.Value, Missing.Value, Missing.Value,
                                                Missing.Value, Missing.Value, Missing.Value);

            if (this.book == null)
                throw new Exception("Can't open the excel book file.");
        }

        /// <summary>
        /// Writes a value in a cell.
        /// </summary>
        /// <param name="sheet">The sheet to write.</param>
        /// <param name="cell">The cell to write.</param>
        /// <param name="value">The value to write.</param>
        public void Write(string sheet, string cell, string value) {
            Worksheet wsheet = this.getSheet(sheet);
            Range range = wsheet.get_Range(cell, cell);
            range.Value2 = value;
        }

        /// <summary>
        /// Reads a value from a cell.
        /// </summary>
        /// <param name="sheet">The sheet to read.</param>
        /// <param name="cell">The cell to read.</param>
        /// <returns>The value from the cell.</returns>
        public string Read(string sheet, string cell) {
            Worksheet wsheet = this.getSheet(sheet);
            Range range = wsheet.get_Range(cell, cell);

            if (range.Value2 != null)
                return range.Value2.ToString();
            else
                return "";
        }

        /// <summary>
        /// Clears the content of the excel book.
        /// </summary>
        public void Clear() {
            Worksheet sheet = null;
            for (int i = 1; i <= this.book.Worksheets.Count; i++) {
                sheet = (Worksheet)this.book.Worksheets[i];
                sheet.Cells.Clear();
            }
        }

        /// <summary>
        /// Closes the excel file.
        /// </summary>
        public void Close() {
            this.book.SaveAs(this.path, XlFileFormat.xlWorkbookNormal, Missing.Value, Missing.Value,
                             false, false, XlSaveAsAccessMode.xlShared, false, false, Missing.Value,
                             Missing.Value, Missing.Value);
            this.book.Close(true, Missing.Value, Missing.Value);
            this.app.Quit();

            this.app = null;
            this.book = null;
            this.path = null;
        }

        /// <summary>
        /// Gets all the names of the sheets inside the excel book.
        /// </summary>
        /// <returns>A list of the sheets names.</returns>
        public string[] GetSheetsNames() {
            List names = new List();
            Worksheet sheet = null;

            for (int i = 1; i <= this.book.Worksheets.Count; i++) {
                sheet = (Worksheet)this.book.Worksheets[i];
                names.Add(sheet.Name);
            }

            return names.ToArray();
        }

        /// <summary>
        /// Gets a sheet we're looking for.
        /// </summary>
        /// <param name="name">The name of the sheet.</param>
        /// <returns>The sheet we're looking for.</returns>
        protected Worksheet getSheet(string name) {
            int index = this.getSheetIndex(name);
            if (index == 0)
                throw new Exception("Invalid sheet name.");

            Worksheet sheet = (Worksheet)this.book.Worksheets[index];
            return sheet;
        }

        /// <summary>
        /// Gets the index of a sheet we're looking for.
        /// </summary>
        /// <param name="name">The name of the sheet.</param>
        /// <returns>The index of the sheet we're looking for.</returns>
        protected int getSheetIndex(string name) {
            Worksheet sheet = null;
            for (int i = 1; i <= this.book.Worksheets.Count; i++) {
                sheet = (Worksheet)this.book.Worksheets[i];
                if (sheet.Name == name) return i;
            }
            return 0;
        }
    }
}

El objeto de app de tipo ApplicationClass es una instancia del programa Excel, por lo que si vamos al administrador de tareas mientras se ejecuta nuestra aplicación, veremos que “Excel.exe” está ejecutándose. Luego el objeto de tipo Workbook es una instancia del libro contenido en un fichero de tipo excel. Y por último path es simplemente la ruta del fichero, que la guardaremos para salvar los cambios al cerrar el fichero. Así que teniendo esto en cuenta para abrir un documento excel lo haríamos así:

public void Open(string path) {
  // Asignamos la ruta del fichero.
  this.path = path;
  // Instanciamos la aplicación excel.
  this.app = new ApplicationClass();
  // Indicamos que no será visible,
  this.app.Visible = false;
  // que no se va a actualizar la pantalla
  this.app.ScreenUpdating = false;
  // y que no va a mostrar los mensajes de alerta.
  this.app.DisplayAlerts = false;

  // Abrimos un libro excel pasándole la ruta y el
  // resto de valores los dejamos sin asignar.
  this.book = this.app.Workbooks.Open(this.path,
    Missing.Value, Missing.Value, Missing.Value,
    Missing.Value, Missing.Value, Missing.Value,
    Missing.Value, Missing.Value, Missing.Value,
    Missing.Value, Missing.Value, Missing.Value,
    Missing.Value, Missing.Value);

  // Si el libro no se ha abierto lanzamos una excepción.
  if (this.book == null)
    throw new Exception("Can't open the excel book file.");
}

Para poder escribir algo en el fichero, tendremos que indicar la hoja y la celda como cadenas. El valor también será una cadena, sin embargo a efectos internos del fichero, será tratado como un número, un texto o una fórmula si encaja con el formato que excel considera apropiado para esa clase de datos. Por ello podemos asignar a una celda una fórmula siempre que cumpla el formato, para que excel lo detecte.

public void Write(string sheet, string cell,
                  string value)
{
  // Obtenemos la hoja del libro.
  Worksheet wsheet = this.getSheet(sheet);
  // Obtenemos la celda.
  Range range = wsheet.get_Range(cell, cell);
  // Asignamos el valor a la celda.
  range.Value2 = value;
}

Para obtener una celda tenemos que utilizar una función que nos devuelve un conjunto de celdas determinado por un rango de direcciones. Dentro de esta clase, la propiedad Value2 contiene o bien un array de celdas o una celda simplemente si solo hemos pedido un “conjunto” de una celda. De modo similar para leer tendríamos lo siguiente.

public string Read(string sheet, string cell)
{
  // Obtenemos la hoja del libro.
  Worksheet wsheet = this.getSheet(sheet);
  // Obtenemos la celda.
  Range range = wsheet.get_Range(cell, cell);

  // Devolvemos el valor de la celda.
  if (range.Value2 != null)
    return range.Value2.ToString();
  else
    return "";
}

Hay que tener en cuenta que si una celda no está inicializada o no tiene valor, nos devolvería un null, con lo que en este caso devolvemos una cadena vacía como representación de eso. Ahora que sabemos leer y escribir, lo suyo es saber salvar y cerrar el fichero.

public void Close() {
  // Salvamos el contenido del libro.
  this.book.SaveAs(this.path,
    XlFileFormat.xlWorkbookNormal, Missing.Value,
    Missing.Value, false, false,
    XlSaveAsAccessMode.xlShared, false, false,
    Missing.Value, Missing.Value, Missing.Value);
  // Cerramos el libro.
  this.book.Close(true, Missing.Value, Missing.Value);
  // Y salimos de la aplicación excel.
  this.app.Quit();

  this.app = null;
  this.book = null;
  this.path = null;
}

Otras operaciones varias en el código de la clase son:

  • Clear: Que borra todas las hojas del fichero.
  • GetSheetsNames: Que devuelve un array con los nombres de las hojas del fichero.
  • getSheet: Que busca una hoja en base a su nombre y la devuelve.
  • getSheetIndex: Que devuelve en qué índice está una hoja en base a su nombre.

Para hacer esas operaciones hay que tener en cuenta cosas como que en Worksheet tenemos la propiedad Cells, que tiene el método Clear() para borrar el contenido de una hoja. Que en Workbook tenemos la propiedad Worksheets que es un array con todas las hojas del libro excel, y como tal array tiene la propiedad Count para saber el número de hojas del libro. Sin embargo dicho array va desde 1 hasta Count incluido. Y por último dentro de Worksheet tenemos la propiedad Name, con el nombre de la hoja.

Y esto es lo más básico para trabajar con fichero Excel usando interop desde .NET, se pueden hacer más cosas pero aun así hay algunos problemillas de esta API, como que no siempre se cierra bien la aplicación o que a veces tarda un buen rato en ser cerrada, o que si salimos de forma abrupta de nuestra aplicación, se puede quedar el programa Excel abierto en el limbo. Pero peor sería hacerlo todo desde cero, eso seguro.