Maquetación 2 - Grupo Conforsa

Anuncio
TEMA 4. MANEJO DE ARCHIVOS Y CARPETAS
4.1.
INTRODUCCIÓN
Las aplicaciones que hemos creado hasta ahora han obtenido los datos que
han precisado a través de la información introducida por el usuario mediante
los dispositivos de entrada estándar (teclado, ratón), mostrando resultados al
usuario por pantalla.
Toda esta información ha persistido únicamente durante la ejecución de la
aplicación, si volvemos a utilizarla en otra ocasión habrá que subministrar de
nuevo los datos de partida necesarios para obtener los resultados.
En muchos casos esto nos supone una grave limitación. Por ejemplo, si
pretendemos manejar algunos datos personales de nuestros usuarios, sería
muy molesto tener que solicitarlos cada vez que usen la aplicación. Lo idóneo
sería solicitarlos la primera vez y conservarlos de algún modo para sucesivas
ocasiones.
¿Cómo conservar datos de una ejecución a otra? Mediante archivos que
guardaremos en algún medio accesible por nuestras aplicaciones.
Un archivo es una agrupación de datos, que, como ya hemos mencionado, es
accesible por una computadora. Se identifica por su nombre, opcionalmente
una extensión y su ubicación dentro del sistema de archivos que lo contiene.
Normalmente los archivos se agrupan en carpetas o directorios según su
contenido, propósito o cualquier otro criterio para una mejor organización de
la información.
En este tema vamos a aprender a manejar los archivos y carpetas desde
nuestras aplicaciones para acceder así a su contenido. Para ello, .Net pone a
nuestra disposición el espacio de nombres System.IO.
System.IO contiene tipos que nos permitirán leer y escribir en archivos, así
como tipos que proporcionan funcionalidad para manejar los archivos y
carpetas.
Programación .NET (II). Manejo de archivos y carpetas
4.1
4.2. ACCESO AL SISTEMA DE FICHEROS
La comunicación de información desde un origen hasta una aplicación se
produce a través de un flujo de información (stream). Este elemento será
un objeto que actuará de intermediario entre la aplicación y el origen.
De este modo la aplicación lee o escribe en el flujo de información sin importarle
de donde viene la información o a donde va, y sin importar el tipo de dispositivo
que alberga el archivo.
El nivel de abstracción que nos ofrece el flujo de información va a permitir que
la forma de leer o escribir información desde nuestras aplicaciones sea
generalmente el mismo.
Lectura
Escritura
Abrir un flujo desde un fichero
Mientras haya información
Leer información
Cerrar el flujo
Abrir un flujo hacia un fichero
Mientras haya información
Escribir información
Cerrar el flujo
Podremos distinguir dos tipos de acceso al archivo según la forma en que
podemos recuperar su información: acceso secuencial y acceso aleatorio.
Vamos a definir ambos tipos.
4.2
Programación .NET (II). Manejo de archivos y carpetas
 Acceso Secuencial
Es la forma más simple de acceder a un archivo. La información se leerá o
escribirá de forma secuencial, eso es desde el principio hasta el fin del
archivo.
Se utiliza generalmente con archivos de texto en los que se escribe toda la
información desde el principio hasta el final y se lee de igual forma.
 Acceso Aleatorio
