Hojas de cálculo en Excel - página principal
Mostrando entradas con la etiqueta FSO. Mostrar todas las entradas
Mostrando entradas con la etiqueta FSO. Mostrar todas las entradas

Espacio libre y espacio total en disco

Algunas veces, nos interesa obtener información sobre el espacio libre que tiene un disco (ya sea un disco duro, un disco externo, una llave USB, etc.). Desde VBA, y a través de un macro, esta operación es tan sencilla como aquella en la que vimos como determinar la fecha de creación, la fecha de modificación, la fecha del último acceso, y el peso de kbytes de un fichero.

Para ello, volveremos una vez más a utilizar el siempre socorrido objeto FileSystemObject. Tan solo tendremos que llamar a la propiedad AvailableSpace, y que será la que nos aportará la información que estamos buscando.

Vamos a ilustrar esto con un ejemplo, como suele ser habitual. Vamos a redondearlo un poquito más, y montaremos un desplegable en el que se nos cargarán las unidades disponibles (C:, D:, F:, etc.). Seleccionaremos una de ellas, pulsaremos sobre un botón habilitado a tal efecto, y nos determinará el espacio libre que hay en esa unidad que hemos elegido. Lo primero que haremos será precisamente el desplegable (un combobox) que incluiremos en un UserForm, y al que le asociaremos este código cada vez que se cargue:


Private Sub UserForm_Initialize()
'Creamos el objeto fso
Set fso = CreateObject("Scripting.FileSystemObject")
'vamos a mirar las unidades que hay
Set unidades = fso.Drives
For Each Unidad In unidades
'Recorremos la colección
ComboBox1.AddItem Unidad.DriveLetter
Next
'limpiamos los objetos
Set unidades = Nothing
Set fso = Nothing
End Sub

Ese código hará que cada vez que se cargue el UserForm, se rellene automáticamente el ComboBox1 con las unidades disponibles en el sistema.

Ahora asociaremos este otro código a un botón que hay en el UserFrom:

Private Sub CommandButton1_Click()
'Si hay errores, que continúe
On Error Resume Next
'Miramos qué elemento se ha seleccionado
'del Combobox1, y lo pasamos a una variable

unidad_elegida = ComboBox1.List(ComboBox1.ListIndex)
'creamos el objeto fso
Set fso = CreateObject("Scripting.FileSystemObject")
Set driveObject = fso.GetDrive(unidad_elegida)
'pasamos los datos que nos interesan, a variables
espacio_total = FormatNumber(driveObject.TotalSize / 1024 / 1024 / 1024, 2, , , -1)
espacio_libre = FormatNumber(driveObject.AvailableSpace / 1024 / 1024 / 1024, 2, , , -1)
porcentaje_libre = FormatNumber(espacio_libre * 100 / espacio_total, 2, , , -1)
'si no hay errores, mostramos un MsgBox
If Err = 0 Then
MsgBox (Chr(13) + "Espacio total: " _
+ espacio_total & " Gb" _
+ Chr(13) _
+ "Espacio disponible (libre): " _
+ espacio_libre & " Gb" _
+ " (" & porcentaje_libre & "% libre)" _
+ Chr(13) + Chr(13)), vbOKOnly, " Espacio libre"
End If
'limpiamos los objetos
Set driveObject = Nothing
Set fso = Nothing
End Sub

El formulario que tendremos que diseñar, deberá tener un aspecto parecido a este:


Si os fijáis bien, en el segundo código que he puesto (el del CommandButton1), aparte de mostrar el espacio disponible de cada unidad, también mostraremos el espacio total que tiene. Aquí tenéis un ejemplo de lo que sale si ejecuto la consulta de una unidad F (una llave USB), de 2 Gb de capacidad (en la imagen, he desplazado el Msgbox, para que se vea el UserForm al fondo):


Si nos encontramos con alguna unidad de tipo CD-ROM o DVD, evidentemente, no obtendremos los datos del espacio libre y del espacio disponible, pues esas unidades no nos pueden aportan esos datos, ya que no son ellas las que físicamente almacenan los datos, sino los soportes, es decir, los discos que introducimos en esas unidades (los CD's o los DVD's).

Desde aquí podéis descargar el fichero, con el ejemplo que hemos visto en este artículo.



Fecha de creación, modificación, último acceso de un fichero, y peso en Kbytes

Hace poco, un usuario dejaba un comentario en el blog, preguntando si era posible obtener la fecha de creación de un fichero excel, para implementarla dentro del propio fichero. Algo así como la función =HOY(), pero sin que variase a la hora de abrir el fichero, pues al abrir el libro en cuestión, automáticamente se actualizaría la fecha, poniendo la fecha del día en curso.

Como esta utilidad puede servirle a más de uno, y como me he acordado del preciado objeto FileSystemObject (ahora veréis que es, si es que no lo habéis visto ya en algún que otro artículo de este blog), vamos a dar respuesta a las inquietudes de ese lector.

El objeto FileSystemObject (en adelante lo llamaremos FSO), nos permite operar con ficheros. No es algo que debamos añadir (ningún complemento, ningún ActiveX, ...nada de esas cosas). El objeto FSO lo tenemos todos :-)

El objeto FSO nos permite leer, escribir, borrar, copiar, etc., determinados ficheros y carpetas (directorios). Tiene sus ventajas, pero también sus riesgos, si este objeto es utilizado por gente irresponsable. ¿Qué riesgos?. Vamos a dejarlo ahí, ...si no te has dado cuenta de lo que quiero decir, es que no has leído bien este párrafo :-)

Vamos al grano. ¿Podemos saber la fecha de creación de un fichero excel?. Por supuestísimo. Pero no solo eso, además, podemos saber la fecha de la última modificación de un fichero excel, y la fecha del último acceso a un fichero excel (si ejecutamos esto en el fichero abierto, aparentemente, la fecha de último acceso será la de hoy y ahora, pero Excel devuelve la fecha inmediatamente anterior a la de hoy, es decir, el último acceso sin contar el de hoy, a no ser que grabes los cambios, cierres y abras una segunda vez el fichero en el mismo día), aunque no lo hayamos modificado (si lo hemos abierto para chafardear su contenido). ¿Qué te parece?. ¿Mola saber todo eso, eh?. Pues hala, como postre, también averiguaremos cuanto pesa el fichero en Kbytes.

Copiaremos este macro dentro de ThisWorkbook (no dentro de un Módulo, sino dentro de ThisWorkbook), tal y como se muestra en la siguiente imagen:


El macro en cuestión es este (debe mantenerse ese nombre, pues se ejecuta en el momento de abrirse el fichero, a través del evento open):


Private Sub Workbook_Open()
'Si hay errores, que continúe
On Error Resume Next
'pasamos la ruta y el nombre del
'fichero activo, a una variable

fichero_y_ruta = ActiveWorkbook.Path & "\" & ActiveWorkbook.Name
'Creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'seleccionamos el libro activo
Set archivo = fso.GetFile(fichero_y_ruta)
'vamos a ver los datos que necesitamos
fecha_creacion = archivo.DateCreated
fecha_modificacion = archivo.DateLastModified
fecha_ultimo_acceso = archivo.DateLastAccessed
'pasaremos su tamaño de bytes a Kbytes,
'para lo cual, lo dividimos entre 1024

tamano_fichero = archivo.Size / 1024
'Si no hemos guardado previamente el fichero,
'nos dará error nº 424, por lo que tendremos
'esto en cuenta

If Err.Number <> 424 Then
Range("A1") = "Fecha de creación del fichero: " & fecha_creacion
Range("A2") = "Fecha de la última modificación del fichero: " & fecha_modificacion
Range("A3") = "Fecha del último acceso al fichero: " & FormatDateTime(fecha_ultimo_acceso, vbShortDate)
Range("A4") = "Tamaño del fichero: " & tamano_fichero & " Kb"
Else
Range("A1") = "Fecha de creación del fichero: n.d."
Range("A2") = "Fecha de la última modificación del fichero: n.d."
Range("A3") = "Fecha del último acceso al fichero: n.d."
Range("A4") = "Tamaño del fichero: n.d."
End If
'limpiamos los objetos
Set fso = Nothing
Set archivo = Nothing
End Sub

