Exportar (0) Imprimir
Expandir todo
Expandir Minimizar

El rincón del desarrollador de bases de datos: Trabajo con archivos y directorios (Hardcore Visual Basic 2004)

Puede que en el artículo se incluyan direcciones URL que eran correctas en la fecha de publicación, pero que ahora hagan referencia a sitios o páginas inexistentes. Para conservar la fluidez, se han mantenido estas direcciones en el texto, aunque se han desactivado los vínculos.

Publicado: 18 de Julio de 2005

Carl Ganz, Jr.

Hasta VB6 incluido, la sintaxis de programación necesaria para trabajar con directorios y archivos de disco ha permanecido prácticamente intacta desde los días del GW-BASIC. (¿Recuerda la primera vez que programó un OPEN "xx" FOR INPUT?) .NET Framework ofrece clases que proporcionan toda la funcionalidad que siempre ha estado disponible para leer y escribir archivos, pero además agrega características y una interfaz de objetos adecuada. Este mes, Carl Ganz nos muestra cómo utilizar las herramientas de .NET para trabajar con los datos almacenados en los archivos de disco.

En esta página

El rincón del desarrollador de bases de datos: Trabajo con archivos y directorios El rincón del desarrollador de bases de datos: Trabajo con archivos y directorios
Barra lateral: Un vistazo a VS 2005 Barra lateral: Un vistazo a VS 2005

El rincón del desarrollador de bases de datos: Trabajo con archivos y directorios

Si comienza a explorar las clases del espacio de nombres System.IO que realizan las funciones de archivos y directorios de bajo nivel, no tardará en descubrir que con frecuencia existen varias clases que realizan la misma función. Las clases Directory y File tienen miembros compartidos que realizan la mayoría de las tareas habituales. "Compartidos" se refiere sencillamente al hecho de que no es necesario crear explícitamente instancias de las clases; basta con llamar directamente a sus métodos, como vemos en estos ejemplos:

File.Copy("source.txt", "destination.txt")

y:

File.Move("c:\here\myfile.txt", "c:\there\myfile.txt")

Todos estamos familiarizados con la posibilidad de leer y escribir datos ASCII y, aunque todos los RDBMS incluyen sus propias utilidades para admitir la importación y exportación de archivos ASCII (por ejemplo, SQL Server ofrece Servicios de transformación de datos (DTS), mientras que Oracle ofrece SQL Loader), a veces se prefiere leer todo el contenido de un archivo o procesarlo por registro. La lista 1 muestra cómo utilizar un cuadro de diálogo para seleccionar y abrir un archivo de texto, leer todo su contenido en una variable y mostrarla en un cuadro de mensaje.

Lista 1. Lectura de todo el contenido de un archivo de texto ASCII

Dim oOpenFileDialog As OpenFileDialog
Dim oStreamReader As StreamReader
Dim szAllFileData As String
Dim szRecord As String
oOpenFileDialog = New OpenFileDialog
With oOpenFileDialog
   .DefaultExt = "txt"
   .Filter = "txt files (*.txt)|*.txt"
   .InitialDirectory = "C:\"
   .ShowDialog()

End With
oStreamReader = _
   New StreamReader(oOpenFileDialog.FileName)
szAllFileData = oStreamReader.ReadToEnd()
oStreamReader.Close()
MessageBox.Show (szAllFileData)

El objeto StreamReader encapsula una referencia al archivo, mientras que el método ReadToEnd extrae todo su contenido en una variable. Aunque podría haber utilizado el método File.OpenText, que hubiera devuelto un objeto StreamReader, para este ejemplo he preferido crear una instancia del objeto StreamReader directamente. Las clases Stream representan las secuencias de bytes que proceden de un soporte de almacenamiento o se traslada a él. Por lo general este soporte es un archivo, aunque podría tratarse de un puerto de comunicación o cualquier otro tipo de dispositivo físico o virtual. Vea en la barra lateral cómo VS 2005 hace la vida más sencilla.