El acceso aleatorio a un fichero nos permite leer o escribir a partir de
determinada posición dentro del fichero.
Es útil para acceder a ciertos datos dentro de un archivo sin tener que recorrer
los datos previos para llegar hasta ellos, como sucede con el acceso secuencial.
El espacio de nombres System.IO de la biblioteca .NET contiene las clases
necesarias para leer y escribir en archivos. A continuación vamos a describir
las más importantes.
Clase FileStream
Con un flujo de la clase FileStream podremos leer o escribir datos en un fichero
byte a byte. Proporciona tanto acceso secuencial como aleatorio.
Un flujo de este tipo permitirá tanto leer como escribir en el archivo
vinculado al mismo.
Deriva de la clase abstracta Stream
Esta clase nos proporciona los siguientes constructores:
Sub New(ByVal nombre As String, ByVal modo As FileMode)
Con este constructor abrimos un flujo de entrada y salida vinculado al
fichero especificado en nombre. El parámetro modo indicará la forma de
apertura del archivo.
Programación .NET (II). Manejo de archivos y carpetas
4.3
Sub New(ByVal nombre As String, ByVal modo As FileMode, _
ByVal acceso As FileAccess)
Este otro constructor hace lo mismo que el anterior, salvo que se puede
especificar el tipo de acceso al archivo (lectura, escritura o lectura y
escritura) en el parámetro acceso.
Los parámetros modo y acceso son de los tipos enumerados FileMode y
FileAccess respectivamente. A continuación puedes ver los valores que puede
tomar cada uno.
Posibles valores para FileMode
CreateNew
Create
Crear un nuevo archivo. Si ya
existe se lanzará una excepción
de tipo IOException.
Crear un nuevo archivo o
sobrescribirlo si ya existe.
Open
Abrir un archivo existente. Si no
existe se lanzará una excepción
de tipo FileNotFoundException.
OpenOrCreate
Si el archivo existe lo abre, si no
lo crea.
Truncate
Abre un archivo existente y lo
trunca a cero bytes de longitud.
Append
Abre un fichero para añadir datos
al final del mismo. Si no existe lo
crea.
Posibles valores para FileAccess
4.4
Read
El archivo será accesible para
lectura.
ReadWrite
El archivo será accesible para
lectura y escritura.
Write
El archivo será accesible para
escritura.
Programación .NET (II). Manejo de archivos y carpetas
A continuación realizaremos un ejemplo que ilustrará el uso de la clase
FileStream. Para ello, creamos un nueva aplicación de consola en Visual
Basic Express. Este tipo de aplicación nos permitirá poder leer texto de forma
simple, desde la entrada estándar.
'Importamos el espacio de nombres System.IO
Imports System.IO
Module EscribirFS
Public Sub Main()
Dim flujo As FileStream
Dim texto(70) As Byte
Dim car, contador As Integer
'leemos caracteres
car = Console.Read
While (car <> 13 And contador < 70)
texto(contador) = Convert.ToByte(car)
contador = contador + 1
car = Console.Read
End While
'creamos el flujo y escribimos!
flujo = New FileStream("c:\archivo.txt", FileMode.Create, _
FileAccess.Write)
flujo.Write(texto, 0, contador)
flujo.Close()
End Sub
End Module
Observa el código anterior. Su función es leer caracteres de la entrada estándar
y después guardarlos en un archivo.
Programación .NET (II). Manejo de archivos y carpetas
4.5
Lo primero que hacemos es importar el espacio de nombres System.IO. Esto
es vital para poder usar la clase FileStream.
Después, en el proceso principal del módulo, declaramos un flujo de tipo
FileStream llamado “flujo” y un array de bytes para guardar los caracteres
introducidos por el usuario (“texto”). También declaramos una variable que
servirá para almacenar el número de caracteres leídos (“contador”).
Leemos caracteres hasta que le usuario pulse “Enter” (código 13) o bien se
supere la capacidad de 70 caracteres de nuestro array.
Una vez que tenemos el texto a guardar en el archivo, pasamos a crear el flujo
FileStream. Usamos modo Create para crear el archivo y el modo de acceso
Write para escritura.
Una vez creado el flujo, usamos su método Write para escribir en el fichero.
Pasamos a este método como parámetros el array de bytes que queremos
guardar, la posición inicial del array desde la que queremos escribir y el número
de bytes (caracteres) a escribir (variable “contador”).
Al iniciar la aplicación se abrirá una ventana de consola. En ella escribimos el
texto que queremos escribir en el archivo, por ejemplo:
Escribimos lo que va en el archivo...
Si abrimos el archivo generado por la aplicación (“archivo.txt”), veremos que
contiene el texto que habíamos escrito.
4.6
Programación .NET (II). Manejo de archivos y carpetas
Ten en cuenta que el archivo se creará de nuevo cada vez que ejecutemos el
programa, al crear su flujo con modo Create. Si quisiéramos crear el archivo
sólo si no existe, sólo tendríamos que cambiar al modo OpenOrCreate.
Con el modo OpenOrCreate, si el archivo existe, escribiremos inicialmente al
principio del archivo sobre su contenido previo.
Si escribimos en la consola “HOLA” y luego terminamos pulsando Enter, el texto
en el archivo sería este:
HOLAibimos lo que va en el archivo…
Como ves, si no indicamos otra cosa, el método Write va a empezar a
escribir en la primera posición del archivo.
Antes hemos comentado que FileStream permite acceso aleatorio y secuencial
al archivo, por lo que podremos indicar la posición en la que queremos
empezar a escribir o leer (acceso aleatorio).
Programación .NET (II). Manejo de archivos y carpetas
4.7
Para este modo de acceso FileStream ofrece las siguientes propiedades y
método:
 La propiedad Position.