¿Que es lo que vamos a conseguir con este macro?. Pues vamos a escribir en las celdas que van desde A1 a A4, los siguientes datos:
  • La fecha de creación del fichero: Este dato nos saldrá por defecto con formato dd/mm/aaaa hh:mm:ss, es decir, nos saldrá la fecha, y al lado la hora, en la que se creó el fichero (se crea cuando se guarda por primera vez). Si en tu formato regional tienes indicado mm/dd/aaaa, entonces te lo mostrará así, invirtiendo día y mes.

  • La fecha de la última modificación del fichero: Este dato nos saldrá por defecto con formato dd/mm/aaaa hh:mm:ss, es decir, nos saldrá la fecha, y al lado la hora, en la que por última vez se modificó el fichero. Si en tu formato regional tienes indicado mm/dd/aaaa, entonces te lo mostrará así, invirtiendo día y mes.

  • La fecha del último acceso: Este dato nos saldrá por defecto con formato dd/mm/aaaa, es decir, nos saldrá la fecha, pero sin la hora al lado. Si en tu formato regional tienes indicado mm/dd/aaaa, entonces te lo mostrará así, invirtiendo día y mes.

  • Tamaño del fichero: Este dato nos saldrá en Kbytes.


Si deseamos que tanto la fecha de creación, como la fecha de modificación nos salgan con el formato dd/mm/aaaa, pero sin la hora, entonces nos bastará cambiar estas líneas del macro:

Range("A1") = "Fecha de creación del fichero: " & fecha_creacion
Range("A2") = "Fecha de la última modificación del fichero: " & fecha_modificacion

Por estas otras:

Range("A1") = "Fecha de creación del fichero: " & FormatDateTime(fecha_creacion, vbShortDate)
Range("A2") = "Fecha de la última modificación del fichero: " & FormatDateTime(fecha_modificacion, vbShortDate)

O si lo deseamos, por estas otras, que hacen exactamente lo mismo:

Range("A1") = "Fecha de creación del fichero: " & FormatDateTime(fecha_creacion, 2)
Range("A2") = "Fecha de la última modificación del fichero: " & FormatDateTime(fecha_modificacion, 2)

Si en lugar de un macro, queréis crearos vuestras propias funciones personalizadas, entonces bastará con copiar estas cuatro funciones en un módulo VBA (no en ThisWokbook, sino en un módulo). He variado ligeramente el mensaje que muestra, para que veáis la diferencia con el macro.

La función para determinar la fecha de creación del fichero será esta:

Function fechadecreacion()
'Si hay errores, que continúe
On Error Resume Next
'pasamos la ruta y el nombre del
'fichero activo, a una variable

fichero_y_ruta = ActiveWorkbook.Path & "\" & ActiveWorkbook.Name
'Creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'seleccionamos el libro activo
Set archivo = fso.GetFile(fichero_y_ruta)
'vamos a ver los datos que necesitamos
fecha_creacion = archivo.DateCreated
'Si no hemos guardado previamente el fichero,
'nos dará error nº 424, por lo que tendremos
'esto en cuenta

If Err.Number <> 424 Then
fecha = "Creado el " & FormatDateTime(fecha_creacion, vbShortDate)
Else
fecha = "Fichero sin guardar"
End If
'limpiamos los objetos
Set fso = Nothing
Set archivo = Nothing
'ponemos el dato en la celda activa
fechadecreacion = fecha
End Function

La función para determinar la última fecha de modificación del fichero, será esta:

Function fechadelaultimamodificacion()
'Si hay errores, que continúe
On Error Resume Next
'pasamos la ruta y el nombre del
'fichero activo, a una variable

fichero_y_ruta = ActiveWorkbook.Path & "\" & ActiveWorkbook.Name
'Creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'seleccionamos el libro activo
Set archivo = fso.GetFile(fichero_y_ruta)
'vamos a ver los datos que necesitamos
fecha_modificacion = archivo.DateLastModified
'Si no hemos guardado previamente el fichero,
'nos dará error nº 424, por lo que tendremos
'esto en cuenta

If Err.Number <> 424 Then
fecha = "Modificado el " & FormatDateTime(fecha_modificacion, vbShortDate)
Else
fecha = "Fichero sin guardar"
End If
'limpiamos los objetos
Set fso = Nothing
Set archivo = Nothing
'ponemos el dato en la celda activa
fechadelaultimamodificacion = fecha
End Function

La función para determinar la fecha del último acceso al fichero, será esta:

Function fechadelultimoacceso()
'Si hay errores, que continúe
On Error Resume Next
'pasamos la ruta y el nombre del
'fichero activo, a una variable

fichero_y_ruta = ActiveWorkbook.Path & "\" & ActiveWorkbook.Name
'Creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'seleccionamos el libro activo
Set archivo = fso.GetFile(fichero_y_ruta)
'vamos a ver los datos que necesitamos
fecha_ultimo_acceso = archivo.DateLastAccessed
'Si no hemos guardado previamente el fichero,
'nos dará error nº 424, por lo que tendremos
'esto en cuenta

If Err.Number <> 424 Then
fecha = "Último acceso el " & FormatDateTime(fecha_ultimo_acceso, vbShortDate)
Else
fecha = "Fichero sin guardar"
End If
'limpiamos los objetos
Set fso = Nothing
Set archivo = Nothing
'ponemos el dato en la celda activa
fechadelultimoacceso = fecha
End Function

La función para determinar el tamaño en Kbytes del fichero, será esta:

Function pesodelfichero()
'Si hay errores, que continúe
On Error Resume Next
'pasamos la ruta y el nombre del
'fichero activo, a una variable

fichero_y_ruta = ActiveWorkbook.Path & "\" & ActiveWorkbook.Name
'Creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'seleccionamos el libro activo
Set archivo = fso.GetFile(fichero_y_ruta)
'vamos a ver los datos que necesitamos
tamano_fichero = archivo.Size / 1024
'Si no hemos guardado previamente el fichero,
'nos dará error nº 424, por lo que tendremos
'esto en cuenta

If Err.Number <> 424 Then
peso = "Tamaño del fichero: " & tamano_fichero & " Kb"
Else
peso = "Fichero sin guardar"
End If
'limpiamos los objetos
Set fso = Nothing
Set archivo = Nothing
'ponemos el dato en la celda activa
pesodelfichero = peso
End Function

Y llamaremos a las funciones desde cualquier celda, así:

=fechadecreacion()


=fechadelaultimamodificacion()


=fechadelultimoacceso()


=pesodelfichero()

Si deseáis que estas funciones personalizadas estén disponibles para cualquier libro de Excel, sin necesidad de incorporarlas expresamente al libro en cuestión, entonces tendréis que echarle un vistazo al artículo donde hablo del libro de macros personal.

Aquí os dejo un pantallazo, con el resultado de aplicar el macro en el evento open, y las cuatro funciones personalizadas que hemos visto a lo largo de este artículo:



Leer las tablas de una base de datos Access

Andrés, un lector del blog, me preguntó hace unos días, si era posible leer las tablas de una base de datos Access desde excel, y como en la anterior ocasión, cuando explicábamos como compactar una base de datos Acess, desde excel, he tenido que hacer una sencilla adaptación de una rutina que servía para leer las tablas de una base de datos desde una página asp, y que fuese utilizable desde Excel.

La rutina que os presento, nos muestra un formulario (UserForm), y un combobox en su interior, que nos presentará las tablas de la base de datos que hayamos elegido. Una vez seleccionada la tabla que deseemos, pasaremos su nombre a una variable, y ya podremos hacer de todo con esa tabla: consultar registros, borrar registros, etc. Evidentemente, podremos mostrar todos los datos de la tabla seleccionada, tal y como explicábamos en el artículo sobre como leer una base de datos Access.

