20
4.1 Programación .NET (II). Manejo de archivos y carpetas 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.

TEMA 4. MANEJO DE ARCHIVOS Y CARPETAS 4.1. … · 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,

Embed Size (px)

Citation preview

4.1Programación .NET (II). Manejo de archivos y carpetas

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 carpetas4.2

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.

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.

Lectura

Abrir un flujo desde un fichero

Mientras haya información

Leer información

Cerrar el flujo

Escritura

Abrir un flujo hacia un fichero

Mientras haya información

Escribir información

Cerrar el flujo

4.3Programació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 delarchivo.

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 dedeterminada 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 archivovinculado 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 carpetas4.4

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.

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 puedeespecificar el tipo de acceso al archivo (lectura, escritura o lectura y

escritura) en el parámetro acceso.

Posibles valores para FileMode

CreateNewCrear un nuevo archivo. Si ya

existe se lanzará una excepción

de tipo IOException.

CreateCrear un nuevo archivo o

sobrescribirlo si ya existe.

OpenAbrir un archivo existente. Si no

existe se lanzará una excepción

de tipo FileNotFoundException.

OpenOrCreateSi el archivo existe lo abre, si no

lo crea.

TruncateAbre un archivo existente y lo

trunca a cero bytes de longitud.

AppendAbre un fichero para añadir datos

al final del mismo. Si no existe lo

crea.

Posibles valores para FileAccess

ReadEl archivo será accesible para

lectura.

ReadWriteEl archivo será accesible para

lectura y escritura.

WriteEl archivo será accesible para

escritura.

4.5Programació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.

Observa el código anterior. Su función es leer caracteres de la entrada estándar

y después guardarlos en un archivo.

'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

Programación .NET (II). Manejo de archivos y carpetas4.6

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.7Programació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 aescribir 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 queremosempezar a escribir o leer (acceso aleatorio).

Programación .NET (II). Manejo de archivos y carpetas4.8

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 delectura/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 unvalor 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”:

El método Seek.

Seek nos permite establecer la posición en la que se empezará a leer oescribir en el archivo en función de un desplazamiento respecto a cierta

posición.

Su sintaxis es la siguiente:

Este método moverá el puntero de lectura/escritura a una posición desplazada

desp bytes de la posición pos del archivo.

If flujo.Length = flujo.Position Then

Console.WriteLine("Fin del archivo")

End If

Public Function Seek(ByVal desp As Long, ByVal pos As SeekOrigin) As Long

4.9Programació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:

Con esta instrucción situamos el puntero 10 bytes antes del final del archivo.

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 tiposprimitivos (Boolean, Byte, Single, Long, Integer, etc) en formatobinario y cadenas de caracteres en formato UTF-8.

Hay que tener en cuenta que BinaryReader sólo podrá leer datos que hayansido escritos con BinaryWriter.

Utilizaremos estas clases cuando sea necesario escribir o leer en archivos

información en formato binario, no como texto.

flujo.Seek(-10, SeekOrigin.End)

flujo.Seek(10, SeekOrigin.Current)

Programación .NET (II). Manejo de archivos y carpetas4.10

Los flujos de estas clases se definen a partir de un flujo de la clase Streamo sus derivadas (FileStream), de la siguiente forma:

De esta forma, los flujos BinaryReader y BinaryWriter actúan como un filtro del

flujo al que están asociados.

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.

Sub New(ByVal flujo As Stream)

La forma genérica de trabajar con estos filtros será lasiguiente

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.

4.11Programació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 carpetas4.12

En el código anterior cabe destacar que usamos el mismo flujo tanto paraescribir 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 eltexto se guarda en formato UTF-8.

4.13Programació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 tipotexto, 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:

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).

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 nombre As String[, ByVal añadir As Boolean, _

[ByVal codif As System.Text.Encoding]])

Sub New(ByVal flujo As Stream[, ByVal codif As System.Text.Encoding])

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 carpetas4.14

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).

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.

Sub New(ByVal ruta As String _

[, ByVal codif As System.Text.Encoding] _

[, ByVal detecta_codif As Boolean] _

[, ByVal buffer As Integer])

4.15Programación .NET (II). Manejo de archivos y carpetas

Observa que, a diferencia de BinaryReader, StreamReader nos ha permitido

leer todos los valores con el mismo método, con independencia del tipo dedato a leer, puesto que lo tratará como texto.

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

Programación .NET (II). Manejo de archivos y carpetas4.16

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 cadenasde 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.17Programació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 abrirarchivos. 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.

GetAttributesObtiene los atributos de un archivo (un objeto

de tipo FileAttributes).

SetAttributesEstablece los atributos FileAttributes en el

archivo especificado.

Programación .NET (II). Manejo de archivos y carpetas4.18

Directory

Directory proporciona métodos para crear, copiar, eliminar, mover ycambiar el nombre a directorios y subdirectorios.

Existe otra clase, la clase DirectoryInfo, que, en esencia, ofrece la misma

funcionalidad que Directory. La diferencia entre ambas clases radica enque Directory se usa sin necesidad de instanciar la clase, lo que noocurre con DirectoryInfo.

DirectoryInfo será más apropiada si queremos realizar múltiples accionessobre 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.

Algunos métodos de Directory

CreateDirectoryCrea todos los directorios y subdirectorios

especificados.

Delete Elimina un directorio y su contenido.

ExistsDetermina si existe el directorio dado por la ruta

especificada.

MoveMueve un directorio y su contenido a una nueva

localización.

GetFilesDevuelve los nombres de todos los archivos que

contiene el directorio especificado.

GetParentDevuelve un objeto de tipo DirectoryInfo del

directorio “padre” del directorio especificado.

4.19Programación .NET (II). Manejo de archivos y carpetas

Path

La clase Path permite realizar diversas operaciones sobre una rutaespecificada 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 diversasplataformas.

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.

Algunos métodos de Path

ChangeExtension Cambia la extensión de un archivo.

GetDirectoryName Obtiene la ruta de un archivo.

GetExtension Devuelve la extensión de la ruta a un archivo.

GetFileNameDevuelve el nombre y extensión de la ruta

especificada.

GetPathRoot Devuelve la raíz de la ruta indicada.

Programación .NET (II). Manejo de archivos y carpetas4.20

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.

'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