Position devuelve la posición actual en bytes del puntero de
lectura/escritura. Este puntero apunta a la posición del archivo en la que se
realizará la próxima lectura o escritura. También podemos establecer un
valor para Position, incluso más allá del final del archivo.
 La propiedad Length.
Esta propiedad indica la longitud del archivo en bytes.
Por ejemplo, el siguiente código comprueba si se ha llegado al fin del archivo
vinculado al flujo “flujo”:
If flujo.Length = flujo.Position Then
Console.WriteLine("Fin del archivo")
End If
 El método Seek.
Seek nos permite establecer la posición en la que se empezará a leer o
escribir en el archivo en función de un desplazamiento respecto a cierta
posición.
Su sintaxis es la siguiente:
Public Function Seek(ByVal desp As Long, ByVal pos As SeekOrigin) As Long
Este método moverá el puntero de lectura/escritura a una posición desplazada
desp bytes de la posición pos del archivo.
4.8
Programación .NET (II). Manejo de archivos y carpetas
El parámetro pos toma un valor del tipo enumerado SeekOrigin. Estos
valores pueden ser Begin (principio del archivo), End (final) y Current
(posición en la que está actualmente el puntero de lectura/escritura).
Por ejemplo:
flujo.Seek(-10, SeekOrigin.End)
Con esta instrucción situamos el puntero 10 bytes antes del final del archivo.
flujo.Seek(10, SeekOrigin.Current)
Con esta instrucción situamos el puntero 10 bytes después de la posición
actual.
Clases BinaryWriter y BinaryReader
Estas clases permiten leer y escribir, respectivamente, datos de tipos
primitivos (Boolean, Byte, Single, Long, Integer, etc) en formato
binario y cadenas de caracteres en formato UTF-8.
Hay que tener en cuenta que BinaryReader sólo podrá leer datos que hayan
sido escritos con BinaryWriter.
Utilizaremos estas clases cuando sea necesario escribir o leer en archivos
información en formato binario, no como texto.
Programación .NET (II). Manejo de archivos y carpetas
4.9
Los flujos de estas clases se definen a partir de un flujo de la clase Stream
o sus derivadas (FileStream), de la siguiente forma:
Sub New(ByVal flujo As Stream)
De esta forma, los flujos BinaryReader y BinaryWriter actúan como un filtro del
flujo al que están asociados.
La forma genérica de trabajar con estos filtros será la
siguiente
 Creamos un flujo asociado a un archivo (FileStream).
 Asociamos un filtro (BinaryReader/BinaryWriter) al flujo.
 Leemos o escribimos en el archivo a través del filtro.