En el siguiente ejemplo, vamos a leer las tablas de una base de datos Access existente en una unidad F (en mi caso, la unidad F, corresponde a una llave USB), y dentro de una carpeta llamada hojas-de-calculo-en-excel (el nombre de este blog).

Lo primero que tendremos que hacer, será crear el UserForm. A continuación, le añadiremos un Combobox (dejaremos el nombre por defecto: ComboBox1), y le incluiremos este código:


Private Sub ComboBox1_Enter()
'Esto se producirá cuando
'cliqueemos en el combobox
'Si hay errores, que continúe

On Error Resume Next
'quitaremos todo lo que haya en el combobox1
ComboBox1.Clear
'Vamos a llenar dinámicamente el combobox
'con los nombres de las tablas.
'Primero definimos una constante

Const rsSchemaTablas = 20
'definimos la ruta de la base de datos
ruta = "F:\hojas-de-calculo-en-excel\base-de-datos.mdb"
'creamos la conexión
Set oConn = CreateObject("ADODB.Connection")
'abrimos la base de datos
oConn.Open ("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & ruta)
'creamos el filtro
Filtro = Array(Empty, Empty, Empty, "TABLE")
'leemos las tablas de la base de datos
Set rsSchema = oConn.OpenSchema(rsSchemaTablas, Filtro)
'hasta que no llegue al final...
Do While Not rsSchema.EOF
'Añadimos el nombre de la tabla al combobox
ComboBox1.AddItem rsSchema("TABLE_NAME")
'nos movemos al siguiente registro
'que nos devuelve el nombre de la
'siguiente tabla

rsSchema.MoveNext
Loop
'cerramos y limpiamos los objetos
oConn.Close
rsSchema.Close
Set oConn = Nothing
Set rsSchema = Nothing
End Sub

Si deseáis compactar una base de datos, que esté en la misma carpeta donde tengamos el fichero de excel, entonces sustituiremos la variable ruta del código anterior, para que nos quede definida de la siguiente forma:

ruta = ActiveWorkbook.Path & "\"

También añadiremos al UserForm, un botón que presente el texto "Aceptar" (un CommandButton), y que al pulsarlo, nos mostrará un mensaje (un MsgBox) que nos indicará el nombre de la tabla elegida, de tal forma que podremos hacer una consulta a esa tabla específica. El código del botón aceptar (el nombre del botón será el que nos presenta excel por defecto: CommandButton1), será este:

Private Sub CommandButton1_Click()
'pasamos el nombre de la tabla
'elegida, a una variable

tabla_elegida = ComboBox1.List(ComboBox1.ListIndex)
'mostramos un mensaje
MsgBox ("Has elegido la tabla: " & tabla_elegida)
End Sub

Si lo aplicáis a un ejemplo concreto, recordad cambiar en el código fuente de la hoja de cálculo, la ruta donde tendréis la base de datos, para que os funcione correctamente, informando manualmente de la carpeta donde se encuentra ésta, o bien, si la colocáis en la misma carpeta que el fichero de excel, poniendo como ruta, el path del propio fichero de excel, tal y como se menciona en este artículo.



Compactar una base de datos Access, desde Excel

Andrés, un lector del blog, me preguntaba el otro día -al hilo del artículo en el cual explicaba como importar desde excel y mediante un macro, los datos existentes de una tabla de Acess-, si era posible compactar una base de datos, directamente desde Excel. Yo intuía que sí se podía, pues eso mismo lo venía haciendo desde una página asp (active server page) atacando a una base de datos Access, así que suponía que con tan solo modificar algo el código, en teoría se debería poder hacer también desde Excel.

Y efectivamente así fue. Modifiqué el código ligeramente, y alehop!, desde excel podemos compactar una base de datos access, sin necesidad de abrir ni tan siquiera esa base de datos. Esta operación de compactación, la realizaremos mediante un sencillo código que incluiremos en un macro.

En el siguiente ejemplo, vamos a compactar una base de datos Access existente en una unidad F (en mi caso, la unidad F, corresponde a una llave USB), y dentro de una carpeta llamada hojas-de-calculo-en-excel (el nombre de este blog:


Sub compactar_base_de_datos()
'Si hay errores, que continúe
On Error Resume Next
'definimos la ruta y el nombre de la base de datos
ruta = "F:\hojas-de-calculo-en-excel\"
base = ruta & "base-de-datos.mdb"
'creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'si hemos informado de la base de datos...
If fso.FileExists(base) Then
'creamos el objeto JetEngine
Set oje = CreateObject("JRO.JetEngine")
'compactamos la base de datos,
'con una copia de respaldo

oje.CompactDatabase "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & _
base, "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & ruta & _
"base_temporal.mdb"
'copiamos reemplazando y borramos
'la copia de la base de datos

fso.CopyFile ruta & "base_temporal.mdb", base
fso.DeleteFile (ruta & "base_temporal.mdb")
'eliminamos el objeto
Set oje = Nothing
'creamos un mensaje para mostrarlo al final
mensaje = "La base de datos " & base & "," & _
Chr(13) & "ha sido compactada."
Else
'Si no existe la base de datos, o
'la ruta es incorrecta, mostraremos un
'mensaje distinto

mensaje = "Base de datos o ruta, incorrecta."
End If
'eliminamos el objeto
Set fso = Nothing
'mostramos el mensaje
MsgBox (mensaje)
End Sub

Si deseáis compactar una base de datos, que esté en la misma carpeta donde tengamos el fichero de excel, entonces sustituiremos la variable ruta del código anterior, para que nos quede definida de la siguiente forma:

ruta = ActiveWorkbook.Path & "\"

Creo que comentar algo más, respecto a este macro, es innecesario, pues el propio código fuente ya está comentado. Solo añadiré una observación. Si nuestra base de datos es relativamente pequeña (pongamos por ejemplo de 1 o 2 MB, aunque no hay que tomarlo al pie de la letra, pues puede ocurrir en una base de datos de mayor tamaño), entonces todas las operaciones se realizarán tan rápidamente que no podemos comprobar que realmente el macro hace todo lo que el propio código fuente dice hacer. Si nuestra base de datos es algo más grande (yo he utilizado una de 8 MB, para ver los efectos), entonces observaremos que efectivamente se crea una copia de la base de datos, y que una vez compactada la base de datos original, al final se borra la base de datos temporal.

Como la operación de compactación de la base de datos será más rápida, cuanto más pequeña sea la base de datos, todo ese procedimiento de compactación, copia, y borrado, no los podremos ver, por muy rápidos que cambiemos de ventana (de Excel, al explorador de archivos de Windows), si nuestra base de datos es relativamente pequeña, pues todo el procedimiento se realiza de una forma realmente rápida.



Importar datos concretos de un fichero de texto

A raíz el artículo en el que explicaba como leer un fichero de texto desde excel, un lector del blog, me preguntó como podía automatizar la tarea de leer solo una parte en concreto, de un fichero de texto. Como su pregunta no me quedaba clara, le dije que me mandara el fichero y que me dijera qué es lo que quería leer concretamente, y vería si era capaz de resolver su duda.

Cuando abrí el fichero, y leí lo que quería hacer, vi rápidamente la utilidad de escribir este artículo. Y en eso estamos. Para que entendáis la idea, imaginad un fichero de texto plano, generado por un software cualquiera, que tiene X líneas, pero de las cuales, nos interesa obtener solo unas cuantas, y además, de esas cuantas líneas, solo nos interesa una parte del texto comprendido en ellas. ¿Un poco lioso, verdad?. Bueno, vamos a verlo con el mismo ejemplo que me pasó el amigo Victor Fajardo, pues me ha autorizado a hacerlo público.

Imaginad que tenemos un fichero de texto como este (pantallazo de un fragmento del fichero), que contiene información topográfica:



Ahora la cuestión radica en poder obtener una serie de datos que hay en el fichero de texto. En concreto aquellos valores que indican el punto geodésico, la latitud sur, la longitud oeste, la altura elipsoidal, la coordenada norte, la coordenada este, la altura ortométrica, y el factor de escala de proyección.

Quizás te preguntes ¿y dónde está todo eso?. Pues esa misma pregunta me la hice yo, pero el amigo Fajardo, ya me la resolvía antes de planteársela. Me dijo que esos datos figuraban en el fichero de texto, y eran los que aparecían coloreados en la siguiente imagen:



Perfecto, ya iba entendiendo lo que quería hacer. Quería obtener una serie de datos que se repetían cada X líneas, para pasarlos a un fichero de excel, sin tener que hace un copiar y pegar, o sin tener que escribirlos a mano, mientras los leía en el fichero de texto. Vamos, que quería obtener una tabla de excel como esta:



Fantástico. Ya tenemos todos los datos necesarios para empezar a plantear el problema. Tenemos el fichero de texto, sabemos qué datos queremos obtener, y sabemos como queremos presentarlos. Lo único que nos queda es resolver el problema.

Cuando se nos presenta un problema de este tipo, lo primero que tenemos que hacer es pensar si el fichero de texto sigue un patrón de comportamiento común. Si lo tiene, nuestro problema se resuelve de una forma más sencilla.

Veamos si existe ese patrón de comportamiento. Lo primero que observamos, es que el fichero de texto tiene una cabecera donde nos aparece lo que puede ser el software que lo ha generado (haced doble clic en la primera imagen que aparece en este artículo, para poder ampliar la imagen del fichero de texto), y algunas líneas más de carácter general. Luego, un poco más abajo, vemos que hay una serie de bloques de texto, que contienen datos homogéneos. En concreto hay 8 líneas de texto, luego 1 línea en blanco, luego otras 8 líneas de texto, y así sucesivamente, hasta llegar al final del fichero.

Para poder obtener los datos relativos al punto geodésico, a la latitud sur, a la longitud oeste, a la altura elipsoidal, a la coordenada norte, a la coordenada este, a la altura ortométrica, y el factor de escala de proyección, podemos utilizar varias técnicas. Por ejemplo, para obtener la Longitud Oeste (el dato que hay detrás del texto "Lon: W"), a bote pronto, se me ocurre que lo más sencillo va a ser hacer lo siguiente, pues existe un patrón de comportamiento idéntico en los datos del fichero:

1.- Buscaremos en todas las líneas, aquellas que contengan el texto "Lon: W".

2.- Una vez hayamos encontrado ese texto, nos quedaremos con los siguientes 21 caracteres, pues a partir de ese carácter ya obtenemos otro dato diferente a la longitud oeste.

3.- Como además el amigo Victor Fajardo quería añadir los grados, minutos y segundos, deberemos descomponer esos 21 caracteres, en tres grupos. Caca uno de ellos estará separado por un espacio vacío, y contendrá un bloque de números. El primer grupo serán los que representaremos con grados, el segundo con los minutos, y el tercero con los segundos.

Pues bien, eso nos sirve para obtener el dato correspondiente a la longitud oeste, pero ¿y el resto de los datos?. Pues exactamente igual. Para el resto de datos que queramos importar, haremos exactamente lo mismo, pero seleccionando aquellos caracteres que nos definan las variables a importar. En lugar de "Lon: W", serán " hgt:", "n:", etc.

Para el caso concreto del primer dato que queremos obtener, es decir, el punto geodésico, como no hay ningún texto a partir del cual sepamos que lo que hay detrás es lo que nos sirve, yo he optado por hacer lo siguiente:

1.- Hemos buscado la palabra "(meters)", que está en la 2ª línea de esos bloques de los que hablábamos antes, y que estaban formados por 8 líneas de texto.

2.- Hemos retrocedido una línea, para pasar a la línea 1 (recordemos que "(meters)" está en la segunda línea).

3.- Nos hemos quedado con los 19 primeros caracteres de esa primera línea.

Como veis, solo es cuestión de buscar el patrón de comportamiento del fichero de texto, para a partir de él, obtener cada uno de los datos que necesitamos.

Y no hay más secreto... Bueno sí. Para ahorrarnos dolores de cabeza, lo primero que hemos hecho, ha sido importar todo el fichero de texto, en una zona de la hoja de cálculo que no nos moleste, y luego a partir de esos datos, es con los que hemos trabajado (el fichero de texto, una vez importado todo él, lo cerramos). Una vez tengamos todo listo, y bien bonito en nuestra hoja de cálculo, borraremos todos esos datos que nos han servido de borrador (todos los datos importados a lo bruto y que hemos escrito en una zona de la hoja de cálculo en la que no estorbe).

El código que nos permite importar todos esos datos, será este:


Sub leer_fichero_de_texto()
On Error Resume Next
'Ocultamos el procedimiento
Application.ScreenUpdating = False
'eliminamos todo lo que haya escrito desde A6 hasta abajo
Range("A6").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.EntireRow.Delete
'informamos del nombre del fichero de texto
fichero_de_texto = "datos.txt"
'Creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'Abrimos el fichero. Si el fichero de texto no está
'en la misma ruta que el fichero de excel donde tenemos
'este macro, deberemos poner la ruta de esta forma:
'Set archivo = fso.OpenTextFile("E:\excel\" & fichero_de_texto, 1)
'Como supondremos que tenemos el fichero de texto
'en la misma ruta, pondremos esto otro:

ruta = ActiveWorkbook.Path
Set archivo = fso.OpenTextFile(ruta & "\" & fichero_de_texto, 1)
'Cargamos en una variable todas las líneas
contenido = archivo.readall
'Cerramos el fichero
archivo.Close
'Limpiamos los objetos
Set fso = Nothing
Set archivo = Nothing
'creamos un array con los datos,
'y los separamos por los intros que haya
'en el fichero de texto (cada "intro" es
'una línea nueva). Tecla intro = vbCrLf

contenido = Split(contenido, vbCrLf)
'empezamos a escribir en la celda I6
Range("I6").Select
'Para todos los elementos del vector...
For i = 0 To UBound(contenido)
'Vamos escribiendo línea a línea
ActiveCell = contenido(i)
'Pasamos a la siguiente línea
ActiveCell.Offset(1, 0).Select
Next 'seguimos con el bucle
'fichamos la celda hasta la que hemos llegado
'para extraer la fila deonde estamos

fila_final = ActiveCell.Row
'Nos situamos en la columna I, a partir de I6
Range("I6").Select
'Ahora vamos a coger los datos que nos interesan,
'para todo el rango de filas que van desde la
'fila 1, hasta la fila_final

For i = 1 To fila_final
'fichamos la celda donde estamos, para volver a ella
celda = ActiveCell.Address
'******************************************
' PUNTO GEODÉSICO
'******************************************
'si la fila contiene la palabra " (meters) ", fichamos la fila de arriba
'que es la que contiene el punto geodésico

If InStr(LCase(ActiveCell), " (meters) ") > 1 Then
'cogemos los 19 primeros caracteres, pues a partir
'del 20, ya aparece la fecha y la hora

punto_geodesico = Left(ActiveCell.Offset(-1, 0), 19)
'Escribimos los datos a partir de A6
Range("A6").Select
'Escribimos el título
ActiveCell = "PUNTO GEODÉSICO"
'lo ponemos en negrita, subrayado, y en rojo
With Selection
.Font.Bold = True
.Font.Underline = xlUnderlineStyleSingle
.Font.ColorIndex = 3
End With
'buscamos la primera celda vacía
Do While Not IsEmpty(ActiveCell)
ActiveCell.Offset(1, 0).Select
Loop
'y escribimos el dato x
ActiveCell = punto_geodesico
End If
'volvemos donde estábamos
Range(celda).Select
'******************************************
' LATITUD SUR
'******************************************
'si la fila contiene la palabra " Lat: S ", fichamos la fila
'que contiene la latitud sur

If InStr(LCase(ActiveCell), " lat: s ") > 1 Then
'cogemos desde el caracter 35, hasta el 56,
'es decir, 21 caracteres más, a partir del 35,
'con "trim" quitaremos los espacios vacíos de delante y detrás

lati_sur = Trim(Mid(ActiveCell, 35, 21))
'descomponemos el valor en los tres elementos,
'con el delimitador de espacio " "

latitud_sur = Split(lati_sur, " ")
'añadimos los grados, minutos y segundos, a cada elemento (el 0, el 1, y el 2)
'pues los elemenos del vector, empiezan por 0
latitud_sur = latitud_sur(0) & "º" & latitud_sur(1) & "'" & latitud_sur(2) & """"
'Escribimos los datos a partir de B6
Range("B6").Select
'Escribimos el título
ActiveCell = "LATITUD SUR"
'lo ponemos en negrita, subrayado, y en rojo
With Selection
.Font.Bold = True
.Font.Underline = xlUnderlineStyleSingle
.Font.ColorIndex = 3
End With
'buscamos la primera celda vacía
Do While Not IsEmpty(ActiveCell)
ActiveCell.Offset(1, 0).Select
Loop
'y escribimos el dato x
ActiveCell = latitud_sur
End If
'volvemos donde estábamos
Range(celda).Select
'******************************************
' LONGITUD OESTE
'******************************************
'si la fila contiene la palabra " Lon: W ", fichamos la fila
'que contiene la longitud oeste

If InStr(LCase(ActiveCell), " lon: w ") > 1 Then
'cogemos desde el caracter 35, hasta el 56,
'es decir, 21 caracteres más, a partir del 35,
'con "trim" quitaremos los espacios vacíos de delante y detrás

long_oeste = Trim(Mid(ActiveCell, 35, 21))
'descomponemos el valor en los tres elementos,
'con el delimitador de espacio " "

longitud_oeste = Split(long_oeste, " ")
'añadimos los grados, minutos y segundos, a cada elemento (el 0, el 1, y el 2)
'pues los elemenos del vector, empiezan por 0
longitud_oeste = longitud_oeste(0) & "º" & longitud_oeste(1) & "'" & longitud_oeste(2) & """"
'Escribimos los datos a partir de C6
Range("C6").Select
'Escribimos el título
ActiveCell = "LONGITUD OESTE"
'lo ponemos en negrita, subrayado, y en rojo
With Selection
.Font.Bold = True
.Font.Underline = xlUnderlineStyleSingle
.Font.ColorIndex = 3
End With
'buscamos la primera celda vacía
Do While Not IsEmpty(ActiveCell)
ActiveCell.Offset(1, 0).Select
Loop
'y escribimos el dato x
ActiveCell = longitud_oeste
End If
'volvemos donde estábamos
Range(celda).Select
'******************************************
' ALTURA ELIPSOIDAL
'******************************************
'si la fila contiene la palabra " Hgt: ", fichamos la fila
'que contiene la altura elipsoidal

If InStr(LCase(ActiveCell), " hgt: ") > 1 Then
'cogemos desde el caracter 35, hasta el 56,
'es decir, 21 caracteres más, a partir del 35,
'con "trim" quitaremos los espacios vacíos de delante y detrás

altura_elipsoidal = Trim(Mid(ActiveCell, 35, 21))
'Escribimos los datos a partir de D6
Range("D6").Select
'Escribimos el título
ActiveCell = "ALTURA ELIPS."
'lo ponemos en negrita, subrayado, y en rojo
With Selection
.Font.Bold = True
.Font.Underline = xlUnderlineStyleSingle
.Font.ColorIndex = 3
End With
'buscamos la primera celda vacía
Do While Not IsEmpty(ActiveCell)
ActiveCell.Offset(1, 0).Select
Loop
'y escribimos el dato x
ActiveCell = altura_elipsoidal
End If
'volvemos donde estábamos
Range(celda).Select
'******************************************
' COORDENADA NORTE
'******************************************
'si la fila contiene la palabra " N: ", fichamos la fila
'que contiene la coordenada norte

If InStr(LCase(ActiveCell), " n: ") > 1 Then
'cogemos desde el caracter 58, hasta el 71,
'es decir, 13 caracteres más, a partir del 58,
'con "trim" quitaremos los espacios vacíos de delante y detrás

coordenada_norte = Trim(Mid(ActiveCell, 58, 13))
'Escribimos los datos a partir de E6
Range("E6").Select
'Escribimos el título
ActiveCell = "COORD. NORTE"
'lo ponemos en negrita, subrayado, y en rojo
With Selection
.Font.Bold = True
.Font.Underline = xlUnderlineStyleSingle
.Font.ColorIndex = 3
End With
'buscamos la primera celda vacía
Do While Not IsEmpty(ActiveCell)
ActiveCell.Offset(1, 0).Select
Loop
'y escribimos el dato x
ActiveCell = coordenada_norte
End If
'volvemos donde estábamos
Range(celda).Select
'******************************************
' COORDENADA ESTE
'******************************************
'si la fila contiene la palabra " E: ", fichamos la fila
'que contiene la coordenada este

If InStr(LCase(ActiveCell), " e: ") > 1 Then
'cogemos desde el caracter 58, hasta el 71,
'es decir, 13 caracteres más, a partir del 58,
'con "trim" quitaremos los espacios vacíos de delante y detrás

coordenada_este = Trim(Mid(ActiveCell, 58, 13))
'Escribimos los datos a partir de F6
Range("F6").Select
'Escribimos el título
ActiveCell = "COORD. ESTE"
'lo ponemos en negrita, subrayado, y en rojo
With Selection
.Font.Bold = True
.Font.Underline = xlUnderlineStyleSingle
.Font.ColorIndex = 3
End With
'buscamos la primera celda vacía
Do While Not IsEmpty(ActiveCell)
ActiveCell.Offset(1, 0).Select
Loop
'y escribimos el dato x
ActiveCell = coordenada_este
End If
'volvemos donde estábamos
Range(celda).Select
'******************************************
' ALTURA ORTOMÉTRICA
'******************************************
'si la fila contiene la palabra " Orth: ", fichamos la fila
'que contiene la altura ortométrica

If InStr(LCase(ActiveCell), " orth: ") > 1 Then
'cogemos desde el caracter 61, hasta el 71,
'es decir, 10 caracteres más, a partir del 61,
'con "trim" quitaremos los espacios vacíos de delante y detrás

altura_ortometrica = Trim(Mid(ActiveCell, 61, 10))
'Escribimos los datos a partir de G6
Range("G6").Select
'Escribimos el título
ActiveCell = "ALTURA ORTO."
'lo ponemos en negrita, subrayado, y en rojo
With Selection
.Font.Bold = True
.Font.Underline = xlUnderlineStyleSingle
.Font.ColorIndex = 3
End With
'buscamos la primera celda vacía
Do While Not IsEmpty(ActiveCell)
ActiveCell.Offset(1, 0).Select
Loop
'y escribimos el dato x
ActiveCell = altura_ortometrica
End If
'volvemos donde estábamos
Range(celda).Select
'******************************************
' FACTOR DE ESCALA DE PROYECCIÓN
'******************************************
'si la fila contiene la palabra " Grid Scale: ", fichamos la fila
'que contiene el factor de escala de proyección

If InStr(LCase(ActiveCell), " grid scale: ") > 1 Then
'cogemos desde el caracter 67, hasta el 79,
'es decir, 12 caracteres más, a partir del 79,
'con "trim" quitaremos los espacios
'vacíos de delante y detrás

factor_escala = Trim(Mid(ActiveCell, 67, 12))
'Escribimos los datos a partir de H6
Range("H6").Select
'Escribimos el título
ActiveCell = "FACTOR ESCALA PROY."
'lo ponemos en negrita, subrayado, y en rojo
With Selection
.Font.Bold = True
.Font.Underline = xlUnderlineStyleSingle
.Font.ColorIndex = 3
End With
'buscamos la primera celda vacía
Do While Not IsEmpty(ActiveCell)
ActiveCell.Offset(1, 0).Select
Loop
'y escribimos el dato x
ActiveCell = factor_escala
End If
'volvemos donde estábamos
Range(celda).Select
'******************************************
'ahora bajamos una fila

ActiveCell.Offset(1, 0).Select
Next 'continuamos con el bucle
'finalmente eliminamos los datos que
'habíamos escrito en la columna I

Columns("I:I").Select
Selection.Delete Shift:=xlToLeft
'nos situamos en A6
Range("A6").Select
'Mostramos el procedimiento
Application.ScreenUpdating = True
End Sub


Quizás ese código puede que os parezca un poco largo, pero en realidad, como estamos usando la misma técnica para importar la latitud sur, la longitud oeste, la altura elipsoidal, etc., pues nos bastará con copiar y pegar determinadas partes del código, para retocarla posteriormente.

Una vez ejecutemos ese macro (con el fichero de texto llamado "datos.txt", y en la misma carpeta que el fichero de excel), obtendremos esto:



Con este macro, lo que conseguimos es que cada vez que tengamos que trabajar con el fichero de tiempo, no perdamos horas (da igual lo largo que sea el fichero de texto), haciendo un copiar y pegar, o bien transcribiendo a mano los datos del fichero de texto, directamente en excel. Ahora con tan solo pulsar un botón en la hoja de cálculo, obtendremos los datos deseados del fichero de texto.

Si deseáis ver esto con vuestros propios ojos, desde aquí podéis descargar un fichero comprimido que contiene tanto la hoja de cálculo, como el fichero de texto que he utilizado en este artículo.



Listar los archivos de un directorio

Hoy trataré de explicar, como obtener el nombre de todos los ficheros existentes en un directorio. Esto nos puede servir para muchas cosas, por ejemplo, para saber existe o no determinado fichero, o para grabar un fichero y que no se grabe con el mismo nombre de otro ya existente. Con independencia de la utilidad que queramos darle, estoy seguro que nos puede ser útil en más de una ocasión.

Para que este macro funcione, tendremos que tener grabado el fichero en nuestro disco duro. El código para devolvernos los ficheros existentes en la misma carpeta donde hayamos grabado el fichero, es el que aparece a continuación. El nombre de los ficheros, lo imprimiremos en la propia hoja de cálculo, aunque podemos imprimirlos en un combobox, por ejemplo:


Sub ficheros_del_directorio()
'Si hay errores, que continúe
On Error Resume Next
'Ocultamos el procedimiento
Application.ScreenUpdating = False
'Creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'Informamos de la ruta de donde vamos a obtener
'los ficheros, en este caso, el mismo directorio
'donde tengamos grabado este fichero con el macro

ruta = ActiveWorkbook.Path
'definimos dos variables que necesitaremos,
'para recuperar el nombre de la carpeta, y
'los ficheros que haya dentro

Set directorio = fso.GetFolder(ruta)
Set ficheros = directorio.Files
'escribimos un encabezado en la celda A6
Range("A6").Select
ActiveCell = "Ficheros del directorio:"
'lo ponemos en negrita y subrayado
ActiveCell.Font.Bold = True
ActiveCell.Font.Underline = xlUnderlineStyleSingle
'escribimos los ficheros, a partir de A7
Range("A7").Select
For Each archivo In ficheros
'escribimos el nombre del fichero
ActiveCell = archivo.Name
'bajamos una fila
ActiveCell.Offset(1, 0).Select
Next
'Limpiamos los objetos
Set fso = Nothing
Set directorio = Nothing
Set ficheros = Nothing
'Mostramos el procedimiento
Application.ScreenUpdating = True
End Sub

Si lo que deseamos es escribir los ficheros del directorio, pero sin incluir el nombre del fichero activo, es decir, del fichero desde donde estamos ejecutando el macro -porque por supuesto, al estar en el directorio, se listará-, entonces deberemos cambiar el bucle For Each anterior, por este otro que os incluyo a continuación, y que incorpora un condicional (el resto del código es el mismo, así que me ahorro el copiar y pegar). Esto solo nos servirá si el directorio del cual consultamos los ficheros, es el mismo donde tenemos guardado el fichero que ejecuta el macro, es decir, si se trata del mismo path:

For Each archivo In ficheros
'escribimos el nombre del fichero
If archivo.Name <> ActiveWorkbook.Name Then
ActiveCell = archivo.Name
'bajamos una fila
ActiveCell.Offset(1, 0).Select
End If
Next

Si lo que deseamos es listar solo los nombres de los subdirectorios existentes en el directorio donde tenemos guardado el fichero activo, entonces el macro que tendremos que utilizar, será este:

Sub subdirectorios_del_directorio()
'Si hay errores, que continúe
On Error Resume Next
'Ocultamos el procedimiento
Application.ScreenUpdating = False
'Creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'Informamos de la ruta de donde vamos a obtener
'los ficheros, en este caso, el mismo directorio
'donde tengamos grabado este fichero con el macro

ruta = ActiveWorkbook.Path
'definimos dos variables que necesitaremos,
'para recuperar el nombre de la carpeta, y
'los subdirectorios que haya dentro

Set directorio = fso.GetFolder(ruta)
Set subdirectorios = directorio.subfolders
'escribimos un encabezado en la celda C6
Range("C6").Select
ActiveCell = "Subdirectorios del directorio:"
'lo ponemos en negrita y subrayado
ActiveCell.Font.Bold = True
ActiveCell.Font.Underline = xlUnderlineStyleSingle
'escribimos los subdirectorios
ActiveCell.Offset(1, 0).Select
For Each subdirectorio In subdirectorios
'escribimos el nombre del subdirectorio
ActiveCell = subdirectorio.Name
'bajamos una fila
ActiveCell.Offset(1, 0).Select
Next
'Limpiamos los objetos
Set fso = Nothing
Set directorio = Nothing
Set subdirectorios = Nothing
'Mostramos el procedimiento
Application.ScreenUpdating = True
End Sub

Si lo que deseamos es listar los nombres de los subdirectorios y los ficheros existentes en el directorio donde tenemos guardado el fichero activo, entonces el macro que tendremos que utilizar, será este:

Sub ficheros_y_subdirectorios_del_directorio()
'Si hay errores, que continúe
On Error Resume Next
'Ocultamos el procedimiento
Application.ScreenUpdating = False
'Creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'Informamos de la ruta de donde vamos a obtener
'los ficheros, en este caso, el mismo directorio
'donde tengamos grabado este fichero con el macro

ruta = ActiveWorkbook.Path
'definimos dos variables que necesitaremos,
'para recuperar el nombre de la carpeta, y
'los subdirectorios y ficheros que haya dentro

Set directorio = fso.GetFolder(ruta)
Set subdirectorios = directorio.subfolders
Set ficheros = directorio.Files
'escribimos un encabezado en la celda D6
Range("D6").Select
ActiveCell = "Subdirectorios del directorio:"
'lo ponemos en negrita y subrayado
ActiveCell.Font.Bold = True
ActiveCell.Font.Underline = xlUnderlineStyleSingle
'escribimos los subdirectorios
ActiveCell.Offset(1, 0).Select
For Each subdirectorio In subdirectorios
'escribimos el nombre del subdirectorio
ActiveCell = subdirectorio.Name
'bajamos una fila
ActiveCell.Offset(1, 0).Select
Next
'a continuación escribimos los ficheros
'pero antes, escribiremos el encabezado

ActiveCell = "Ficheros del directorio:"
'lo ponemos en negrita y subrayado
ActiveCell.Font.Bold = True
ActiveCell.Font.Underline = xlUnderlineStyleSingle
'pasamos a la siguiente fila
ActiveCell.Offset(1, 0).Select
For Each archivo In ficheros
'escribimos el nombre del fichero
ActiveCell = archivo.Name
'bajamos una fila
ActiveCell.Offset(1, 0).Select
Next
'Limpiamos los objetos
Set fso = Nothing
Set directorio = Nothing
Set subdirectorios = Nothing
Set ficheros = Nothing
'Mostramos el procedimiento
Application.ScreenUpdating = True
End Sub


Desde aquí podéis descargar el fichero de excel, con el ejemplo que os presento en este artículo.



Mostrar imágenes asociadas a un desplegable

Quizás te hayas preguntado alguna vez, como podrías seleccionar un dato de un desplegable, y que al lado salga su foto, es decir, que salga la foto asociada al elemento seleccionado del desplegable. Esto es especialmente útil por ejemplo, si tenemos un listado desplegable de productos o de empleados, y queremos que al seleccionar cada empleado o artículo, nos salga al lado la foto de cada uno de ellos.

¿Verdad que parece un tema interesante?. Pues nada, vamos a tratar de explicarlo, porque no tiene mucho secreto.

Lo primero que tendremos que hacer, es que las imágenes (fotos de los empleados, de los productos, etc.) sean todas del mismo tamaño, para que no se muestren unas imágenes grandes y otras pequeñas, y nos quede poco elegante. Además, es importante que las fotos no tengan espacios vacíos, ni acentos (tampoco los elementos del desplegable), para que los usuarios de todos los países puedan utilizar el código sin problemas. En lugar de utilizar espacios vacíos, usaremos guiones. Por ejemplo, si tenemos un listado de artículos, y hay uno que se llama Bidones de 50 litros, a la foto le pondremos como nombre Bidones-de-50-litros.jpg. Lo recomendable -para tener un código bastante simple, como el que os muestro en este artículo-, es que las fotos tengan todas la misma extensión, …o todas "jpg", o todas "gif", o todas "png", etc. En el caso de que utilicéis otra extensión distinta a "jpg", tendréis que cambiar la línea del código fuente que veréis al final de este artículo, para que en su lugar aparezca "gif", o la extensión que utilicéis para vuestras imágenes.

Las fotos y el libro de excel con el desplegable, los guardaremos en la misma carpeta o directorio. Si estuvieran en directorios diferentes, deberíamos modificar el código VBA para informar de la ruta correcta de las imágenes.

Vamos a explicarlo con un ejemplo. Yo he utilizado una serie de vehículos clásicos (de los cuales soy un apasionado, todo sea dicho de paso), de tal forma que cada vez que seleccionemos uno de los coches, nos saldrá debajo su foto correspondiente. Si por ejemplo seleccionases en el desplegable, el Golf GTI mk1, aparecería esto en tu pantalla:


Y si seleccionases por ejemplo el Seat 124 Sport, aparecería esto de forma automática:


Lo primero será crear en la Hoja2, un listado con los coches que tenemos. En este caso tenemos 5 vehículos, tal y como se muestra en la imagen siguiente:


Ahora, le pondremos un nombre al rango de datos comprendido entre B4 y B8, para lo que seleccionaremos ese rango, y definiremos un nombre para el mismo. En este caso, le he puesto como nombre coches, tal y como se puede ver en la zona enmarcada en rojo, en la siguiente imagen (para definir un nombre, la forma más sencilla es escribir el nombre en esa zona enmarcada en rojo, y pulsar intro):


Ahora ya sabemos que el rango de celdas que hay entre B4 y B8 de la Hoja2, se llama coches.

A continuación pasaremos a la Hoja1, y por ejemplo, en la celda C4, crearemos la lista desplegable con los coches, para que cada vez que seleccionemos un modelo, nos salga debajo su foto correspondiente. Para ello, seleccionaremos en el menú Datos, la opción Validación…, y en la pestaña Configuración, que es la que nos saldrá por defecto en primer plano, elegiremos en Permitir la opción Lista, y en Origen introduciremos el nombre definido anteriormente, precedido del signo igual, tal y como se muestra en la siguiente imagen:


De tal forma que tendremos algo como esto que se muestra en la siguiente imagen, al pulsar en la flechita del desplegable:


Una vez hecho esto, solo nos quedará copiar y pegar el código que hará que se nos muestre la foto, pero no lo incluiremos en un módulo, sino dentro de la Hoja1, tal y como se muestra en esta imagen:


El código que tenemos que copiar y pegar en la Hoja1, es este:


Private Sub Worksheet_Change(ByVal Target As Range)
'Si ha errores, que continúe
On Error Resume Next
'Si cambiamos el dato de la celda C4,
'mostramos la foto de ese vehículo

If Target.Cells = Range("C4") Then
'Ocultamos el procedimiento
Application.ScreenUpdating = False
'pasamos a una variable, el nombre de la foto,
'que será el mismo que el nombre del coche, pero
'separado con guiones, y sin acentos, para que
'todos los usuarios puedan verlo correctamente

foto = Range("C4").Value
'en la foto, reemplazamos los espacios, por guiones
foto = Replace(foto, " ", "-")
'ahora le añadimos la extensión "jpg"
foto = foto & ".jpg"
'ahora buscamos la foto en el mismo directorio
'donde tenemos este fichero de excel

rutayarchivo = ActiveWorkbook.Path & "\" & foto
'borramos la foto del coche (si hubiera alguna)
Me.Shapes("foto_del_coche").Delete
'creamos el objeto fotografia, con la foto insertada
Set fotografia = Me.Pictures.Insert(rutayarchivo)
'haremos que la foto ocupe desde B6 hasta D21,
'para que no salgan fotos supergrandes, o
'superpequeñas, y salgan más "normalitas"

With Range("B6:D21")
Arriba = .Top
Izquierda = .Left
Ancho = .Offset(0, .Columns.Count).Left - .Left
Alto = .Offset(.Rows.Count, 0).Top - .Top
End With
'le ponemos un nombre al objeto "fotografia"
'para poder borrarla cuando cambie la celda D6
'(ver que borramos la foto que hubiese, antes de insertar la nueva)

With fotografia
.Name = "foto_del_coche"
.Top = Arriba
.Left = Izquierda
.Width = Ancho
.Height = Alto
End With
'eliminamos el objeto
Set fotografia = Nothing
'ponemos todo como estaba
Application.ScreenUpdating = True
End If
End Sub

Ese código lo que hace es insertar la foto, de cada uno de los coches, cada vez que seleccionemos uno de ellos. Para ello, debemos tener presente una serie de variables que he utilizado, para simplificar el código VBA:

1.- Las fotos y el fichero deben estar en el mismo directorio.
2.- Las fotos y los modelos de vehículos, deben tener el mismo nombre (en ambos casos sin acentos, y en el caso de las fotos, sustituyendo los espacios por guiones).
3.- Las fotos deben tener todas la misma extensión (en el código verás que aparece por ahí la extensión ".jpg").

Desde aquí podéis descargar el fichero de excel, con el ejemplo que os presento en este artículo, junto con las imágenes de los vehículos clásicos, para que podáis ver en funcionamiento como se crean listas de validación con imágenes asociadas a cada elemento del desplegable.



Saber si existe un fichero

El objeto FileSystemObject, lo hemos visto ya en algunos de los artículos de este blog, pero hoy nuevamente, vamos a ver algunas de sus posibilidades. Con el objeto FileSystemObject, podemos hacer muchas cosas, en realidad muchas más de las que veréis en este blog. Muchas de esas cosas no las explicaré, porque pueden ser utilizadas de forma maliciosa por parte de algunos usuarios.

En esta entrega, vamos a ver como podemos averiguar si existe determinado fichero, ya sea de excel, o de cualquier otro tipo (una imagen, un archivo de word, un archivo pdf, etc.), sin necesidad de buscarlo de forma " manual " a través del explorador de archivos.

Aquí os dejo el código necesario, para determinar si existe un fichero. Lo copiaremos en un módulo VBA:


Sub comprobar_si_existe_el_fichero()
'Creamos el objeto FileSystemObject
Set archivo = CreateObject("Scripting.filesystemObject")
'Creamos una variable para que el usuario introduzca la
'ruta y el nombre del archivo a buscar

ruta_y_archivo = InputBox("Introduce la ruta y el nombre del archivo" & _
Chr(13) & Chr(13) & Chr(13) & Chr(13) & Chr(13) & "Por ejemplo:", _
"Ruta y archivo", "C:\Mis documentos\ejemplo.xls")
'Comprobaremos si existe o no el archivo, y dependiendo
'del resultado, mostraremos un mensaje u otro

If ruta_y_archivo <> "" Then
If archivo.FileExists(ruta_y_archivo) Then
'Si el fichero existe, mostramos un mensaje
respuesta = MsgBox("El archivo sí que existe.", vbInformation, "Resultado")
Else
'Si el fichero no existe, mostramos otro mensaje distinto
respuesta = MsgBox("El archivo no existe.", vbInformation, "Resultado")
End If
End If
'limpiamos el objeto
Set archivo = Nothing
End Sub

Al ejecutar ese macro, se nos mostrará un InputBox, donde deberemos introducir la ruta y el nombre del fichero cuya existencia queremos averiguar:



Y una vez hayamos pulsado sobre el botón de aceptar, nos mostrará un mensaje a través de un MsgBox, tanto si existe como si no, el fichero en cuestión (en la siguiente imagen se muestra el mensaje, en el caso de existir el fichero buscado):



Leer un fichero de texto plano

En una entrega anterior, vimos como grabar los datos de una hoja de cálculo en un fichero de texto plano (fichero con extensión "txt"), a través de un macro, de tal forma que el fichero de texto era el destino de nuestros datos. En esta ocasión el fichero de datos será nuestro origen de datos, y lo que haremos será leerlo, sin necesidad de abrirlo directamente (esto no es del todo cierto, porque en realidad tendremos que abrir el fichero, pero esta apertura no será visible, ya que se realizará de forma "interna", a nivel de código de programación).

Para explicarlo más claramente, vamos a suponer que tenemos un fichero de texto plano llamado inversiones 2008.txt, alojado en la misma carpeta donde tenemos el fichero de excel desde el cual queremos leer el texto, y que ese fichero de texto plano presenta los siguientes datos:


Y que queremos leer la línea 5 del fichero, es decir, la línea donde aparece esto:

Instalaciones técnicas               178.000,00

Para poder leer la línea 5 de ese fichero de texto, copiaremos y pegaremos este código en un módulo VBA (como veis, el código está comentado para aclarar las posibles dudas que podíais tener):





Sub leer_linea_de_un_fichero_de_texto()
On Error Resume Next
'Ocultamos el procedimiento
Application.ScreenUpdating = False
'informamos del nombre del fichero de texto
fichero_de_texto = "inversiones 2008.txt"
'Creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'Abrimos el fichero. Si el fichero de texto no está
'en la misma ruta que el fichero de excel donde tenemos
'este macro, deberemos poner la ruta, como en este ejemplo:
'Set archivo = fso.OpenTextFile("C:\Mis documentos\" & fichero_de_texto, 1)
'Como supondremos que tenemos el fichero de texto
'en la misma ruta, pondremos esto otro:

ruta = ActiveWorkbook.Path
Set archivo = fso.OpenTextFile(ruta & "\" & fichero_de_texto, 1)
'Cargamos en una variable la línea que queremos leer.
'En el ejemplo, leemos la línea 5, para lo cual nos
'saltaremos las 4 primeras líneas del fichero.

For i = 1 To 4
'nos saltamos 4 líneas
archivo.Skipline
Next 'seguimos con el bucle
'Leemos la línea 5
contenido = archivo.readline()
'Cerramos el fichero
archivo.Close
'Limpiamos los objetos
Set fso = Nothing
Set archivo = Nothing
'Escribimos la línea 5 del fichero en la celda A13
Range("A13") = contenido
'Mostramos el procedimiento
Application.ScreenUpdating = True
End Sub

Pero quizás estés pensando "no, no, …no me interesa leer una línea determinada de un fichero de texto, porque lo que quiero es leer el fichero entero, …¿es esto posible?". Por supuesto que es posible. Si lo que queréis es leer el fichero entero, tan solo tendréis que copiar y pegar este otro macro en un módulo VBA:





Sub leer_fichero_de_texto()
On Error Resume Next
'Ocultamos el procedimiento
Application.ScreenUpdating = False
'informamos del nombre del fichero de texto
fichero_de_texto = "inversiones 2008.txt"
'Creamos el objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'Abrimos el fichero. Si el fichero de texto no está
'en la misma ruta que el fichero de excel donde tenemos
'este macro, deberemos poner la ruta, como en este ejemplo:
'Set archivo = fso.OpenTextFile("C:\Mis documentos\" & fichero_de_texto, 1)
'Como supondremos que tenemos el fichero de texto
'en la misma ruta, pondremos esto otro:

ruta = ActiveWorkbook.Path
Set archivo = fso.OpenTextFile(ruta & "\" & fichero_de_texto, 1)
'Cargamos en una variable todas las líneas
contenido = archivo.readall
'Cerramos el fichero
archivo.Close
'Limpiamos los objetos
Set fso = Nothing
Set archivo = Nothing
'creamos un array con los datos,
'y los separamos por los intros que haya
'en el fichero de texto (cada "intro" es
'una línea nueva). Tecla intro = vbCrLf

contenido = Split(contenido, vbCrLf)
'empezamos a escribir en la celda A1
Range("A1").Select
'Para todos los elementos del vector...
For i = 0 To UBound(contenido)
'Vamos escribiendo línea a línea
ActiveCell = contenido(i)
'Pasamos a la siguiente línea
ActiveCell.Offset(1, 0).Select
Next 'seguimos con el bucle
'Mostramos el procedimiento
Application.ScreenUpdating = True
End Sub

A partir de ahora, leer un fichero de texto plano, ya no tendrá secretos para vosotros :-)

Desde aquí podéis descargar el fichero de excel junto con el fichero de texto, donde tenéis el ejemplo funcionando.



Crear carpetas desde excel

¿Sabíais que desde excel, y desde muchas otras aplicaciones, se pueden crear carpetas?. Sí, sí, tal y como lo oyes, crear carpetas, es decir, directorios.

Imaginemos que tenemos estos datos en nuestra hoja de cálculo:



Si queremos crear tantas carpetas como nombres hayamos incluido en nuestra hoja de excel, tan solo tendremos que ejecutar este macro:


Sub Crear_carpetas()
'Ocultamos el procedimiento
Application.ScreenUpdating = False
'llamamos al objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")
'pasamos a una variable, la ruta donde se encuentra el
'fichero de excel donde se está ejecutando este código

ruta = ActiveWorkbook.Path
'seleccionamos la primera celda que contiene
'los nombres de las carpetas (suponemos que es A4)

Range("A4").Select
'recorremos toda la columna, hasta que encuentre una fila vacía, y
'por cada fila con texto, crearemos una carpeta con el nombre
'de ese mismo texto

Do While Not IsEmpty(ActiveCell)
'si el fichero no existe, entonces lo creamos
If Not fso.FolderExists(ruta & "\" & ActiveCell.Value) Then
fso.CreateFolder (ruta & "\" & ActiveCell.Value)
End If
'pasamos a la fila siguiente, y volvemos a recorrer el bucle
ActiveCell.Offset(1, 0).Select
Loop
'limpiamos el objeto
Set fso = Nothing
'Mostramos el procedimiento
Application.ScreenUpdating = True
End Sub


Ejecutando el macro del ejemplo, se nos habrán creado 3 directorios en la misma carpeta donde tengamos guardado el fichero con el macro, y esos directorios se llamarán: pepe, luis, y antonio.

Las limitaciones son dos:

1.- Los nombres que tengamos en la hoja de excel (en nuestro ejemplo tenemos 3 nombres de personas), deben estar de forma continua, es decir, sin filas en blanco entre ellos.

2.- Las carpetas se crean en el mismo directorio donde se encuentre el fichero donde vas a ejecutar el macro, por lo que deberás tenerlo previamente guardado en tu ordenador, ya sea en tu carpeta de Mis Documentos, o donde desees. Será en ese directorio donde se creen esas nuevas carpetas.