La lista 2 muestra un ejemplo bidireccional en el que los objetos StreamReader y StreamWriter trabajan conjuntamente para abrir un archivo de texto. StreamReader lee los datos línea a línea, mientras que StreamWriter escribe los datos en otro archivo simultáneamente y crea una copia exacta del original. Como está leyendo los datos por registro, puede modificar fácilmente la técnica básica para realizar cualquier limpieza de datos que sea necesaria (por ejemplo, convertir los datos de mayúsculas a tipo título o analizar la información de nombre y dirección para convertirla en elementos de datos individuales). El método Split de la clase String analizará el texto delimitado en elementos de matrices individuales. Normalmente, StreamWriter también crearía un registro de errores que documentaría los problemas.

Lista 2. Lectura y escritura en un archivo de texto

Dim oStreamReader As StreamReader
Dim oStreamWriter As StreamWriter
Dim szRecord As String
Dim aData As Array
'Open an existing file
oStreamReader = New StreamReader_
   (Application.StartupPath + "\datafile.txt")
'Create a new destination file
oStreamWriter = New StreamWriter_
   (Application.StartupPath + "\newfile.txt", True)
szRecord = oStreamReader.ReadLine()'pick up 1st line
While (szRecord <> String.Empty)
   oStreamWriter.WriteLine (szRecord)
   szRecord = oStreamReader.ReadLine()
   'Parse record into array elements, pass data to
   'an INSERT stored procedure if desired
   aData = szRecord.Split(",")
End While
oStreamWriter.WriteLine (szRecord) 'write last line
oStreamReader.Close()
oStreamWriter.Close()

El texto ASCII no es lo único que se puede escribir en una secuencia. MemoryStreams permite escribir datos en la memoria. De esta forma, se trata la memoria como si fuera un archivo temporal del disco. Por ejemplo, supongamos que necesita clonar un objeto serializable. Si escribe el objeto serializado en un objeto MemoryStream, puede deserializar el objeto directamente de la secuencia y evitar la mayor lentitud de la E/S del disco. La lista 3 muestra cómo crear un objeto MemoryStream.

Lista 3. Trabajo con MemoryStreams