A continuación te mostramos un ejemplo sencillo con BinaryReader y
BinaryWriter.
El ejemplo consiste en escribir datos de diversos tipos en un archivo con
BinaryWriter y después los leerlos con BinaryReader.
4.10
Programación .NET (II). Manejo de archivos y carpetas
'Importamos el espacio de nombres System.IO
Imports System.IO
Module Module1
Public Sub Main()
Dim fs As FileStream
Dim bw As BinaryWriter
'Abrimos un flujo FileStream
fs = New FileStream("c:\binario.txt", FileMode.Create, _
FileAccess.ReadWrite)
'Asociamos un BinaryWriter al flujo fs
bw = New BinaryWriter(fs)
'Escribimos con Write datos de diversos tipos
bw.Write("Texto guardado en binario")
bw.Write(True)
Dim variable As Double = - 564.6
bw.Write(variable)
bw.Write("Fin")
Dim br As BinaryReader
'Asociamos un BinaryReader al flujo fs para leer datos
br = New BinaryReader(fs)
'Volvemos al comienzo del archivo
fs.Position = 0
'Leemos según el tipo de dato, y mostramos por la consola
Console.WriteLine(br.ReadString)
Console.WriteLine(br.ReadByte)
Console.WriteLine(br.ReadDouble)
Console.WriteLine(br.ReadString)
'Ya podemos cerrar flujo y filtros
bw.Close()
br.Close()
fs.Close()
'Esperamos a que el usuario pulse una tecla para terminar
Console.ReadKey()
End Sub
End Module
Programación .NET (II). Manejo de archivos y carpetas
4.11
En el código anterior cabe destacar que usamos el mismo flujo tanto para
escribir como para leer los datos. Por ello, cuando terminamos de escribir
y nos disponemos a leer los datos guardados para presentarlo por pantalla,
hemos tenido que mover el cursor de lectura/escritura desde el final de archivo
hasta el principio.
Observa además que se hará diferenciación del tipo del dato a la hora de leer,
usando el método más apropiado al tipo de dato (ReadString, ReadByte...).
Cuando probamos la aplicación se muestra una ventan de consola con el
siguiente contenido:
Cada lectura del archivo realizada se muestra en una línea. Cada uno se ha
leído de acuerdo a su tipo, como ya hemos comentado.
Comparamos con la vista directa del contenido del archivo:
Como ves, BinaryWrite no guarda los datos como texto plano.
Los datos de tipos primitivos se guardan en binario, mientras que el
texto se guarda en formato UTF-8.
4.12
Programación .NET (II). Manejo de archivos y carpetas
StreamReader y StreamWriter
Estas clases son la opción más indicada para procesar información de tipo
texto, pues nos van a permitir respectivamente leer o escribir datos carácter
a carácter. Estas clases heredan los métodos de las clases TextReader y
TextWriter.
StreamWriter ofrece dos constructores:
Sub New(ByVal nombre As String[, ByVal añadir As Boolean, _
[ByVal codif As System.Text.Encoding]])
Este constructor crea un flujo de escritura vinculado al archivo nombre.
Opcionalmente, se puede indicar si se desea añadir texto al que ya contiene el
archivo y especificar cómo se codificará el texto en él (por defecto se asignará
codificación UTF-8).
Sub New(ByVal flujo As Stream[, ByVal codif As System.Text.Encoding])
Este constructor hace lo mismo, pero sobre otro flujo existente, como hacíamos
con BinaryReader y BinaryWriter. Opcionalmente podremos especificar una
codificación (parámetro codif).
A su vez, StreamReader cuenta con los siguientes constructores:
Sub New(ByVal flujo As Stream
[, ByVal codif As System.Text.Encoding] _
[, ByVal detecta_codif As Boolean] _
[, ByVal buffer As Integer])
Programación .NET (II). Manejo de archivos y carpetas
4.13
Crea un StreamReader a partir de un flujo existente. Opcionalmente
podemos detectar la codificación del archivo (detecta_codif), establecer la
codificación (codif)y el tamaño mínimo del buffer de lectura utilizado (buffer).
Sub New(ByVal ruta As String _
[, ByVal codif As System.Text.Encoding] _
[, ByVal detecta_codif As Boolean] _
[, ByVal buffer As Integer])
Crea un flujo StreamReader vinculado al archivo indicado en ruta.
Opcionalmente podemos detectar la codificación del archivo (detecta_codif),
establecer la codificación (codif) y el tamaño mínimo del buffer de lectura
utilizado (buffer).
A continuación puedes ver un ejemplo de código contenido en una aplicación
de consola. Su función consiste en escribir unos valores en un archivo con
StreamWriter y leerlos con StreamReader.
4.14
Programación .NET (II). Manejo de archivos y carpetas
Imports System.IO
Module Module1
Sub Main()
Dim sw As StreamWriter
'creamos un flujo de escritura vinculado a archivo.txt
sw = New StreamWriter("e:\texto.txt")
'escribimos en el archivo, con WriteLine una línea por dato
sw.WriteLine("Un texto")
sw.WriteLine(True)
Dim variable As Double = -564.6
sw.WriteLine(variable)
'cerramos el flujo de escritura
sw.Close()
Dim sr As StreamReader
'abrimos ahora un flujo de lectura del archivo
sr = New StreamReader("e:\texto.txt")
'leemos del archivo una línea cada vez con ReadLine
Console.WriteLine(sr.ReadLine())
Console.WriteLine(sr.ReadLine())
Console.WriteLine(sr.ReadLine())
'cerramos el flujo de lectura
sr.Close()
Console.ReadKey()
End Sub
End Module
Observa que, a diferencia de BinaryReader, StreamReader nos ha permitido
leer todos los valores con el mismo método, con independencia del tipo de
dato a leer, puesto que lo tratará como texto.
Programación .NET (II). Manejo de archivos y carpetas
4.15
Si probamos la aplicación obtendremos en la ventana de la consola una vista
muy parecida a la del ejemplo anterior:
Hemos leído todos los valores con el mismo método y se muestran
correctamente, pues en el archivo se han almacenado todos como cadenas
de texto. Lo comprobamos viendo directamente el contenido del archivo:
Como puedes observar, los valores se han guardado en el archivo como
caracteres de texto, a diferencia de la información guardada con BinaryWriter.
4.16
Programación .NET (II). Manejo de archivos y carpetas
4.3. MANIPULANDO ARCHIVOS Y CARPETAS
Trabajando con archivos a menudo
necesitaremos acceder al sistema de
archivos de la máquina, para buscar
un archivo concreto, conocer sus
propiedades, o incluso examinar el
contenido de cierto directorio.
System.IO cuenta con tres clases que
nos
ofrecen
métodos
para
la
manipulación de archivos y directorios:
File, Directory y Path.
Con ellas, podremos crear, copiar,
borrar, mover, alterar, determinar la
existencia
y
conocer
diversa
información de archivos y carpetas.
File
Proporciona métodos para crear, copiar, eliminar, mover y abrir
archivos. También se puede usar File para consultar o definir los atributos de
un archivo.
Algunos métodos de File
Copy
Copia un archivo existente en un archivo nuevo.
Delete
Elimina un archivo.
Exists
Determina si existe el archivo especificado.
Move
Mueve un archivo a una nueva localización.
GetAttributes
Obtiene los atributos de un archivo (un objeto
de tipo FileAttributes).
SetAttributes
Establece los atributos FileAttributes en el
archivo especificado.
Programación .NET (II). Manejo de archivos y carpetas
4.17
Directory
Directory proporciona métodos para crear, copiar, eliminar, mover y
cambiar el nombre a directorios y subdirectorios.
Algunos métodos de Directory
CreateDirectory
Crea todos los directorios y subdirectorios
especificados.
Delete
Elimina un directorio y su contenido.
Exists
Determina si existe el directorio dado por la ruta
especificada.
Move
Mueve un directorio y su contenido a una nueva
localización.
GetFiles
Devuelve los nombres de todos los archivos que
contiene el directorio especificado.
GetParent
Devuelve un objeto de tipo DirectoryInfo del
directorio “padre” del directorio especificado.
Existe otra clase, la clase DirectoryInfo, que, en esencia, ofrece la misma
funcionalidad que Directory. La diferencia entre ambas clases radica en
que Directory se usa sin necesidad de instanciar la clase, lo que no
ocurre con DirectoryInfo.
DirectoryInfo será más apropiada si queremos realizar múltiples acciones
sobre el mismo directorio, pues no habrá necesidad de especificar la ruta
del directorio cada vez que usemos un método, como ocurre con Directory, ya
que se especifica al hacer la instancia.
4.18
Programación .NET (II). Manejo de archivos y carpetas
Path
La clase Path permite realizar diversas operaciones sobre una ruta
especificada mediante una cadena de texto. Estas operaciones están
destinadas a obtener cierta información de archivos y carpetas.
Esta clase permite trabajar con sistemas de archivos de diversas
plataformas.
Algunos métodos de Path
ChangeExtension
GetDirectoryName
Cambia la extensión de un archivo.
Obtiene la ruta de un archivo.
GetExtension
Devuelve la extensión de la ruta a un archivo.
GetFileName
Devuelve el nombre y extensión de la ruta
especificada.
GetPathRoot
Devuelve la raíz de la ruta indicada.
Como ejemplo, vamos a modificar una de las aplicaciones realizadas
anteriormente para que, aprovechando la funcionalidad de File, solicite al
usuario la ruta del archivo sobre la que se trabajará, asegurándonos de su
existencia.
Programación .NET (II). Manejo de archivos y carpetas
4.19
'Importamos el espacio de nombres System.IO
Imports System.IO
Module EscribirFS
Public Sub Main()
Dim flujo As FileStream
Dim texto(70) As Byte
Dim car, contador As Integer
'solicitamos el archivo

Do
Console.Write("Ruta del archivo: ")
archivo = Console.ReadLine
Loop While Not File.Exists(archivo)
'leemos caracteres
car = Console.Read
While (car <> 13 And contador < 70)
texto(contador) = Convert.ToByte(car)
contador = contador + 1
car = Console.Read
End While
'creamos el flujo y escribimos!

flujo = New FileStream(archivo, FileMode.Create, _
FileAccess.Write)
flujo.Write(texto, 0, contador)
flujo.Close()
End Sub
End Module
Como ves, simplemente se ha añadido un bucle do – loop while para solicitar
el archivo hasta que sea un archivo existente en nuestro equipo.
Después abrimos el flujo asociado a la ruta del archivo indicado.
4.20
Programación .NET (II). Manejo de archivos y carpetas
Descargar