Dim oMemoryStream As MemoryStream
Dim oUnicodeEncoding As System.Text.UnicodeEncoding
Dim szData As Byte()
oMemoryStream = New MemoryStream(100)
oUnicodeEncoding = New System.Text.UnicodeEncoding
szData = oUnicodeEncoding.GetBytes("Write this to _
   a MemoryStream")
oMemoryStream.Write(szData, 0, szData.Length)
MessageBox.Show ("Capacity = " + _
   oMemoryStream.Capacity.ToString() + _
   ControlChars.CrLf + "Length = " + _
   oMemoryStream.Length.ToString())

La lectura y escritura en archivos binarios es similar. [He incluido la sección de la columna de Bill Shadish de enero de 1999 que trataba la lectura de archivos binarios en VB5(!) en la descarga que se adjunta con este artículo.–Ed.] El código de la lista 4 muestra cómo crear y abrir un archivo binario y utilizar un objeto BinaryWriter para escribir los enteros 1-5 en él. A continuación, un objeto BinaryReader lee los datos que se acaban de escribir y los muestra en un cuadro de mensaje.

Lista 4. Trabajo con archivos binarios

Dim oStream As Stream
Dim oBinaryWriter As BinaryWriter
Dim oBinaryReader As BinaryReader
Dim i As Integer
'Use File.Open to create a file for write access and
'return a Stream object reference
oStream = File.Open("c:\temp\data.dat", _
   FileMode.Create, FileAccess.Write)
'Instantiate a BinaryWriter obj. from the Stream obj.
oBinaryWriter = New BinaryWriter(oStream)
'Write the numbers 1 through 10 to this file
For i = 1 To 5
   oBinaryWriter.Write (i)
Next
oBinaryWriter.Close()
oStream.Close()
'Open file for read and create a BinaryReader object
oStream = File.Open("c:\temp\data.dat", _
   FileMode.Open, FileAccess.Read)
oBinaryReader = New BinaryReader(oStream)
'As long as there's data, display it in a MessageBox
Do Until oBinaryReader.PeekChar = -1
   MessageBox.Show (oBinaryReader.ReadInt32)
Loop
oBinaryWriter.Close()
oStream.Close()

Las clases DirectoryInfo y FileInfo encapsulan los archivos y directorios individuales. A diferencia de sus homólogos compartidos, Directory y File, en este caso sí es necesario crear explícitamente instancias de estas clases. Imaginemos que desea utilizar el control TreeView para crear un sencillo explorador similar al Explorador de Windows. Mediante estos objetos, puede mostrar todos los archivos y directorios que hay en la raíz y ver los detalles de cada uno de los subdirectorios inferiores. Aunque la descarga que acompaña a este artículo incluye todo el código de este ejemplo, la

lista 5 muestra cómo se extrae la información de este directorio y este archivo.

Lista 5. Recuperación de toda la información sobre directorios y subdirectorios

Sub GetFileDir(ByVal szPath As String, _
ByVal oTopNode As TreeNode)
   Dim oFileInfo As FileInfo
   Dim oDirectoryInfo As DirectoryInfo
   Dim oNode As TreeNode
   oDirectoryInfo = New DirectoryInfo(szPath)
   'If this is not a directory then exit
   If Not oDirectoryInfo.Exists Then
      Exit Sub
   End If
   If Not oTopNode Is Nothing Then 'Clear the nodes
      oTopNode.Nodes.Clear()
   End If
   'Iterate through each file in the chosen directory
   For Each oFileInfo _
      In oDirectoryInfo.GetFiles("*.*")
      oNode = New TreeNode
      oNode.Text = oFileInfo.FullName.Replace(szPath _
         + "\", String.Empty)
      If Not oTopNode Is Nothing Then
         oTopNode.Nodes.Add (oNode)
      Else
         TreeView1.Nodes.Add (oNode)
      End If
   Next
  'Iterate through subdirectories in chosen directory
   For Each oDirectoryInfo _
      In oDirectoryInfo.GetDirectories("*.*")
      oNode = New TreeNode
      oNode.Text = oDirectoryInfo.FullName
      If Not oTopNode Is Nothing Then
         oTopNode.Nodes.Add (oNode)
      Else
         TreeView1.Nodes.Add (oNode)
      End If
   Next
End Sub

Cada objeto directory y file contiene varias propiedades que devuelven más información acerca de ellos. Además, se puede utilizar el método GetAttributes para devolver un objeto de la clase FileAttributes que contenga un valor codificado mediante bits que indique si el archivo es Hidden, System, ReadOnly, Archive, etc. La

lista 6 muestra cómo rellenar un DataTable con los archivos del directorio C:\Program Files\Microsoft Office\.

Lista 6. Recuperación de propiedades y atributos

Dim oFileInfo As FileInfo
Dim oFileAttributes As FileAttributes
Dim oDirectoryInfo As DirectoryInfo
Dim oDT As DataTable
Dim oDR As DataRow
oDirectoryInfo = New DirectoryInfo(_
   "C:\Program Files\Microsoft Office\")
.
For Each oFileInfo In oDirectoryInfo.GetFiles("*.*")
   oDR = oDT.NewRow
   oDR("FullName") = oFileInfo.FullName
   oDR("CreationTime") = oFileInfo.CreationTime
   oDR("Extension") = oFileInfo.Extension
   oDR("LastAccessTime") = oFileInfo.LastAccessTime
   oDR("LastWriteTime") = oFileInfo.LastWriteTime
   oDR("Length") = oFileInfo.Length
   oFileAttributes = oFileInfo.Attributes
   oDR("Hidden") = IIf(CBool(oFileAttributes And _
      oFileInfo.Attributes.Hidden), True, False)
   oDR("ReadOnly") = IIf(CBool(oFileAttributes And _
      oFileInfo.Attributes.ReadOnly), True, False)
   oDR("System") = IIf(CBool(oFileAttributes And _
      oFileInfo.Attributes.System), True, False)
   oDT.Rows.Add(oDR)
Next

Debido a la naturaleza ubicua de los servicios Web, lo más probable es que quiera desarrollar algunos procedimientos estándar para trasladar archivos de un servidor a un equipo cliente. Una manera fácil de hacerlo consiste en leer el archivo de datos en un objeto Stream y utilizar una codificación Base-64 para convertir la secuencia de bytes en texto. (Base-64 es un esquema de codificación de datos en que los datos con codificación binaria se convierten en caracteres ASCII.) Los únicos caracteres que se utilizan son las letras de la A a la Z en mayúscula y minúscula, los números del 0 al 9, los símbolos + y /, y el símbolo = como código de sufijo. Cuando una cadena está codificada en Base-64, aumenta su tamaño aproximadamente en un 30-50%.

[Los suscriptores de SQL Server Professional tal vez quieran volver a leer el artículo de marzo de 2004 de Carl, "Delivering Reports with a Crystal Enterprise 9 Web Service", en el que utilizó Base-64.–Ed.]

Imaginemos que tiene un servicio Web que necesita devolver un archivo de informe. Si lee los archivos en una matriz de bytes, puede convertir estos datos en una cadena con codificación Base-64 que se puede devolver a la aplicación cliente, donde se puede volver a ensamblar como un archivo de disco. Afortunadamente, VB.NET ofrece un método del espacio de nombres System que realiza automáticamente la codificación Base-64. Este método recibe como parámetros una matriz de enteros, un desplazamiento y la longitud de la matriz de enteros. El resultado final es una cadena de texto con un aspecto similar al siguiente:

JVBERi0xLjINJeLjz9MNCjEzMiAwIG9iag08PCANL0xpbmVhcml6Z

La lista 7 muestra el código que permite al usuario seleccionar un archivo y convertirlo en una cadena con codificación Base-64.

Lista 7. Conversión de un archivo en una cadena con codificación Base-64

Dim oOpenFileDialog As OpenFileDialog
oOpenFileDialog = New OpenFileDialog
With oOpenFileDialog
   .InitialDirectory = "C:\"
   .ShowDialog()
End With
szEncodedData = Base64Encode(oOpenFileDialog.FileName)
Private Function Base64Encode(ByVal szFileName _
As String) As String
   Dim oFileStream As FileStream
   Dim aData() As Byte
   Dim lBytes As Long
   Dim szResult As String
   oFileStream = New System.IO.FileStream(szFileName,_
      IO.FileMode.Open, IO.FileAccess.Read)
   ReDim aData(oFileStream.Length)
   lBytes = oFileStream.Read(aData, 0, _
      oFileStream.Length)
   oFileStream.Close()
   szResult = System.Convert.ToBase64String(aData, _
      0, aData.Length)
   Return szResult
End Function

Para volver a convertir la cadena en un archivo de disco, pruebe el código de la lista 8. Este código también utiliza un objeto FileStream con un método Write para invertir el procesamiento realizado en la lista 7.

Lista 8. Conversión de una cadena con codificación Base-64 en un archivo de disco

Base64Decode(szEncodedData, "c:\temp\data.txt")
Sub Base64Decode(ByVal szData As String, _
ByVal szFileName As String)
   Dim aData() As Byte
   Dim oFileStream As System.IO.FileStream
   aData = System.Convert.FromBase64String(szData)
   oFileStream = New System.IO.FileStream(szFileName,_
      IO.FileMode.Create, IO.FileAccess.Write)
   oFileStream.Write(aData, 0, aData.Length - 1)
   oFileStream.Close()
End Sub

A veces, cuando está trabajando con aplicaciones que requieren actualizaciones por lotes mediante la información procedente de otros sistemas, puede que necesite comprobar la disponibilidad de un archivo (normalmente en un gran sistema mainframe o asociado con una aplicación de cliente y servidor) en una determinada ubicación. En el pasado, es posible que haya creado una aplicación que consultase el directorio especificado para ver si el archivo estaba disponible y, en este caso, iniciar una rutina de importación. VB.NET ofrece la clase FileSystemWatcher que hace esto automáticamente. Mediante FileSystemWatcher puede crear una escucha que comprobará las notificaciones especificadas y, cuando se reciban, disparará un controlador de eventos. Concretamente, puede comprobar cuándo se cambia, crea, elimina o cambia el nombre de un archivo. El código de la lista 9 muestra cómo comprobar la creación de un archivo llamado testfile.txt en el directorio c:\temp.

Lista 9. Vigilancia de la aparición de un archivo

Dim oFileSystemWatcher As New FileSystemWatcher
oFileSystemWatcher.Path = "c:\temp"
oFileSystemWatcher.Filter = "import.txt"
AddHandler oFileSystemWatcher.Created, AddressOf _
   OnCreated
oFileSystemWatcher.EnableRaisingEvents = True
Private Shared Sub OnCreated(ByVal source As Object, _
   ByVal e As FileSystemEventArgs)
   MessageBox.Show (e.FullPath + " has been " + _
      e.ChangeType.ToString)
End Sub

Mediante FileSystemWatcher puede especificar los archivos que desea vigilar con sólo establecer el valor apropiado en la propiedad Filter. Para tener en cuenta todos los archivos, defina la propiedad Filter en una cadena vacía.

Resulta importante comprender que FileSystemWatcher reacciona a los eventos de archivo generados por el sistema operativo; el objeto FileSystemWatcher no genera directamente eventos de archivo. Imagínese que necesita esperar a que se cree un archivo en una determinada ubicación y que el archivo es de gran tamaño y tarda mucho tiempo en escribirse. Cuando se dispare el evento de FileSystemWatcher, querrá detener inmediatamente el envío de más notificaciones mediante la definición de la propiedad EnableRaisingEvents en False. Asimismo, querrá comprobar si el archivo que desea está disponible para el acceso exclusivo. Si no lo está, querrá establecer un temporizador y seguir intentándolo hasta que lo esté o hasta que haya transcurrido un período de tiempo de espera. Además, tenga en cuenta que las operaciones con archivos con frecuencia generan varios eventos si se declaran controladores para estos eventos.

.NET ofrece una amplia funcionalidad basada en clases para trabajar con archivos y directorios. Tras una década de estancamiento en este tema, las opciones disponibles son ahora mucho más numerosas y eficaces.

Barra lateral: Un vistazo a VS 2005

Desde este momento hasta el lanzamiento de VS 2005, comentaré brevemente algunas de estas nuevas características en esta columna. Como la columna de este mes trata de las funciones de archivo de bajo nivel, empecemos por ellas. VS 2005 presenta la palabra clave My, que servirá como conducto para toda la funcionalidad del sistema. Esta palabra funciona como referencia de objetos superior y permitirá utilizar IntelliSense para desplazarse a cualquier punto del modelo de objetos de .NET. En la lista 1, leímos los datos de un archivo de texto como el siguiente:

Dim oStreamReader As StreamReader
Dim szAllFileData As String
oStreamReader = New StreamReader("c:\temp\data.txt")
szAllFileData = oStreamReader.ReadToEnd()
oStreamReader.Close()

Gracias a VS 2005 y la palabra clave My, podemos recurrir a las herramientas de archivo de bajo nivel con sólo dos líneas en lugar de cinco:

Dim szAllFileData As String
szAllFileData = _
My.Computer.FileSystem.ReadAllText("c:\temp\data.txt")

De manera similar, podríamos escribir los datos en un nuevo archivo y, a continuación, copiar el archivo en una nueva ubicación, como vemos a continuación:

My.Computer.FileSystem.WriteAllText_
   ("c:\temp\data1.txt", szAllFileData, False)
My.Computer.FileSystem._
   CopyFile("c:\temp\data1.txt", "c:\")

Descargar 412CARL.ZIP

Para obtener más información acerca de Visual Basic Developer y Pinnacle Publishing, visite su sitio Web en http://www.pinpub.com/ (en inglés)

Nota: este sitio Web no pertenece a Microsoft Corporation. Microsoft no se responsabiliza de su contenido.

Este artículo es una reproducción del incluido en el número de diciembre de 2004 de Visual Basic Developer. Copyright 2004, por Pinnacle Publishing, Inc., salvo que se especifique de otro modo. Reservados todos los derechos. Visual Basic Developer es una publicación producida de forma independiente por Pinnacle Publishing, Inc. No se podrá utilizar ni reproducir parte alguna de este artículo de ningún modo (salvo las citas breves utilizadas en las reseñas y los artículos críticos) sin el previo consentimiento por escrito de Pinnacle Publishing, Inc. Si desea ponerse en contacto con Pinnacle Publishing, Inc., llame al 1-800-788-1900.

Mostrar:
© 2014 Microsoft