Está en la página 1de 38

TEMA 3: FICHEROS................................................................................................................

2
CONCEPTOS BSICOS..........................................................................................................................2
RUTAS DE FICHEROS Y DIRECTORIOS....................................................................................................3
SINTAXIS EN WINDOWS........................................................................................................................3
RUTAS INDEPENDIENTES DEL SISTEMA OPERATIVO.....................................................................................4
OPERACIONES CON RUTAS.....................................................................................................................5
REPRESENTACIN DE FICHEROS Y DIRECTORIOS EN .NET......................................................................6
ELEMENTOS COMUNES..........................................................................................................................6
ELEMENTOS ESPECFICOS DE DIRECTORIOS...............................................................................................9
ELEMENTOS ESPECFICOS DE FICHEROS..................................................................................................10
FLUJOS DE ENTRADA-SALIDA.............................................................................................................11
LECTURA Y ESCRITURA.......................................................................................................................12
MOVIMIENTO POR EL FLUJO................................................................................................................12
VOLCADO DE DATOS EN FLUJOS...........................................................................................................14
CIERRE DE FLUJOS.............................................................................................................................14
FLUJOS DE ENTRADA-SALIDA EN FICHEROS..........................................................................................14
CREACIN DE OBJETOS FILESTREAM....................................................................................................14
MANIPULACIN DEL CONTENIDO DE LOS FICHEROS..................................................................................16
CONTROL DE CONCURRENCIA EN ACCESOS A FICHEROS............................................................................20
ACCESO NATIVO A FICHEROS...............................................................................................................20
FICHERO BINARIOS...........................................................................................................................21
FICHEROS DE TEXTO.........................................................................................................................22
LECTURA DE FICHEROS DE TEXTO.........................................................................................................22
ESCRITURA EN FICHEROS DE TEXTO.....................................................................................................25
MANIPULACIN DEL SISTEMA DE ARCHIVOS........................................................................................27
MANIPULACIN DE FICHEROS..............................................................................................................27
MANIPULACIN DE DIRECTORIOS..........................................................................................................29
MANIPULACIN DE RUTAS..................................................................................................................30
DETECCIN DE CAMBIOS EN EL SISTEMA DE ARCHIVOS.........................................................................30
SELECCIN DE FICHEROS A VIGILAR......................................................................................................31
SELECCIN DE CAMBIOS A VIGILAR.......................................................................................................32
DETECCIN SNCRONA DE CAMBIOS......................................................................................................32
DETECCIN ASNCRONA DE CAMBIOS....................................................................................................34
EJEMPLO: CIFRADOR DE DIRECTORIOS..................................................................................................35
PROBLEMAS DE DESBORDAMIENTOS DEL BUFFER DE CAMBIOS...................................................................36
FICHEROS TEMPORALES....................................................................................................................37

Tema 3: Ficheros
Conceptos bsicos
Un fichero puede verse como una porcin de un dispositivo de almacenamiento no
voltil (disco duro, disquete, etc.) a la que se le asocia un determinado nombre, estando
en principio la cantidad de datos que puede almacenar slo limitada por la cantidad de
espacio del que disponga en cada momento el dispositivo donde se almacenen esos
datos o por las caractersticas del cada sistema operativo. Por no voltil se entiende que
a diferencia de lo que ocurre con otros almacenes de datos como la memoria RAM, la
informacin en ellos almacenadas no se pierde al apagarse el ordenador.
Dada la importancia de los ficheros como almacenes no voltiles de la informacin, la
BCL incluye todo un espacio de nombres llamado System.IO especialmente orientado al
trabajo con ellos. En este tema se realizar un estudio en profundidad del mismo y se
explicar cmo se pueden aprovechar los servicios que sus tipos ofrecen para facilitar la
manipulacin de los mismos. Por ello, salvo que se indique explcitamente lo contrario
puede considerar que todos los nuevos tipos aqu citados forman parte de dicho espacio.
Aunque cada sistema operativo puede tener su propio formato de nombres de fichero,
nosotros veremos el que se utiliza en la plataforma .NET, que bsicamente puede
considerarse que consiste en nombrar a los ficheros usando hasta 259 caracteres
Unicode imprimibles e interpretndose el nombre que se les d segn este formato:
<nombre>.<extensin>
<nombre> indica cul ha de considerarse que es en realidad el nombre del
<extensin> se indica cul es el tipo de fichero del que se trata. La idea es

fichero y en
que ficheros
con el mismo tipo de contenido tengan una extensin comn para que as sea ms fcil
identificar su contenido, por lo que lo que distinguir a unos ficheros de un tipo de otros
de su mismo tipo ser su <nombre> As, los ficheros de texto generados por Microsoft
Word tienen extensin doc, las compresiones con Winzip tienen extensin zip, etc.
Los ficheros se agrupan en directorios o carpetas, que pueden verse simplemente
como nombres comunes bajo los que se agrupan conjuntos de ficheros relacionados
entre s. Cada directorio pueden contener a su vez otros directorios, lo que hace que el
sistema de archivos adquiera una estructura jerrquica donde cada fichero o directorio
tiene como padre al directorio en que est contenido. Obviamente, para que sta sea una
estructura finita habr de existir un directorio raz que contenga a todos los dems y
no est contenido dentro de ninguno otro.
La utilidad de los directorios es doble:
1. Permiten organizar el sistema de archivos del ordenador de manera que sea ms
fcil localizar ficheros en l, pues evitan tener que buscarlos entre todos los ficheros
de la mquina y acotar las bsquedas tan slo a los incluidos en ciertos directorios.
2. Evitan conflictos de nombres, pues si cada aplicacin instala sus ficheros en un
directorio propio podrn coexistir en una misma mquina varios ficheros con el

mismo nombre siempre y cuando se almacenen en directorios distintos. Como ver,


la relacin entre ficheros y directorios es muy similar a la relacin que en C# se
establece entre tipos y espacios de nombres.
Como los nombres de ficheros, en .NET los nombres de directorios pueden ser
cualquier combinacin de caracteres Unicode imprimibles excepto \. Sin embargo,
ahora el lmite de caracteres que pueden tener est limitado a 248 y se ha reservado un
el nombre especial \ para el directorio raz.
Rutas de ficheros y directorios
Sintaxis en Windows
Como cada fichero y directorio est a su vez contenido dentro de otro directorio, para
identificarlo unvocamente habr que indicar el camino que lleva desde el directorio raz
hasta dicho fichero o directorio. Esto se le suele denominar su ruta completa, y en cada
sistema operativo sigue su propia sintaxis. Por ahora nos centraremos en la usada en los
sistemas Windows, aunque luego veremos el diseo de la BCL es bastante flexible y
permite escribir aplicaciones independientes de sistema operativo en este aspecto.
En Windows la sintaxis que sigue para escribir una ruta completa es de la forma:
<nombreDirectorioPadre>\<nombreFicheroODirectorio>

Como cada directorio puede estar contenido dentro de otro directorio, lo que se indica
en <nombreDirectorioPadre> es a su vez la ruta completa del directorio donde se
encuentra el fichero indicado en <nombreFicheroODirectorio>, y obviamente el primer
carcter de cualquier ruta completa ser el \ correspondiente al directorio raz. Por
ejemplo, la ruta completa de un fichero datos1.dat incluido dentro de un directorio
llamado Datos es \Datos\datos.dat, pero si dicho directorio se encontrase a su vez
contenido dentro de Programa entonces sera \Programa\Datos\datos.dat
Si al indicar el nombre de un fichero no se diese su ruta completa se considerara que la
ruta especificada es una ruta relativa a la posicin actual en el rbol de directorios. Es
decir, que se trata de una ruta en la que <nombreDirectorio> ha de considerarse que es el
directorio desde el que se la hace referencia al fichero. Por tanto, si desde el directorio
\Programa se quisiese hacer referencia al fichero datos.dat del ejemplo anterior
bastara indicar Datos\datos.dat, pero para hacerle referencia desde el directorio
\Programa\Datos bastara indicar datos.dat.
Adicionalmente a la sintaxis vista, en sistemas operativos como Windows donde se
puede trabajar con mltiples unidades de disco a cada una de las que se les asocia un
nombre diferente, la sintaxis anterior para las rutas completas se ve ampliada as:
<nombreUnidad>:<nombreDirectorio>\<nombreFichero>

Por ejemplo, A:\Programa\Datos\datos.dat hara referencia al fichero datos.dat


del directorio Datos incluido dentro del directorio Programa de la raz del dispositivo
de nombre A (unidad de disquete de 3.5), y C:\Programa\Datos\datos.dat se

referira al fichero datos.dat del directorio Datos incluido en el directorio Programa


de la unidad C (primera unidad de disco duro)
Si no se indicase la seccin <nombreUnidad>: se tomara como unidad del fichero la
denominada unidad actual, que no es ms que la unidad desde la que se le referencia.
Por tanto, \leeme.txt ser el fichero leeme.txt de la unidad actual, , y \ ser el
directorio raz de dicha unidad.
En caso de que se desee especificar un fichero que est ubicado en una mquina remota,
entones para hacerle referencia hay que usar el formato UNC1, cuya sintaxis es:
\\<nombreServidor>\<nombreFicheroCompartido>

Por ejemplo, \\pc1\Fuentes\principal.cs hara referencia al fichero principal.cs


de la carpeta compartida Fuentes ubicada en la mquina pc1.
Rutas independientes del sistema operativo
En el epgrafe anterior se explic el formato de las rutas siguiendo la sintaxis propia de
Windows pero se coment que la BCL incorpora mecanismos que permiten escribir las
aplicaciones de manera independiente del formato usado por el sistema operativa de la
mquina sobre la que se ejecute el programa. Pues bien, esos mecanismos consisten en
escribir dichas rutas usando las siguientes campos char static readonly del tipo Path:
Campo

Parte de la ruta que su valor representa


Separador de directorios. En Windows es \, en Unix es / y
Macintosh es :
AltDirectorySeparatorChar Carcter alternativo usable como separador de directorios.
En Windows y Macintosh es /, mientras que en Unix es \
PathSeparator
Separador entre rutas. Aunque en los sistemas operativos
ms comunes es ; podra variar en otros.
VolumeSeparatorChar
Separador de unidades lgicas. En Windows y Macintosh
es : (por ejemplo c:\datos) y en Unix /
DirectorySeparatorChar

Tabla 1: Campos de Path independizadores del formato de rutas de los sistemas operativos

En la versin de la BCL para cada sistema operativo se almacenar en estos campos el


carcter correspondiente en dicho sistema a la parte de la ruta que representan. De esta
manera, si en lugar de escribir directamente la ruta \datos\datos.dat escribimos:
String ruta = Path.DirectorySeparatorChar + datos +
Path.DirectorySeparatorChar + datos.dat;

Conseguiremos que la variable ruta almacene el formato de la misma segn corresponda


al sistema operativo sobre el que se ejecute el cdigo anterior. Es decir, mientras que en
Windows contendra \datos\datos.dat, en Unix contendra /datos/datos.dat

UNC = Universal Naming Convention o Convenio Universal de Nombrado

Operaciones con rutas


Aparte de la utilidad independizadora del sistema opeartivo vista, Path tambin es muy
til porque incluye un conjunto de miembros estticos especficamente diseados para
realizar cmodamente las operaciones ms frecuentes relacionadas con rutas.
La mayora de estos mtodos toman como parmetros cadenas de texto con las rutas
sobre la que se desean aplicar la operacin que facilitan. Si recuerda, como en Windows
el carcter usado como separador de directorios (\) coincide con el que C# usa como
indicador de secuencias de escape, en este lenguaje es incorrecto representar rutas como
c:\datos con literales como c:\datos. En su lugar hay tres alternativas:

Usar el campo independizador del sistema operativo, aunque ello tiene el problema
de que da lugar a cdigos poco compacto. Por ejemplo, para la ruta anterior habra
que representarla con c:+Path.DirectorySeparatorChar+\datos.

Duplicar los caracteres \ de los literales para que dejen de considerarse secuencias de
escape. As, la ruta de ejemplo anterior quedara como c:\\datos

Especificar la ruta mediante un literal de cadena plano, pues en ellos no se tienen en


cuenta las secuencias de escape. As, ahora la ruta del ejemplo quedara como
@c:\datos Esta ser la alternativa que ms se usar en el libro ya que para rutas
largas se hace muchos ms compacta y fcil de leer al no incluir tantos \ duplicados.

Dicho esto, el primer grupo de mtodos que veremos son los destinados a facilitarnos la
extraccin de informacin sobre las diferentes partes de la ruta que se les pase, que son:

string GetExtension(string ruta):

string GetFileName(string ruta):

string GetFileNameWithoutExtension(string ruta): Idem al anterior pero sin devolver


la extensin del fichero. O sea, dada la ruta @c:\datos\data.dat devolvera data.

string GetDirectoryName(string ruta):

string GetPathRoot(string ruta): Devuelve la raz de la ruta indicada. Por ejemplo, si


sta era @c:\datos\data.dat devolver c:\. Si slo queremos saber si la ruta dispone
de raz podemos usar mejor el bool IsPathRooted(string ruta) incluido para ello.

Devuelve la extensin, del fichero o directorio cuya


ruta se le indica incluido el carcter . usado como separador en ella. As, para una
ruta como @c:\datos\data.dat devolv .dat Si slo nos interesa saber si una ruta
contiene un fichero o directorio con extensin entonces podemos usar en su lugar el
bool HasExtension(string ruta) incluido especficamente para ello.
Devuelve el nombre del fichero o directorio cuya
ruta se le indica. Por ejemplo, dada la ruta @c:\datos\data.dat devolvera data.dat

Devuelve la ruta completa del directorio padre


del fichero o directorio cuya ruta se le indica. Por ejemplo, si dicha ruta es
c:\datos\data.dat devolver c:\datos.

Aparte de estos mtodos tambin se incluyen otros que permiten hacer tareas de
diversos tipo relacionadas con rutas como:

Modificar su extensin: El mtodo string ChangeExtension(string ruta, string


nuevaExtensin) puede usarse tanto para cambiar la extensin del directorio o
fichero cuya ruta se le indica como para aadirle una nueva si no tena ninguna o
quitarle la que tuviese si se le pasa null como nuevaExtensin.
As, Path.ChangeExtension(@"c:\datos\dato1.dat", txt) genera @"c:\datos\dato1.txt",
Path.ChangeExtension(@"c:\datos\dato1", dat) produce @"c:\datos\dato1.dat", y
Path.ChangeExtension(@"c:\datos\dato1.dat", null) devuelve @"c:\datos\dato1"

Obtenerse su versin completa: El mtodo string GetFullPath(string rutaRelativa)


de Path permite obtener la versin completa de la ruta relativa que se le indique. Por
ejemplo, si el directorio actual es d:\temp, al llamarle con Path.GetFullPath(f.txt)
devolver @d:\temp\f.txt

Concatenar rutas: Dadas dos rutas cualesquiera el mtodo string Combine(string


ruta1, string ruta2) devuelve el resultado de combinarlas siempre. Por ejemplo, dada
la llamada Path.Combine(@c:\datos, fichero.txt) su resulado ser c:\datos\fichero.txt
Al usar este mtodo tenga cuidado, pues si el resultado de combinar las rutas no da
lugar a una ruta vlida no se producir ninguna excepcin, sino que en su lugar se
devolver el valor indicado como ruta2. Por ejemplo, el resultado de una llamada
como Path.Combine(@c:\datos, @d:\patatas) es @d:\patatas.

Cuando use cualquiera de estos mtodos tenga siempre presente una cosa: permiten slo
operar con cadenas de texto que representan rutas pero no estn de en ninguna manera
relacionados con las ruta que representan. Su utilidad es permitir preparar nuevas rutas a
partir de otras ya existentes para luego poder, si se desea, podrn ser pasarse a otros
mecanismos para actuar fsicamente sobre ellas. Por ejemplo, modificar una ruta con
ChangeExtension() no implica que la ruta fsica en el dispositivo no voltil al que est
asociada quede tambin modificada, pero la cadena que se devuelva puede usarse luego
para modificarla mediante otros mtodos que iremos viendo a lo largo del tema.
Representacin de ficheros y directorios en .NET
Elementos comunes
En la plataforma .NET el trabajo con ficheros y directorios se hace, como no, siguiendo
un enfoque orientado a objetos en el que cada se los encapsula dentro de un objetos de
ciertos tipos: los ficheros en objetos FileInfo y los directorios en objetos DirectoryInfo.
Dado que ambos tipos de elementos dispone de muchas caractersticas , lo que se ha
hecho en la BCL es definir los tipos envolventes que los representan en base a una clase
abstracta comn llamada FileSystemInfo que facilite la escritura de cdigo genrico que
al slo usar sus caractersticas comunes pueda trabajar tanto con unos como con otros.
Segn su funcionalidad los miembros comunes ofrecidos por esta clase se agrupan en:

Determinacin de existencia: Saber si existe fsicamente o el fichero o directorio


representado por un objeto FileSystemInfo es tan sencillo como mirar el valor de su
propiedad bool Exists.

Borrado: Como tanto los ficheros como los directorios pueden ser borrados, en
FileSystemInfo se proporciona un mtodo Delete() mediante el que es posible borrar
ambos tipos de elementos del sistema de archivos. Sin embargo, al usarlo hay que
tener en cuenta que si el elemento a borrar es un directorio, ste ha de estar vaco
porque si no lanzar una IOException

Acceso a informacin: Se ofrecen propiedades de slo lectura que permiten tanto


obtener cadenas con el nombre completo del fichero o directorio representado
(FullName) como con su nombre principal (Name) o su extensin (Extension)
Como muchos sistemas operativos almacenan informacin sobre las fechas de
creacin, ltimo acceso y ltima modificacin de los ficheros y directorios de su
sistema de archivo, tambin se proporcionan propiedades (DateTime CreationTime,
DateTime LastAccessTime y DateTime LastWriteTime respectivamente) que permiten
acceder a ellas tanto para leerlas como para modificarlas.
Sin embargo, no todos los sistemas operativos almacenan esta informacin o si lo
hacen permiten modificarla. Si un sistema operativo no la admite, la lectura de estas
propiedades devolver null; y si las admite pero no permite modificarlas (como es el
caso de Windows 95 y Windows 98 con los directorios), las escrituras en ellas no
tendrn ningn efecto y sern completamente ignoradas.

Por otro lado, los sistemas operativos tambin suelen asociar una serie de atributos a
cada uno de sus ficheros y directorios que describen cmo ha de interpretarse bajo
determinadas circunstancias. Mediante la propiedad Attributes podemos acceder a
ellos tanto para leerlos como para modificarlos. Esta propiedad es de un tipo
enumerado llamado FileAttributes que admite los siguientes literales:
Literal
Normal
Directory
Hidden
System
ReadOnly
Encrypted
Compressed
Temporary
Archive

Offline

Significa que el fichero o directorio...


Es fichero normal y corriente, sin ningn otro atributo
Es un directorio, no un fichero
Ha de considerarse como oculto y no mostrarse en los
listados normales del contenido de su directorio padre
Forma parte del sistema operativo
Es de slo lectura
Est cifrado. Si es un directorio entones los ficheros que
se le aadan se cifrarn automticamente
Est comprimido
Es temporal y ser borrado por el sistema operativo
cuando finalice la ejecucin de la aplicacin que lo cre
Est archivado. Este atributo suelen activarlo las
aplicaciones de backup para marcar los archivos o
directorios de los que se dispone de copias de seguridad
No est disponible debido a que est almacenado en una
mquina remota con la que no se puede conectar

NotContentIndexed
SparseFile
ReparsePoint

No ha de ser indexado por los servicios de indexado de


contenidos de los que disponga el sistema operativo
Es un fichero esparcido, lo que significa que es un fichero
grande cuyo contenido son casi todo ceros.
Contiene un bloque de datos asociado con informacin
configurable por el usuario

Tabla 2: Literales indicadores de atributos de ficheros de FileAttributes

Tenga en cuenta que la tabla anterior abarca atributos propios de muy diversos
sistemas operativos que no tienen porqu estar presentes en todos los sistemas
operativo. Adems, aunque pueda darle la sensacin de que cada fichero o directorio
slo puede disponer de slo uno de los atributos sealados porque su propiedad
Attributes devuelve un nico objeto, esto no es as porque FileAttributes ha sido
definido como una enumeracin de flags y sus objetos pueden almacenar
combinaciones OR de sus literales. Por ejemplo, un mtodo que convierta ficheros o
directorios en ocultos y de slo lectura puede escribirse as:
public void ConvierteEnOcultoDeSoloLectura(FileSystemInfo f)
{
f.Attributes = FileAttributes.Hidden | FileAttributes.ReadOnly;
}

Dado que los atributos asociados a un fichero pueden ir cambiando dinmicamente,


es importante que siempre que se acceda a ellos se obtenga una versin actualizada
de los mismos. Para conseguirlo basta llamar al mtodo Refresh() del objeto
FileSystem que lo representa.
A modo de resumen a continuacin se muestra un ejemplo de mtodo que hace uso
de todas estas propiedades para mostrar por la consola la informacin asociada al
fichero o directorio que representa el objeto FileSystemInfo que se le indique:
public static void muestraInfo (FileSystemInfo f)
{
if (f.Exists)
{
Console.WriteLine("Nombre completo: {0}", f.FullName);
Console.WriteLine("Nombre : {0}", f.Name);
Console.WriteLine("Extensin : {0}", f.Extension);
Console.WriteLine("Fecha creacin: {0}", f.CreationTime);
Console.WriteLine("Fecha ltimo acceso: {0}", f.LastAccessTime);
Console.WriteLine("Fecha ltima modificacin: {0}", f.LastWriteTime);
Console.WriteLine("Atributos: {0}", f.Attributes.ToString("F"));
}
else
throw new FileNotFoundException();
}

Aunque el trabajo con ficheros y con directorios es muy similar de hecho, como ya
hemos visto, los tipos que los representan derivan de una clase comn FileSystemInfo-,
tambin es cierto que cada uno de ellos dispone tiene una serie de caractersticas propias
que lo diferencian del otro. Por esta razn, en la BCL se ha definido como abstracta
FileSystemInfo y se han incluido subclases suyas no abstractas FileInfo y DirectoryInfo

que aaden a los miembros comunes heredados de su padre miembros relacionados con
funcionalidades particulares de cada tipo de elemento.
Elementos especficos de directorios
Para crear el objeto DirectoryInfo que represente a un determinado directorio basta pasar
como parmetro de su constructor una cadena con la ruta relativa o completa- del
mismo. Por ejemplo, un objeto d que represente al directorio c:\Pruebas se crea as:
DirectoryInfo d = new DirectoryInfo(@c:\Pruebas);

Este directorio no tiene porqu existir, pues como se ver ms adelante es posible crear
directorios a travs de objetos DirectoryInfo que representen directorios no existentes.
Como ya se ha dicho, este objeto es en realidad de una subclase de FileSystemInfo y por
tanto dispondr de todos los miembros definidos en ella adems de los explcitamente
definidos en DirectoryInfo. Estos miembros adicionales le aportan las funcionalidades
especficas del trabajo con directorios descritas a continuacin.
Informacin adicional sobre el directorio
A la informacin sobre un directorio que proporciona FileSystemInfo (nombre, fechas,
atributos, etc.), DirectoryInfo aade los siguientes datos:

Directorio padre: Aunque a partir del nombre completo de un directorio que la


propiedad FullName de FileSystemInfo proporciona puede deducirse con facilidad
cul es su directorio padre, DirectoryInfo proporciona una propiedad DirectoryInfo
Parent {get;} que permite consultar a esta informacin con mayor comodidad.

Obviamente no tiene mucho sentido aplicar esta propiedad a un DirectoryInfo que


represente al directorio raz ya que ste carece de padre, y hacerlo devuelve null.

Directorio raz: Si en lugar del padre inmediato de un directorio lo que se desea es


obtener su ancestro ms antiguo (directorio raz) puede consultarse cul es ste
directamente a travs de su propiedad DirectoryInfo Root {get;}

Acceso al contenido de un directorio


Los objetos DirectoryInfo cuentan con mtodos con los que pueden obtenerse tablas con
todos los ficheros (FileInfo[] GetFiles()), directorios (DirectoryInfo[] GetDirectories()), o
ficheros y directorios (FileSystemInfo[] GetFileSystemInfos()) contenidos en el directorio
que representan. Por ejemplo, todos los ficheros de c:\Windows pueden obtenerse as:
FileInfo[] ficheros = new DirectoryInfo(@C:\Windows).GetFiles();

Cada uno de estos mtodos tiene una sobrecarga que admite como parmetro una
cadena con la que pueden filtrarse los nombres de los ficheros o directorios que se
desean obtener. Dicho filtrado consiste en usar los caracteres ? y * como comodines con

los que representar, respectivamente, cualquier carcter y cualquier nmero de


cualesquiera caracteres. As por ejemplo, si en vez de todos los ficheros de c:\windows
queremos obtener tan slo los ejecutables (extensin .exe) podemos hacer lo siguiente:
FileInfo[] ficheros = new DirectoryInfo(@C:\Windows).GetFiles(*.exe);

Operaciones tpicas de manipulacin de directorios


Todo sistema operativo proporciona mecanismos mediante los que sus usuarios pueden
realizar modificar la estructura de directorios del sistemas de archivos de las mquinas,
realizando tareas tales como crear nuevos directorios, eliminar directorios existentes,
etc. Pues bien, para poder realizar este tipo de tareas de una manera automatizada desde
nuestro programas, la clase DirectoryInfo incluye el siguiente conjunto de mtodos que
permiten realizarlas programticamente:

Creacin de directorios: Para crear fsicamente el directorio representado por un


objeto DirectoryInfo basta aplicarle al mismo su mtodo Create(). Por ejemplo, para

crear un subdirectorio Test dentro c:\Windows se puede hacer lo siguiente:


new DirectoryInfo(@c:\Windows\Test).Create();

Si el directorio a crear de esta forma ya existe no se producir ninguna excepcin.


Simplemente la ejecucin de Create() no tendr absolutamente ningn efecto.

Delete():

MoveTo(string directorioDestino):

Borra el directorio vaco representado por el objeto DirectoryInfo sobre el


que se aplica. Si ese directorio no est vaco entonces hay que pasarle true como
parmetro para indicar explcitamente que deseamos tambin borrar su contenido,
pues como mecanismo de seguridad si no lo hicisemos se lanzara una IOException
Mueve el directorio y todos sus contenidos al
nuevo directorio que se le indica. Por ejemplo, para trasaladar el contenido de un
directorio c:\Datos1 en otro directorio c:\Datos2 bastara hacer:
DirectoryInfo d = new DirectoryInfo(@c:\Datos1);
d.MoveTo(@c:\Datos2);

El directorio de destino especificado no debe existir, pues si no se lanzara una


IOException. Si lo que se desea es trasladar el contenido de un directorio a otro ya
existente entonces hay que especificar en la llamada a MoveTo() el nombre que se
desea que tenga en el directorio de destino. Es decir, si queremos copiar c:\Datos1
dentro de un directorio c:\Datos2 ya existente entonces habr que hacer:
DirectoryInfo d = new DirectoryInfo(@c:\Datos1);
d.MoveTo(@c:\Datos2\Datos1);

Elementos especficos de ficheros


Del mismo modo que para representar especficamente directorios se proporcionan
objetos DirectoryInfo, para representar ficheros se proporcionan objetos FileInfo Este
tipo tambin desciende de FileSystemInfo y se utiliza de forma similar a los objetos

ya vistos. As por ejemplo, para crear un objeto f de tipo FileInfo que


represente a un fichero c:\winnt\wplog.txt se puede hacer:
DirectoryInfo

FileInfo f = new FileInfo(@c:\winnt\wplog.txt);

A travs de este objeto se pueden hacer las operaciones tpicas de creacin (Create()),
borrado (Delete()) y movimiento (MoveTo()) de ficheros de manera anloga a como se ha
visto que se hacen con los directorios. Adicionalmente se ha aadido un mtodo
CopyTo() que funciona de manera similar a MoveTo() slo que en vez de cambiar al
ubicacin del fichero lo que hace es crear una copia del mismo en la ubicacin indicada.
De nuevo, para evitar sobreescrituras no deseadas ambos mtodos no admiten que el
fichero de destino especificado ya exista lanzan IOExceptions si fuese as, pero ello
tambin puede cambiarse pasndoles true como segundo parmetro.
En lo referente al acceso a informacin adicional sobre el fichero hay que sealar que en
este caso la analoga con el caso de los directorios no es tan pura, pues para obtener el
directorio padre de un fichero la propiedad a usar del objeto FileInfo que lo representa
no se llama ahora Parent sino DirectoryName. Adems, tambin se ofrece una segunda
propiedad llamada Directory que permite acceder a esa misma informacin pero en
forma de objeto DirectoryInfo en lugar de cadena de texto.
Como habr adivinado, aparte de estos miembros los objetos FileInfo deben tambin de
ofrecer mecanismos que permitan acceder al contenido de los ficheros que representan,
ya sea para leerlo o para modificarlo. Pues bien, precisamente a explicar cmo se realiza
eso es a lo que esta destinado los siguientes epgrafes.
Flujos de entrada-salida
La forma con la que se trabaja con ficheros en la plataforma .NET est ntimamente
ligada al concepto de flujo de entrada-salida, que consiste en tratar su contenido como
si de una secuencia ordenada de bytes se tratase. Este concepto no est ligado en
exclusividad a los ficheros, sino que es un concepto abstracto aplicable a otros tipos de
almacenes de informacin tales como conexiones de red o buffers en memoria.
En realidad se distinguen dos tipos de flujos:

Flujos base: Trabajan directamente con algn tipo de medio fsico, como puede ser
una porcin de memoria, de espacio en disco o una conexin a red.
Flujos intermedios: No trabajan con medios fsicos directamente sino envuelven a
otros flujos a los que proporcionan diferentes caractersticas, pudindose combinar
entre s de manera que el flujo base que haya tras ellos pueda verse beneficiado por
todas las funcionalidades que ofrezcan todas ellos. Ejemplos son los flujos que
proporcionan encriptacin de datos o buffers de almacenamiento intermedio.

En la BCL todos los tipos relativos al trabajo con flujos se encuentran agrupados dentro
del espacio de nombres System.IO Un tipo muy importante entre ellos es Stream, que es
la clase abstracta base de todos los flujos y les proporciona los miembros bsicos que
permiten trabajar con ellos. Estos miembros se explicarn en los epgrafes que siguen.

Lectura y escritura
Como es lgico, todo flujo dispondr de mecanismos mediante los que se le puedan
extraer y aadir bytes. No todos los flujos tienen porqu admitir ambas operaciones, por
lo que para saber qu operaciones admite cada flujo concreto Stream aade a todos ellos
dos propiedades bool CanRead y bool CanWrite que permite consultarlo.
Para la lectura se ofrecen mtodos ReadByte() y Read() que, respectivamente, permiten
extraerles uno o varios bytes (se almacenaran en una tabla byte[]); y para la escritura se
hace lo mismo con WriteByte() y Write(). Obviamente los mtodos de lectura slo sern
aplicables flujos que admitan lectura y los de escritura slo sern aplicables a los que
admitan escritura, y si se intentan aplicar a flujos que no los admitan se lanzarn
excepciones NotSupportedException.
Los mtodos anteriores funcionan sncronamente, lo que significa que tras llamarlos el
cdigo que los llam se quedar en espera de que terminen de ejecutarse. Sin embargo,
como las operaciones de entrada-salida suelen ser lentas tambin se ofrecen parejas de
mtodos BeginRead()-EndRead() y BeginWrite()-EndWrite() que permiten realizarlas
asncronamente de manera que mientras se estn realizando el cdigo llamador pueda
seguir ejecutndose y realizando otras tareas. Estos mtodos funcionan anlogamente a
como lo hacen las parejas de mtodos BeginInvoke y EndInvoke de los delegados.
Tras cada lectura la siguiente llamada a estos mtodos devolvera los bytes del flujo
siguientes a los ltimos ledos, y para detectar cuando se alcance el final del flujo basta
mirar su valor de retorno, pues en ese caso ReadByte() devolver un 1 y Read() un 0.
Respecto a la escritura, cada vez que se escriba se escribir a continuacin de los
ltimos bytes escritos en el flujo.
Movimiento por el flujo
Por defecto, la primera operacin que se realice sobre un flujo se aplica a su comienzo y
las siguientes se aplican tras la posicin resultante de la anterior. Es decir, en cada
lectura o escritura se lee o escribe a continuacin de la ltima posicin accedida, por lo
que antes de acceder a una determinada posicin habr que pasar antes por las previas.
A esto se le conoce como acceso secuencial.
Sin embargo, hay situaciones en las que puede resultar interesante poderse escribir o
leer de cualquier posicin del flujo sin necesariamente tener que pasar antes por sus
anteriores. A esto se le conoce como acceso aleatorio, y como no todos los flujos tienen
porqu admitirlo, cada flujo dispone de una propiedad de slo lectura bool CanSeek que
indica si lo admite.
Si un flujo admite acceso aleatorio le sern aplicables los miembros de Stream relativos
a dicho tipo de acceso que a continuacin se muestran, mientras que si no lo admite la
utilizacin de los mismos provocar excepciones NotSupportedException:
long Length {get;}: Nmero de bytes almacenados en el flujo (tamao del
flujo)

SetLength(long tamao): Cambia el nmero de bytes almacenados en el


flujo por el indicado. Si este nuevo tamao es inferior al que tena se truncarn
los bytes que no sobren, mientras que si es superior se rellenarn los nuevos
bytes con valores inespecificados y conviene darles a mano valores por defecto.
A diferencia de Length, para poderse ejecutar este miembro no slo se necesita
que el flujo admita acceso secuencial sino que adems se necesita que admita la
realizacin de operaciones de escritura.
long Position: Nmero del byte actual en el flujo. Dndole valores
podemos movernos por el flujo tal y como muestra el siguiente mtodo de
ejemplo:
public void MuestraByte(Stream flujo, long nmeroByte)
{
int posicinAnterior = flujo.Position;
if (nmeroByte > flujo.Length)
throw new ArgumentOutOfBoundsException();
flujo.Position = nmeroByte;
Console.WriteLine(Byte {0} = {1}, nmeroByte, flujo.ReadByte());
flujo.Position = posicinAnterior;
}

Este mtodo imprime en la consola el byte que se le indique del flujo que se le
pase como parmetro. Ntese que, para asegurar que su ejecucin sea inocua y
no altere el flujo se controla a su comienzo cul era la posicin inicial en el flujo
y a su final se le restaura a dicha posicin. Por otra parte, tambin se controla
que el nmeroByte indicado se encuentre dentro del flujo, pues sino aunque la
asignacin flujo.Position = nmeroByte sera vlida y no producira excepciones, el
flujo habra quedado en un estado inconsistente y ReadByte() devolvera 1.
long Seek(long posicin, SeekOrigin inicio): Permite colocarnos en un
byte del flujo determinado. La posicin de ste se indica de manera relativa
respecto a la posicin de inicio sealada por su segundo parmetro, que es de un
tipo de enumeracin cuyos posibles literales son Current (posicin actual en el
flujo), Begin (inicio del flujo) y End (final del flujo) Por ejemplo:
// Coloca justo antes del comienzo del flujo
flujo.Seek(0, SeekOrigin.Begin);
// Coloca justo despus del final del flujo
flujo.Seek(0, SeekOrigin.End);
// No nos movemos de la posicin actual
flujo.Seek(0, SeekOrigin.Current);
// Coloca dos bytes a continuacin de la posicin actual en el flujo
flujo.Seek(2, SeekOrigin.Current);
// Coloca dos bytes antes de la posicin actual en el flujo
flujo.Seek(-2, SeekOrigin.Current);
// Coloca en el segundo byte del flujo
flujo.Seek(2, SeekOrigin.Begin);

// Coloca en el penltimo byte del flujo


flujo.Seek(-2, SeekOrigin.End);

Volcado de datos en flujos


Muchos flujos trabajan internamente con buffers donde se almacenan temporalmente
los bytes que se solicitan escribir en ellos hasta que su nmero alcance una cierta
cantidad, momento en que son verdaderamente escritos todos a la vez en el flujo. Esto
se hace porque las escrituras en flujos suelen ser operaciones lentas e interesa que se
hagan el menor nmero de veces posible.
Sin embargo, hay ocasiones en puede interesar asegurarse de que en un cierto instante
se haya realizado el volcado fsico de los bytes en un flujo, ya sea porque de ello
dependa el correcto funcionamiento de otras partes de la aplicacin o se sepa que no se
volver a escribir en l hasta que dentro de mucho. En esos casos puede forzarse el
volcado llamando al mtodo Flush() del flujo, que vaca por completo su buffer interno.
Cierre de flujos
Adems de memoria, muchos flujos acaparan recursos extra que interesa liberar una vez
dejen de ser tiles. Por ejemplo, las conexiones de red acaparan sockets y los ficheros
acaparan manejadores de ficheros del sistema operativo, que son recursos limitados y si
no liberarlos podra impedir la apertura de nuevos ficheros o conexiones de red.
Como podr adivinar, en principio ello es tarea del mtodo Dispose() con el que todo
flujo cuenta como resultado de implementar la interfaz IDisposable que se usa en .NET
como estndar recomendado con el que liberar recursos de manera determinista. Sin
embargo, por similitud con las APIs de manejo de flujos de otros lenguajes a las que
muchos programadores estarn acostumbrados, en .NET la clase Stream tambin
dispone de un mtodo Close() que hace lo mismo.
En cualquier caso, lo que es importante saber es que una vez cerrado el flujo ya sea
llamando a Dispose() o a Close()- ya no es posible hacer uso del mismo y cualquier
intento de acceder a sus miembros provocar excepciones ObjectDisposedException

Flujos de entrada-salida en ficheros


El contenido de los ficheros se trata en la plataforma .NET como flujos de entradasalida, por lo que para manipularlo se usarn los mtodos de Stream ya vistos. Sin
embargo, si recuerda dicha clase es abstracta y por tanto es imposible crear objetos de
ella. Como habr adivinado, lo que en realidad se hace es usar una subclase suya
especializada en el trabajo con flujos asociados a ficheros. sta clase es FileStream.
Creacin de objetos FileStream

Creacin a partir de objetos FileInfo

A crear el FileStream que permita manipular el contenido de un fichero se le denomina


abrir el fichero, y una forma sencilla hacerlo es mediante el mtodo Open(FileMode
modoApertura) del objeto FileInfo que lo representa. Su parmetro indica qu ha de
hacerse si el fichero a abrir ya existe, y es de un tipo enumerado cuyos literales son:
Literal de FileMode
Open

OpenOrCreate
CreateNew
Create
Truncate
Append

Modo de apertura
Abre el fichero presuponiendo existe. Si no fuese as se lanza una
FileNotFoundException.
Si el fichero no existe, lo crea
Abre el fichero presuponiendo que no existe y crendolo. Si
existiese se lanzara una IOException
Crea el fichero, y si ya exista lo sobreescribe
Abre el fichero presuponiendo que existe y borrando todo su
contenido. Si no existiese se lanzara una FileNotFoundException
Abre el fichero en modo de concatenacin, lo que significa que
slo podr escribirse a su final. Si el fichero no existe, lo crea.

Tabla 3: Modos de apertura de ficheros (enumeracin FileMode)

Por ejemplo, para abrir un fichero preexistente llamado c:\datos.dat puede hacerse:
FileInfo fichero = new FileInfo(@c:\datos.dat);
FileStream contenidoFichero= fichero.Open(FileMode.Open);

Adems del modo de apertura, tambin puede resultar interesante especificar qu es lo


que deseamos hacer con el contenido del fichero: si slo leerlo, si slo escribir en l o si
ambas cosas; pues muchos sistemas operativos permiten asociar a los ficheros permisos
de acceso con los que puede restringirse qu puede hacerse con cada uno. Como si no
indicamos nada se considerar que queremos tanto leer como escribir en el fichero, y
para especificar cualquier otra cosa basta pasar a Open() un parmetro adicional del tipo
enumerado FileAccess que indique como se desea abrirlo. Los literales de este tipo son:
Literal de FileAccess
Read
Write
ReadWrite

Modo de acceso solicitado


Slo lectura del contenido
Slo escritura de contenido
Tanto lectura como escritura de contenido

Tabla 4: Modos de acceso a ficheros (enumeracin FileAccess)

Fjese que algunos modos de aperturas de ficheros, por su propia definicin, requieren
de ciertos permisos de acceso. Por ejemplo, un fichero slo puede abrirse en modo de
concatenacin (FileMode.Append) si se tiene permiso de acceso FileAccess.Write; y ni
siquiera se permite abrirlo si se tiene FileAccess.ReadWrite porque es absurdo leer de l.
Un ejemplo de cmo abrir un fichero preexistente en modo de slo lectura es:
FileInfo fichero = new FileInfo(@c:\datos.dat);
FileStream contenidoFichero= fichero.Open(FileMode.Open, FileAccess.Read);

Ahora bien, como es bastante frecuente abrir en modo de slo lectura o de lecturaescritura ficheros preexistentes, a los objetos FileInfo tambin se les ha dotado de un par
de mtodos FileStream OpenRead() y FileStream OpenWrite() que permiten, de manera
respectiva, abrir as los ficheros que representan. Por tanto, en el ejemplo anterior en
realidad podra haberse escrito de la siguiente manera mucho ms compacta:
FileInfo fichero = new FileInfo(@c:\datos.dat);
FileStream contenidoFichero= fichero.OpenRead();

Por otra parte, la mayora de los sistemas operativos permiten controlar la forma en que
diferentes procesos puede acceder simultneamente a sus ficheros. Por seguridad, para
evitar problemas de concurrencia no se permite que mltiples hilos puedan compartir
ficheros abiertos con cualquiera de las sobrecargas de Open() vistas. Sin embargo, ello
puede cambiarse especificando un tercer parmetro de tipo enumerado FileShare, cuyos
posibles literales son:
Literal de FileShare
None
Read
Write
ReadWrite

Modo de comparticin
No se puede compartir
Puede compartirse con hilos que slo quieran leerlo
Puede compartirse con hilos que slo quieran escribir en l
Puede compartirse con cualquier otro hilo

Tabla 5: Modos de comparticin de ficheros (enumeracin FileShare)


Creacin con el constructor de FileStream

Hasta ahora todas las formas de crear objetos FileStream que hemos visto eran mtodos
proporcionados por la clase FileInfo que encapsula ficheros, lo que nos obliga a siempre
tener que crear un objeto de este tipo para poder acceder al contenido de un fichero.
Como ello puede resultar ineficiente, la propia clase FileStream tambin proporciona
una familia de constructores que permiten crear objetos suyos mucho ms directamente.
As, los siguientes constructores son equivalentes a las tres sobrecargas de Open() vistas:
FileStream(string rutaFichero, FileMode modoApertura)
FileStream(string rutaFichero, FileMode modoApertura, FileAccess modoAcceso)
FileStream(string rutaFichero, FileMode modoApertura, FileAccess modoAcceso,
FileShare modoComparticin)

Por ejemplo, para leer el contenido del fichero c:\datos.dat podemos generar un
FileStream as:
FileStream contenidoFichero = new FileStream(@c:\datos.dat, FileMode.Open,
FileAccess.Read);

Manipulacin del contenido de los ficheros


Como ya se ha repetido hasta la saciedad, el contenido de los ficheros es tratado en la
plataforma .NET como flujos de entrada-salida representados por objetos FileStream; y
dado que esta clase deriva de Stream (clase padre de todos los flujos), para manipularlo
podemos usar cualquiera de los miembros ya vistos para sta ltima.

En principio los ficheros admiten tanto lectura como escritura y movimiento por su
contenido, por lo que las propiedades CanRead, CanWrite y CanSeek de los objetos
FileStream valdrn siempre true y los mtodos ReadByte(), Read(), WriteByte(), Write() y
Seek() heredadas de Stream sern plenamente utilizables. Sin embargo, como es obvio,
esto depender de cmo hayamos abierto el fichero, pues si por ejemplo lo abrimos en
modo slo lectura, CanWrite devolver false y mtodos como WriteByte() o Write()
dejarn de funcionar y provocarn NotSupportedExceptions.
Dada la relativa lentitud de las operaciones de acceso a ficheros, para mayor eficiencia
durante su realizacin se ha optado por asociar un buffer a cada FileStream en el que los
bytes que se soliciten escribir se irn almacenando hasta alcanzar un cierto nmero para
entonces escribirlos todos de una vez. Por defecto el tamao de este buffer es de 8192
bytes, pero puede cambiarse usando el siguiente constructor para crear el FileStream:
FileStream(string rutaFichero, FileMode modoApertura, FileAccess modoAcceso,
FileShare modoComparticin, int tamaoBuffer)

Al especificar as un tamao de buffer hay que tener en cuenta que no conviene darle un
tamao inferior a 8 bytes, pues sera tan pequeo que prcticamente no se ganara nada
de eficiencia. Es ms, cualquier valor inferior a dicha cantidad que se le d ser
ignorado y se tomar en su lugar un tamao de 8 bytes.
Lo que si es importante recordar es que debido a este buffer, cuando queramos estar
seguros de que en un momento concreto se haya volcado todo su contenido en el fichero
al que est asociado, hemos de llamar al mtodo Flush() Por cierto, por si lo estaba
pensando: cuando hayamos terminado de usar un FileStream y vayamos a cerrarlo no
tenemos porqu preocuparnos de vaciar este buffer, pues el mtodo Close() usado para
ello se encarga automticamente de hacerlo.
Ntese que hasta ahora no se ha dicho nada acerca de las versiones asncronas de los
mtodos de lectura-escritura heredados de Stream. Esto se debe a que por defecto el
acceso al contenido de los ficheros se realiza siempre de manera sncrona, aunque se
usen los mtodos anteriores. Esto se debe a que para ficheros pequeos es la forma de
acceso ms eficiente, que son los ms frecuentemente utilizados. Sin embargo, como al
trabajar con ficheros grandes puede resultar ms eficiente leerlos asncronamente para
poder realizar otras tareas mientras tanto, tambin se permite activar el acceso
asncrono. Para ello basta crear el FileStream usando el siguiente constructor:
FileStream(string rutaFichero, FileMode modoApertura, FileAccess modoAcceso,
FileShare modoComparticin, int tamaoBuffer, bool asncrono?)

Aunque el parmetro asncrono? de este constructor indica si se desea habilitar el acceso


asncrono al contenido del fichero a travs de las parejas de mtodos BeginWrite()EndWrite(), BeginRead-EndRead(), en realidad nada asegura que vaya a realizarse as
puesto que no todos los sistemas operativos soportan dicho tipo de acceso (por ejemplo,
Windows 9X y Windows ME) y en los que no lo hagan seguirn funcionando de forma
sncrona. Adems, si el acceso que se realiza es demasiado pequeo (menos de 64 KB
en Windows) tambin puede ocurrir que el sistema operativo decida que es ms
eficiente realizarlo de manera sncrona. Por estas razones, para poder consultar cmo se
pueden realizar los accesos a cada FileStream stos disponen de una propiedad llamada
bool isAsync {get;} que indica si se les puede acceder asncronamente.

Aplicacin de ejemplo de acceso a ficheros: visor hexadecimal


A modo de ejemplo que sintetice todo lo visto hasta ahora respecto al acceso a ficheros
y su contenido vamos a desarrollar una sencilla aplicacin de consola que acte como
un visor hexadecimal que muestre toda la informacin relativa al fichero que se le pase
como argumento al llamarla as como los bytes que contiene. Su cdigo es el siguiente:
using System;
using System.IO;
public class VisorHexadecimal
{
static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Error: Llamada incorrecta. Formato correcto de uso:");
Console.WriteLine("\n\t VisorHexadecimal <rutaFichero>");
Environment.Exit(1);
}
FileInfo fichero = new FileInfo(args[0]);
if (!fichero.Exists)
Console.WriteLine("Error: Fichero {0} no encontrado", args[0]);
else
{
muestraInfo(fichero);
muestraContenido(fichero);
}
}
public static void muestraInfo (FileInfo fichero)
{
Console.WriteLine("Nombre : {0}", fichero.Name);
Console.WriteLine("Extensin : {0}", fichero.Extension);
Console.WriteLine("Nombre completo: {0}", fichero.FullName);
Console.WriteLine("Directorio padre: {0}", fichero.DirectoryName);
Console.WriteLine("Fecha creacin: {0}", fichero.CreationTime);
Console.WriteLine("Fecha ltimo acceso: {0}", fichero.LastAccessTime);
Console.WriteLine("Fecha ltima modificacin: {0}", fichero.LastWriteTime);
Console.WriteLine("Atributos: {0}", fichero.Attributes.ToString("F"));
Console.WriteLine("Tamao: {0}", fichero.Length);
}
public static void muestraContenido(FileInfo fichero)
{
using (FileStream contenido = fichero.OpenRead())
{
if (fichero.Length==0)
Console.WriteLine("Sin contenido");
else
{
byte[] bytesLeidos = new byte[16];
long nBytesLeidos = contenido.Read(bytesLeidos, 0, 16);
while (nBytesLeidos!=0)
{

// Mostramos posicin en el fichero del primer byte de la lnea


Console.Write("\n{0:X8}:", contenido.Position-16);
// Mostramos lnea de bytes
for (int i=0; i<nBytesLeidos; i++)
Console.Write(" {0:X2}", bytesLeidos[i]);
// Separamos los bytes de sus respectivos caracteres asegurando que siempre
// se mantenga el mismo espaciado entre ellos
for (int i=0; i<16-nBytesLeidos; i++)
Console.Write(" ");
Console.Write(" | ");
// Mostramos caracteres de los bytes de la lnea. Los no imprimibles se
// muestran como un carcter de punt (.)
for(int i=0; i<nBytesLeidos; i++)
{
char carcterActual = (char) bytesLeidos[i];
if (Char.IsControl(carcterActual))
Console.Write('.');
else
Console.Write(carcterActual);
}
nBytesLeidos = contenido.Read(bytesLeidos, 0, 16);
Console.WriteLine();
}
}
}
}
}

Como se puede ver, el cdigo de esta aplicacin es bastante sencillo: simplemente se


comprueba que se le pase algn argumento y si as se toma como ruta de un fichero del
que se mostrar su informacin asociada y contenido.
La informacin asociada se muestra a travs del mtodo muestraInfo(), que es similar al
mtodo de ejemplo slo que tambin muestra informacin especfica de los ficheros y
no comn con los directorios, como el tamao de su contenido.
Para mostrar el contenido del fichero se usa el mtodo muestraContenido(), que lo abre
en modo de slo lectura y lee su contenido byte a byte mostrando por pantalla los bytes
as ledos en lneas de 16. Cada una de estas lneas va precedida del nmero que su
primer byte ocupa en el fichero, para as facilitar la determinacin de la ubicacin en el
fichero de cada uno de sus bytes, y seguida de la representacin de sus bytes en forma
de caracteres, para as facilitar la lectura de ficheros de texto. Por ejemplo, al pasar a
este programa como argumento el propio fichero de su cdigo fuente se mostrar:
Fjese que no cierra explcitamente el FileStream tras terminar de usarlo. Esto se debe a
que en su lugar se ha usado la novedosa instruccin using de C#, que lo hace
automticamente (en realidad, el cuerpo de Close() es solo una llamada al mtodo
Dipose() al que llama using) Sin embargo, usando otros lenguajes s que podra ser
necesario llamar explcitamente a Close()

Control de concurrencia en accesos a ficheros


Ya se ha explicado que a travs del parmetro de tipo FileShare del constructor de
FileStream puede controlarse si se permite el acceso concurrente a un fichero en funcin
del tipo de operaciones que quieran realizarse con l.
Sin embargo, este mecanismo controla de manera global el acceso al fichero y no es
muy eficiente cuando se conoce de antemano qu trozos concretos de cada fichero va a
necesitar cada proceso. Por ello, para estos casos se ha incluido en FileStream la
siguiente pareja de mtodos que ofrecen la posibilidad de que cada hilo pueda bloquear
temporalmente el acceso a determinadas secciones de los ficheros:

Lock(long byteInicial, long nBytes): Bloquea el acceso a la seccin del fichero que
comienza en el byteInicial indicado y cuyo tamao en bytes es nBytes
Unlock(long byteInicial, long nBytes): Desbloquea el acceso a la seccin del fichero

indicada. Si dicha seccin no estaba bloqueada no se preocupe, pues no pasar nada.

Acceso nativo a ficheros


Muchos sistemas operativos usan internamente manejadores de archivos para trabajar
con los ficheros, y los FileStream de la plataforma .NET no hacen ms que encapsular el
uso de stos ofreciendo una visin orientada objetos y ms cmoda de los mismos. Sin
embargo, este nivel de indireccin adicional implica tambin que la manipulacin de los
ficheros ser ms ineficiente que si se usasen directamente los manejadores de archivos.
Para los casos en que la eficiencia en los accesos a archivos sea crtica, .NET tambin se
da la posibilidad de trabajar directamente con sus manejadores asociados y podrselos
pasar como parmetros a mtodos nativos que trabajen con ellos ms eficientemente.
Esto se consigue mediante la propiedad Handle de los objetos FileStream, que devuelve
un objeto System.IntPtr que representa al manejador que encapsula.
Hay que tener en cuenta que los manejadores as devueltos pueden ser utilizados por
ciertas funciones nativas del sistema operativo, como las funciones ReadFile() y
WriteFile() de la API Win32, pero ello no significa que tambin puedan usarse con
funciones que esperen descriptores de ficheros tales como la fread() de la librera de C.
Recprocamente, a partir de un manejador de archivo tambin es posible generar un
FileStream que lo encapsule. Para ello, basta usar cualquiera de las sobrecargas de los
constructores de FileStream que se muestran a continuacin:
FileStream(IntPtr manejador, FileAccess modoAcceso)
FileStream(IntPtr manejador, FileAccess modoAcceso, bool dueo?)
FileStream(IntPtr manejador, FileAccess modoAcceso, bool dueo?, int tamaoBuffer)
FileStream(IntPtr manejador, FileAccess modoAcceso, bool dueo?, int tamaoBuffer,
bool asncrono?)

Aunque la mayora de los parmetros de estos constructores ya los conoce, dueo? es


nuevo e indica si se ha de considerar que el FileStream que se cree es el dueo del
manejador y por tanto su cierre ha de implicar el cierre de ste tambin. Por defecto el

primer constructor considera que lo es, y si no desea ocurra esto basta con que utilice
cualquiera de los dems constructores dndole el valor false a dicho parmetro.
Fichero binarios
La forma de acceder a ficheros que hasta ahora hemos visto tiene una seria limitacin, y
es que trabaja con la informacin a nivel de bytes, lo cual no suele ser muy conveniente
en tanto que generalmente en las aplicaciones no suele trabajarse directamente con bytes
sino con objetos ms complejos formado por mltiples bytes. Por ejemplo, dado que un
int ocupa 4 bytes, para almacenar su valor en un fichero usando un FileStream habra
que realizar una operacin WriteByte() por cada uno de sus bytes o descomponer todos
los en una tabla byte[] y escribirlos de una vez con una llamada a Write()
Como esta no es ni por asomo una solucin cmoda, en System.IO se han incluido un
par de tipos llamados BinaryReader y BinaryWriter que encapsulan FileStreams y les
proporcionan una serie de mtodos con los que se simplifica la lectura y escritura de
objetos de cualquier tipo en ficheros. Aunque similares a flujos intermedios, no hay que
confundir estos tipos con tales, pues aunque encapsulan a otros flujos y disponen de
muchos de los mtodos comunes a stos, no derivan de Stream.
Para crear objetos de estos tipos simplemente basta con pasar como parmetro de sus
respectivos constructores el flujo a envolver, pues stos son de la forma:
BinaryReader(Stream flujo)
BinaryWriter(Stream flujo)

Una vez encapsulado el flujo siempre es posible extraerlo mediante la propiedad Stream
BaseStream {get;} que ambos tipos de objetos proporcionan para ello.
Los BinaryWriters se caracterizan por disponer de un mtodo Write() con mltiples
sobrecargas que toman parmetros de cualquiera de los tipos bsicos excepto object y
los escriben directamente en el flujo que encapsulan.
Anlogamente, los BinaryReaders disponen de una familia de mtodos ReadXXX() (int
ReadInt(), string ReadString(), etc.) que permiten leer del flujo cualquier tipo bsico.
Adems, adicionalmente se les ha aadido un mtodo int PeekChar() que permite
consultar el siguiente carcter del flujo en forma de int (o un 1 si no quedasen ms)
pero sin extraerlo del mismo, de forma que la siguiente lectura se realizara como si la
consulta nunca hubiese sido realizada. Sin embargo, quizs por error en el diseo de la
beta 2, no disponen de un mtodo Seek() anlogo al que tienen los BinaryWriters2.
Por defecto, se considera que la codificacin de las cadenas de texto que se lean o
escriban del flujo es UTF-8, pero puede especificarse cualquier otra usando las
siguientes sobrecargas de sus respectivos constructores:
BinaryReader(Stream flujo, Encoding codificacin)
BinaryWriter(Stream flujo, Encoding codificacin)

De todas formas fjese que puede seguir haciendo accesos aleatorios a travs del mtodo Seek() del
objeto que encapsula, pues ste puede recuperarse a travs de la propiedad BaseStream antes comentada

Como se deduce de los prototipos anteriores, la codificacin se indica usando objetos


System.Text.Encoding que representarn formatos codificacin. Aunque estos objetos
permiten acceder a informacin sobre los diversos formatos de codificacin (pgina de
cdigo, bits de prembulo, etc.) y realizar conversiones entre sus juegos de caracteres,
nosotros por ahora slo los usaremos para indicar el tipo de codificacin a usar y los
obtendremos de las siguientes propiedades estticas de Encoding:
Propiedad
ASCII
Unicode
BigEndianUnicode
UTF8
UTF7
Default

Formato que representa el objeto devuelto


ASCII (7 bits por carcter)
Unicode (16 bits por carcter) usando notacin little-endian
Unicode (16 bits por carcter) usando notacin big-endian
UTF8 (16 bits por carcter en grupos de 8 bits)
UTF7(16 bits por carcter en grupos de 7 bits)
Juego de caracteres usado por defecto en el sistema.

Tabla 6: Propiedades indicadores de tipo de codificacin incluidas en Encoding

Por ejemplo, para escribir en un flujo que represente a un fichero llamado datos.dat
usando UTF8 al codificar los caracteres puede hacerse lo siguiente:
FileStream fichero = new FileStream(datos.dat, FileMode.Create);
BinaryWriter ficheroBinario = new BinaryWriter(fichero, Encoding.UTF8));
ficheroBinario.Write(Este mensaje se escribir en UTF8 dentro de datos.dat);

Del mismo modo, para leer correctamente dicho mensaje habra que hacer algo como:
FileStream fichero = new FileStream(datos.dat, FileMode.Open);
BinaryReader ficheroBinario = new BinaryReader(fichero, Encoding.UTF8));
Console.WriteLine(Leido de datos.dat: {0}, ficheroBinario.ReadString());

Lo que es importante es tener en cuenta que la codificacin a usar al leer los caracteres
de un fichero de texto debera ser siempre la misma que la que se us para escribirlos en
l, pues si no podran obtenerse resultados extraos al interpretarse mal los datos ledos.
Ficheros de texto
Ya se ha visto que a travs de los objetos BinaryReader y BinaryWriter pueden leerse o
escribirse textos en ficheros mucho ms cmodamente que trabajando a nivel de bytes
con sus caracteres, como se hara usando directamente FileStreams. Sin embargo, la
BCL proporciona otra pareja de tipos llamados TextReader y TextWriter con los que
estas tareas se hacen incluso ms sencillas.
Lectura de ficheros de texto
Para facilitar la lectura de flujos de texto TextReader ofrece una familia de mtodos que
permiten leer sus caracteres de diferentes formas:

De uno en uno: El mtodo int Read() devuelve el prximo carcter del flujo como, o
un 1 si se ha llegado a su final. Tras cada lectura la posicin actual en el flujo se

mueve un carcter hacia delante, y por si slo quisisemos consultar el carcter


actual pero no pasar al siguiente tambin se ha incluido un mtodo int Peek() que
funciona como Read() pero no avanza la posicin en el flujo tras la lectura.

Por grupos:

Por lneas:

Por completo: Un mtodo muy til que se ha incluido en la BCL y que las libreras
de muchos otros lenguajes y plataformas no tienen es string ReadToEnd(), que nos
devuelve una cadena con todo el texto que hubiese desde la posicin actual del flujo
sobre el que se aplica hasta el final del mismo (o null si ya estbamos en su final)

El mtodo int Read(out char[] caracteres, int inicio, int nCaracteres) lee
un grupo de nCaracteres y los almacena a partir de la posicin inicio en la tabla que se
le indica. El valor que devuelve es el nmero de caracteres que se hayan ledo, que
puede ser inferior a nCaracteres si el flujo tena menos caracteres de los indicados o
un 1 si se ha llegado al final del flujo. Tambin se incluye un mtodo ReadBlock()
que funciona de forma parecida al anterior pero que se queda a la espera de nuevos
caracteres si en el flujo no hay disponibles nCaracteres pero se espera que lleguen,
lo que puede ocurrir cuando se lee de flujos asociados a una conexiones de red.
El mtodo string ReadLine() devuelve la cadena de texto correspondiente
a la siguiente lnea del flujo o null si se ha llegado a su final. Por compatibilidad con
los sistemas operativos ms frecuentemente usados, considera que una lnea de texto
es cualquier secuencia de caracteres terminada en \n, \r \r\n, aunque la cadena
que devuelve no incluye dichos caracteres.

Como la mayora de estos mtodos modifican la posicin actual en el flujo pueden darse
problemas de concurrencia si varios hilos usan a la vez un mismo TextReader. Para
evitarlos se ofrece un mtodo esttico TextReader Synchronized(TextReader textreader)
que devuelve una versin del TextReader que se le pasa como parmetro en la que se
asegura la exclusin mutua en el acceso a aquellos miembros suyos que podran dar
problemas al usarlos en entornos concurrentes.
Aparte de estos mtodos, los TextReaders tambin incluyen un mtodo Close() mediante
el que pueden liberarse los recursos que hubiesen acaparado para poder leer de la fuente
que tuviesen asociada. Obviamente, tras llamar a este mtodo no se podr seguir usando
el TextReader para leer de dicha fuente, e intentar de hacerlo provocar IOExceptions.
En realidad TextReader es una clase abstracta que define caractersticas comunes a
objetos capaces de extraer texto de fuentes genricas. Nosotros para leer ficheros lo que
usaremos sern objetos de su subclase StreamReader especializada en la extraccin de
texto de flujos. Sus constructores bsicos son:
StreamReader(Stream flujo)
StreamReader(Stream flujo, Encoding codificacion)
StreamReader(Stream flujo, bool autodetectarCodificacin?)
StreamReader(Stream flujo,Encoding codificacion,bool autodetectarCodificacin)
StreamReader(Stream flujo,Encoding codificacion,bool autodetectarCodificacin,
int tamaoBuffer)

Como se ve, estos constructores permiten crear el StreamReader a partir del flujo cuyo
lectura facilitan (puede luego recuperarse leyendo su propiedad Stream BaseStream
{get;}) y que pueden configurarse para usar un cierta codificacin y tamao de buffer

La codificacin tomada por defecto es UTF-8, aunque puede cambiarse dndole el valor
apropiado al parmetro codificacin. Adems, tambin se incluye un parmetro llamado
autodetectarCodificacin que indica si se desea que se deduzca la codificacin del texto a
partir de los valores de sus primeros bytes. Si no pudiese autodetectarse se considerara
que es la indicada en codificacin UTF-8 en su defecto. En cualquier caso, siempre es
posible saber cul es la codificacin considerada a travs de la propiedad Encoding
CurrentEncoding {get;} del StreamReader, pero al leerla hay que tener en cuenta que su
valor puede cambiar tras la primera lectura como consecuencia de una autodeteccin
El tamao del buffer que internamente usarn los StreamReaders para alojar los
caracteres que lean es por defecto de 256 caracteres (4096 bytes) Sin embargo, dicho
tamao puede cambiarse al crearlos indicando su tamaoen nmero de caracteres- en
el parmetro tamaoBuffer de su constructor. Dicho valor ha de ser de menos 128 (2048
bytes), y si se le diese alguno menor sera ignorado y se tomara el mnimo sealado.
La existencia de un buffer implica que pueda ocurrir que haya momentos en los que la
posicin actual en el StreamReader sea distinta a la posicin actual en su Stream interno
debido a que se hayan ledo ms caracteres de los solicitados con la idea de guardar
temporalmente los sobrantes en el buffer por si se solicita en breve su lectura. Adems,
los StreamReaders suponen que a su flujo interno slo se acceder a travs de ellos, por
lo que si se le hiciesen cambios externamente podran no verlos directamente por estar
leyendo de su buffer interno. Para resolver los problemas de inconsistencia que esto
podra provocar, todo StreamReader cuenta con un mtodo DiscardBufferedData() que
vaca por completo dicho buffer.
Como un uso muy frecuente de los StreamReaders es extraer textos de ficheros, tambin
se han definido versiones de cada uno de los constructores anteriormente vistos que en
lugar de tomar como primer parmetro un Stream lo que toman es la cadena con la ruta
del fichero a leer y a partir de ella generan el Stream apropiado. stos son:
StreamReader(string rutaFichero)
StreamReader(string rutaFichero, Encoding codificacion)
StreamReader(string rutaFichero, bool autodetectarCodificacin?)
StreamReader(string rutaFichero, Encoding codificacion,
bool autodetectarCodificacin?)
StreamReader(string rutaFichero, Encoding codificacion,
bool autodetectarCodificacin?, int tamaoBuffer)

Aplicacin de ejemplo de lectura de ficheros de texto: visor de texto


A modo de ejemplo de cmo de sencillo es leer ficheros con un StreamReader, a
continuacin se muestra el cdigo de un visor de ficheros de texto que muestra todo el
texto contenido del fichero cuya ruta se le pasa como argumento:
using System;
using System.IO;
public class VisorTexto
{
static void Main(string[] args)
{

try
{
if (args.Length < 1)
{
Console.WriteLine("Error: Llamada incorrecta. Formato correcto de uso:");
Console.WriteLine("\n\t VisorTexto <rutaFichero>");
Environment.Exit(1);
}
String nombreFichero = args[0];
StreamReader contenido = new StreamReader(nombreFichero, true);
Console.WriteLine("Contenido de {0}:\n{1}", nombreFichero, contenido.ReadToEnd());
}
catch (FileNotFoundException)
{ Console.WriteLine("Error: Fichero {0} no encontrado", args[0]); }
}
}

Ntese cmo ahora la extraccin del contenido del fichero es mucho ms sencilla que en
el caso de trabajar directamente con el FileStream asociado al contenido del fichero,
habindose reducido el complejo mtodo muestraContenido() del visor hexadecimal de la
anterior aplicacin de ejemplo a una nica llamada a ReadToEnd() en sta.
Escritura en ficheros de texto
Del mismo modo que los TextReaders facilita la lectura de textos procedentes de fuentes
genricas, los TextWriters hace lo propio con la escritura en destinos genricos. Para ello
ofrece mtodos que permiten:

Escribir cadenas de texto: Todo TextWriters cuenta con mtodo Write() que permite
escribir cualquier cadena de texto en el destino que tengan asociado. Esta cadena se
les pasa como primer parmetro, pero no tiene porqu ser un string sino que puede
ser un objeto de cualquier tipo y lo que se hara sera aplicarle su mtodo ToString()
para obtener su representacin en forma de cadena de texto.
Tambin puede pasrsele como parmetro una tabla char[], en cuyo caso lo que se
escribira seran todos sus caracteres en el mismo orden en que apareciesen en ella.
Si adems se usa la sobrecarga Write(char[] caracteres, int posicin, int n) se podra
delimitar los caracteres de la tabla para slo escribir los n primeros incluidos a partir
de la posicin de la tabla indicada.
Como pasa con el Write() de Console, tambin existe sobrecarga de este mtodo que
toma como parmetros la cadena a mostrar y un nmero indefinido de objetos cuya
representacin como cadenas se escribir en el destino en el lugar correspondiente a
las subcadenas {i} de la cadena que se le pas como primer parmetro.

Escribir lneas de texto: Aparte de Write(), todo TextWriter tambin dispone de un


mtodo WriteLine() que funciona igual que el anterior pero aade en su destino
asociado un indicador de fin de lnea tras la cadena que se le solicita escribir en l.
Dado que el indicador de fin de lnea depende de cada sistema operativo, todo
TextWriter dispone de una propiedad string NewLine mediante la que podemos

configurar cul queremos que se use al escribir en su destino asociado. Su valor por
defecto es el \r\n correspondiente al indicador de fin de lnea en Windows, pero
tambin puede drsele el valor \n correspondiente a Linux3.
Cuando se usen cualquiera de estos mtodos hay que tener cuidado con los objetos con
valor null que se les pasen, pues no escriben nada en su lugar en el destino del
TextWriter pero tampoco lanzan ningn tipo de excepcin que informe de ello.
Adems, tambin hay que tener cuidado al usarlos en entornos concurrente ya que si
varios hilos usan a la vez un mismo TextWriter podran presentarse problemas de
concurrencia. Para evitarlos, a partir del TextWriter puede generarse una versin suya
donde se asegure la exclusin mutua al acceder a sus miembros conflictivos llamando
para ello a su mtodo esttico TextWriter Synchronized(TextWriter textwriter)
Aparte de los mtodos anteriores, todo TextWriter tambin incorpora el tpico mtodo
Close() mediante el que pueden liberarse los recursos que hubiese acaparado para ser
capaz de escribir en su destino asociado. Adems, como para optimizar dichas escrituras
las realizan a travs de un buffer intermedio, tambin cuentan con el mtodo estndar
Flush() encargado de vaciarlo para cuando, sin necesidad de cerrar el TextWriter, se
necesite asegurar el volcado fsico de todo lo escrito a travs de l.
Como TextReader, la clase TextWriter es en realidad abstracta y nosotros para usar la
funcionalidad descrita lo que en realidad usaremos sern objetos de una subclase suya
especializada en la escritura en flujos denominada StreamWriter. Sus constructores
permiten configurar el flujo donde se escribir, y la codificacin y tamao de buffer a
usar de forma parecida a como se hace en los de StreamReader. Los bsicos son:
StreamReader(Stream flujo)
StreamReader(Stream flujo, Encoding codificacion)
StreamReader(Stream flujo, Encoding codificacion, int tamaoBuffer)

Aparte de estos constructores, y como ocurra en los StreamReaders, como es frecuente


usar los StreamWriters para escribir en ficheros de texto tambin se ofrecen versiones de
ellos que en lugar de tomar como primer parmetro un Stream toma la ruta del fichero a
escribir y a partir de ella generar el Stream apropiado. stos son:
StreamReader(string rutaFichero)
StreamReader(string rutaFichero, bool concatenar)
StreamReader(string rutaFichero, bool concatenar, Encoding codificacion)
StreamReader(string rutaFichero, bool concatenar, Encoding codificacion,
int tamaoBuffer)

Como se ve, stos constructores toman un parmetro adicional llamado concatenar. Este
parmetro permite indicar qu ha de hacerse si el fichero donde se desea escribir ya
existe. Si vale false ser sobreescrito, pero si vale true se concatenarn a su final los
nuevos datos que se escriban en l. Por defecto vale true.
Aparte de los miembros de TextWriter ya explicados, los StreamWriters incorporan una
propiedad bool AutoFlush mediante la que es posible conseguir, si se le da el valor true,
que todo texto escrito en l usando sus mtodos Write() y WriteLine() sea inmediatamente
3

Aunque puede tomar cualquier otro valor no se recomienda drselo porque los TextReaders slo pueden
leer adecuadamente las fuentes de texto cuyos indicadores de fin de lnea sean \r o \r\n

volcado al dispositivo que tiene asociado sin que el programador tenga que acordarse de
llamar explcitamente a Flush() tras cada escritura.
Manipulacin del sistema de archivos
Ya hemos visto que mediante objetos FileInfo podemos realizar las tareas ms comunes
de manipulacin de archivos, acceso a informacin sobre ellos y a su contenido; y que
mediante objetos DirectoryInfo podemos hacer lo propio con directorios.
Sin embargo, la realizacin de estas tareas con ellos implica la creacin de objetos de
los tipos citados y ello puede llegar a ser un incordio si slo tenemos que crearlos para
hacer una operacin concreta, como por ejemplo renombrar un fichero. Para evitar esto,
la BCL incluye una serie de tipos que slo tiene mtodos estticos y que actan como
utilidades que permiten realizar ese tipo de tareas a partir de las rutas de los ficheros en
una sola instruccin, sin tener que andar creando objetos intermedios. El tipo ofrecido
para manipular as ficheros es File, mientras que para los directorios es Directory.
Hay que tener en cuenta que cada vez que se llame a uno de los mtodos de estos tipos
se harn las comprobaciones de seguridad necesarias para ver si se tienen los permisos
necesarios sobre los archivos que intervengan en la operacin. Sin embargo, cuando se
usan objetos FileInfo o DirectoryInfo las comprobaciones slo se hacen sobre el fichero o
directorio que ellos representen la primera que se solicite una operacin sobre ellos. Por
tanto, para a hacer mltiples operaciones sobre un mismo fichero o directorio es ms
eficiente usar objetos intermedios que los mtodos estticos de los tipos anteriores.
Aparte de versiones ms cmodas de utilizar de los servicios ofrecidos por los objetos
FileInfo y DirectoryInfo, estos tipos tambin proporcionan nuevos servicios relativos a
ficheros y directorios que permiten realizar tareas tan tiles como navegar por el sistema
de archivos de una mquina u obtener la lista de unidades lgicas de las que dispone.
Manipulacin de ficheros
Como ya se ha dicho, el tipo ofrecido en la BCL como utilidad mediante cuyos mtodos
estticos se facilita la realizacin de las operaciones con ficheros ms comunes es File.
Los servicios ofrecidos por los mtodos de este tipo pueden agruparse en:

Acceso a informacin sobre ficheros: Para manipular directamente la informacin


que los sistemas operativos almacenan sobre los ficheros de su sistema de archivo
(atributos, fecha de creacin, etc) se ofrecen mtodos que operan de manera similar
a las propiedades que para ello se vio que ofrecan los objetos FileInfo. stos son:
FileAttributes getAttributes(string rutaFichero)
SetAttributes(string rutaFichero, FileAttributes atributos)
DateTime getCreationTime(string rutaFichero)
SetCreationTime(string rutaFichero, DateTime fechaCreacin)
DateTime getLastAccessTime(string rutaFichero)
SetCreationTime(string rutaFichero, DateTime FechaltimoAcceso)

DateTime getLastWriteTime(string rutaFichero)


SetWriteTime(string rutaFichero, DateTime FechaltimaModificacin)

Acceso al contenido de los ficheros: Para acceder al contenido de los ficheros se


ofrece un mtodo sobrecargado Open() que funciona de igual forma a como lo hace
el Open() de FileStream ya visto pero que toma como primer parmetro su ruta.
Aparte del anterior, tambin se incluyen mtodos FileStream OpenRead(string
rutaFichero) y FileStream OpenWrite(stringrutaFichero) que permiten abrir ficheros
ya existentes en exclusividad y modo de slo lectura o de lectura-escritura segn,
respectivamente, corresponda.
Tambin se ofrecen un mtodo StreamReader OpenText(string rutaFichero) que
funciona de forma parecida a OpenRead() pero en lugar de devolver directamente el
flujo asociado al fichero devuelve un StreamReader facilita su lectura como texto.
Aunque de OpenWrite() no se ofrece un equivalente tan fiel para facilitar la escritura
de texto en el fichero, se incluye un StreamWriter AppendText(string rutaFichero)
que no requiere que el fichero a abrir exista (si no existe lo crea) y que si existe, en
vez de sobreescribirlo lo que hace es concatenar a su final el texto que se le escriba.

Modificacin de los ficheros del sistema de archivos: Finalmente, se incluye un


tercer grupo de mtodos que permiten realizar de una manera cmoda operaciones
tan frecuentes como crear, borrar o mover ficheros por el sistema de archivos.
Para crear ficheros se incluye el mtodo FileStream Create(string rutaFichero), que
ntese que devuelve un FileStream. Este objeto lo devuelve porque tras crearlo lo
ms normal es que interese aadirle datos a travs de l. Adems, se ofrece una
sobrecarga del mismo que toma como segundo parmetro un int que se interpretar
como el tamao que ha de tener el buffer que dicho FileStream usar internamente.
Antes crear un fichero puede que nos interese ver si previamente ya exista, pues si
fuese as se borrara el existente. Para ello puede usarse el mtodo esttico bool
Exists(string rutaFichero) tambin incorporado en File.
Si lo que se desea es trasladar la ubicacin de un fichero en el sistema de archivos
entonces el mtodo a usar es Move(string rutaFuente, string rutaDestino) Ntese que
este mtodo tambin puede usarse para cambiar el nombre del fichero, pues ese sera
el efecto que se conseguira si el directorio que se especificase rutaDestino fuese el
mismo que el directorio donde estaba el fichero.
Si en vez de cambiarlo de ubicacin lo que queremos es crear una copia suya en otra
ruta, entonces el mtodo a usar es Copy(string rutaFuente, string rutaCopia) Cuando
se use hay que tener cuidado con que el fichero rutaCopia no exista, pues para evitar
sobreescrituras no deseadas se lanzara una IOException en caso de que existiese.
Otra posibilidad sera pasarle true como tercer parmetro a Copy() para as indicar
explcitamente que deseamos sobreescribirlo.
Finalmente, para borrar ficheros se ofrece un mtodo Delete(string rutaFichero)

Manipulacin de directorios
Como tambin ya se ha comentado, la clase Directory centraliza todo tipo de servicios
relativos a la manipulacin de directorios y permite utilizarlos de una manera rpida y
cmoda, sin necesidad de tener que andar creando objetos DirectoryInfo intermedios. En
general, estos servicios que ofrece podemos agruparlos en tres grandes categoras:

Acceso a informacin sobre directorios: Igual que, como se ha visto, File cuenta
con miembros que permiten acceder a la informacin que cada sistema operativo
almacenan sobre los ficheros de su sistema de archivos, Directory hace lo propio con
la relativa a los directorios. Para ello, ofrece los siguientes mtodos:
DateTime getCreationTime(string rutaDirectorio)
SetCreationTime(string rutaDirectorio, DateTime fechaCreacin)
DateTime getLastAccessTime(string rutaDirectorio)
SetCreationTime(string rutaDirectorio, DateTime FechaltimoAcceso)
DateTime getLastWriteTime(string rutaDirectorio)
SetWriteTime(string rutaDirectorio, DateTime FechaltimaModificacin)

Acceso al contenido de los directorios: Como sabemos, todo directorio puede


contener mltiples ficheros y/o otros directorios. Pues bien, para obtener la lista de
nombres de todos los ficheros contenidos en un cierto directorio puede usarse el
mtodo esttico string[] GetFiles(string rutaDirectorio) de Directory, para obtener la
de sus directorios string[] GetDirectories(string rutaDirectorio), y para obtener la de
sus ficheros y directorios string[] GetFileSystemEntries(string rutaDirectorio)
Los mtodos anteriores devuelven listas completas con los nombres de todos los
elementos del directorio correspondientes a cada uno. Por ejemplo, la lista completa
de todos los ficheros contenidos en el directorio C:\Pruebas puede obtenerse as:
String[] contenidoPruebas = File.GetFiles(@C:\Pruebas);

Sin embargo, como en ocasiones puede que slo nos interese obtener los contenidos
del directorio cuyos nombres sigan un cierto patrn, de los mtodos anteriores
tambin se ofrece una sobrecarga que admite como parmetro una cadena que
indique el patrn que han de seguir los nombres a obtener. Este patrn puede usar el
carcter ? para indicar que en su lugar se admite cualquier carcter y el carcter *
para indicar que en su lugar se admite cualquier nmero (incluido 0) de cualesquiera
caracteres. As, los nombres de los ficheros almacenados en C:\Pruebas que
empiecen por A y tengan extensin txt pueden obtenerse con:
String[] contenidoPruebas = File.GetFiles(@C:\Pruebas, A*.txt);

Navegacin por el sistema de archivos: A veces puede interesar escribir las rutas
de los ficheros y/o directorios a los que se quiera acceder de manera relativa a un
determinado directorio, para lo que es necesario ir cambiando de directorio actual.
Esto puede ser til porque permite que las aplicaciones accedan a ficheros instalados
con ellas sin tener porqu conocer la ruta completa donde se instalaron: se accedera
a los ficheros con rutas relativas a la ubicacin de su ejecutable.

Una segunda utilidad de escribir las rutas relativamente es que ello puede
simplificar mucho las rutas cuando se va a acceder a varios ficheros y/o directorios
ubicados dentro de uno determinado. Por ejemplo, para acceder a varios ficheros
ubicados en d:\pruebas\versin1 lo mejor puede ser establecer ese directorio
como el actual y as podremos referenciarlos sin tener que escribir el nombre de su
directorio padre.
Como se ve, para poder escribir las rutas de los ficheros de manera relativa a otros
directorios puede necesitarse poder cambiar el directorio considerado como actual.
Para ello, Directory proporciona el mtodo esttico SetCurrentDirectory(string
rutaNuevoDirectorioActual), y tambin incluye un string GetCurrentDirectory() que
permite consultar cul es el en cada instante el directorio considerado como actual.
Para ayudarnos en nuestros movimientos a travs de los rboles de directorios se
ofrece tambin un mtodo DirectoryInfo GetParent(string rutaDirectorio), que
devuelve el objeto DirectoryInfo que representa al padre del directorio indicado.
Hay sistemas operativos que trabajan con mltiples unidades de disco lgicas, que
se corresponden con los distintos dispositivos de almacenamiento de la mquina
sobre la que corren o con porciones lgicas de ellos y que tendrn su propio rbol de
directorios. Para facilitar la navegacin en estos casos, en Directory se ha incluido
un mtodo string GetDirectoryRoot(string rutaDirectorio) que devuelve el nombre del
directorio raz del rbol asociado a la unidad de disco a la que pertenece al directorio
indicado en el formato <nombreUnidad>:\; y un mtodo string[] GetLogicalDrives()
que devuelve una tabla con los nombres, en el formato anterior, de todas y cada una
de las unidades de disco lgicas de la mquina sobre la que se ejecuta.

Modificacin de los directorios del sistema de archivos: Para realizar las tpicas
operaciones de creacin, borrado y movimiento de directorios la clase Directory
incluye los mtodos estticos DirectoryInfo CreateDirectory(string rutaDirectorio),
Delete(string rutaDirectorio) y Move(string rutaFuente, string rutaDestino)
Tenga en cuenta que las operaciones que realice sobre un directorio afectan a todos
los ficheros y subdirectorios contenidos en el mismo. Por ello, si borra un directorio
ste ha de estar vaco o si no Delete() producir una IOException para evitar borrados
accidentales, aunque de todas formas puede pasarle true como segundo parmetro
para indicarle explcitamente su deseo de borrar el contenido del directorio.
Tenga tambin en cuenta que operaciones como CreateDirectory() o Move() lanzarn
IOExceptions si los directorios a crear (en el caso de Move() sera el de rutaDestino)
ya existen; y que Delete() producir una DirectoryNotFoundException si el directorio
a borrar no existe. Para comprobar cmodamente la existencia de directorios y evitar
as problemas puede usar el mtodo bool Exists(string rutaDirectorio) de Directory.

Manipulacin de rutas
Deteccin de cambios en el sistema de archivos

A travs de objetos FileSystemWatcher la BCL proporciona un servicio de vigilancia del


estado del sistema de archivos mediante el que es posible detectar cualquier cambio que
se produzca en l (creacin y borrado de archivos, modificacin de sus atributos, etc.) y
asociar cdigo a ejecutar en esos casos. Es tal su potencia que incluso permite detectar
cambios en sistemas de archivos de mquinas remotas.
De servicios como ste pueden especialmente beneficiarse aplicaciones que funcionen
en una lnea similar a Microsoft Biztalk Server. Es decir, que tomen datos que se les
coloquen como ficheros en ciertos directorios y los procesen de alguna manera, ya sea
transformndolos, extrayendo informacin de ellos, pasndolos a otras mquinas, etc.
Ahora bien, al usar FileSystemWatcher tenga en cuenta que tiene ciertas limitaciones:
slo funciona bajo Windows N 4.0, 2000 XP y la deteccin remota no funciona entre
dos mquinas que usen Windows NT 4.0. Tampoco admite la vigilancia del contenido
de unidades de CD-ROM o DVD, aunque por otra parte ello es lgico en tanto que este
tipo de unidades son de slo lectura y por tanto nunca se producirn cambios en ellas.
Seleccin de ficheros a vigilar
La forma ms sencilla de indicar qu ficheros y directorios ha de vigilar un objeto
FileSystemWatcher es especificando en su propiedad string Path el directorio que los
contiene. Por ejemplo, el contenido de c:\datos puede vigilarse as:
FileSystemWatcher observador = new FileSystemWatcher();
observador.Path = @c:\datos

Otra posibilidad sera especificar directamente el directorio en su constructor:


FileSystemWatcher observador = new FileSystemWatcher(@c:\datos);

En ambos casos tenga en cuenta que el observador vigilar el contenido del directorio
que se le ha especificado aunque posteriormente dicho directorio sea renombrado. Ello
lo consigue gracias a que lo que para l lo identifica no es su nombre sino el manejador
que le asocia el sistema operativo, y ste no cambia por mucho que sea renombrado.
Los observadores creados como se ha dicho detectarn los cambios que se produzcan en
cualquiera de los ficheros y subdirectorios del directorio que se les indique, pero si se
desea que slo se centre en algunos de ellos puede configurarse su propiedad string
Filter para ello. A esta propiedad se le pasara una cadena en la que se indicara el patrn
que seguirn los ficheros a vigilar, representando en ella cualquier carcter mediante un
? y cualquier combinacin de cualesquiera caracteres con un *. Por ejemplo, para slo
vigilar los ficheros de extensin txt de c:\datos podra hacerse:
FileSystemWatcher observador = new FileSystemWatcher(@c:\datos);
observador.Path=*.txt;

Nuevamente, otra posibilidad sera especificar el filtro directamente en el constructor:


FileSystemWatcher observador = new FileSystemWatcher(@c:\datos, *.txt);

En cualquier caso, hay que sealar que, por eficiencia, cuando se crea un observador
este slo vigila por defecto el contenido del directorio que se les indique pero no el de
los subdirectorios del mismo. Si queremos que ello tambin ocurra hay que configurar
explcitamente su propiedad bool IncludeSubdirectories. Por tanto, todo el sistema de
archivos de la unidad C:\ de una mquina podra vigilarse as:
FileSystemWatcher observador = new FileSystemWatcher(@c:\);
observador.IncludeSubdirectories;

Ntese que con IncludeSubdirectories indica que desea vigilar no slo el contenido del
directorio indicado y sus subdirectorios sino tambin el de los subdirectorios de stos, y
el de los subdirectorios de los subdirectorios de estos, etc.
En el lado opuesto, tambin es posible configurar un FileSystemWatcher para que slo
vigile los cambios que se produzcan en un fichero concreto. Para ello basta asignar a su
propiedad Path la ruta del directorio al que pertenece y a Filter el nombre de ese fichero.
Por ejemplo, para vigilar el fichero c:\datos\dato1.dat puede hacerse:
FileSystemWatcher observador = new FileSystemWatcher(@c:\datos, dato1.dat);

Seleccin de cambios a vigilar


Una vez configurados los ficheros que se desean vigilar, los observadores por defecto
slo vigilan aquellos cambios de tamao, nombre o fecha de ltima modificacin que se
produzcan en los mismos. Esto puede cambiarse modificando su propiedad NotifyFilters
NotifyFilter, en la que se indica cules son los cambios ha vigilar usando un objeto de un
tipo enumerado que admite los siguientes literales:
Literal

Significa que se detectan cambios en...

Attributes
CreationTime
DirectoryName
FileName
LastAccess
LastWrite
Size
Security

Atributos de ficheros
Fecha de creacin de ficheros
Nombres de directorios
Nombres de ficheros
Fecha de ltimo acceso de ficheros
Fecha de ltima modificacin de ficheros
Tamao de ficheros
Permisos de seguridad de ficheros

Tabla 7: Significado de los literales de NotifyFilters

Estos literales puede combinarse con operaciones lgicas OR. Por ejemplo, los cambios
de nombre o atributos producidos en los ficheros de c:\datos pueden detectarse con:
FileSystemWatcher observador = new FileSystemWatcher(@c:\datos);
observador.NotifiyFilter = NotifyFilters.DirectoryFile | NotifyFilters.Attributes;

Deteccin sncrona de cambios


Una vez configurados los ficheros a vigilar y los tipos de cambios a detectar, la forma
ms sencilla de usar FileSystemWatcher para detectar cambios de ese tipo es usando su
mtodo WaitForChangedResult WaitForChanged( WatcherChangeTypes tipoCambio), lo

que dejara el cdigo bloqueado en espera de que se produjese algn cambio del tipo
indicado en tipoCambio. Este parmetro es de una enumeracin cuyos los literales son:
Literal
Changed
Renamed
Created
Deleted
All

Tipo de cambio detectado


Cambio en atributos, fechas, tamao o permisos de seguridad de fichero
Cambio en nombre de fichero o directorio
Creacin de fichero o directorio (no incluye renombrado de preexistentes)
Borrado de fichero o directorio
Cualquiera

Tabla 8: Literales indicadores de tipo de cambio de WatcherChangeTypes

El objeto WaitForChangedResult que devuelve este mtodo contiene informacin sobre


el cambio producido a partir de la que podramos actuar en consecuencia. Su propiedad
string Name {get;} almacena el nombre del fichero o directorio afectado por el cambio,
WatcherChangeTypes ChangeType {get;} el tipo de cambio producido, y string OldName
{get;} su nombre anterior si fue un cambio de renombrado (o null si fue de otro tipo)
Por ejemplo, el siguiente programa se queda en espera de que se realice algn cambio
en c:\test y luego muestra un mensaje indicando el tipo de cambio producido:
using System;
using System.IO;
class EsperaCambio
{
static void Main()
{
if (!Directory.Exists(@"c:\test");
Directory.CreateDirectory(@"c:\test");
FileSystemWatcher observador = new FileSystemWatcher(@c:\test);
WatcherChangedResult cambio =
observador.WaitForChanged(WatcherChangesTypes.All);
switch(cambio.ChangeType)
{
case WatcherChangeTypes.Changed:
Console.WriteLine(Detectado cambio en informacin sobre {0}, cambio.Name));
break;
case WatcherChangeTypes.Renamed:
Console.WriteLine(Detectado renombrado de {0} por {1},
cambio.Name, cambio.OldName));
break;
case WatcherChangeTypes.Created:
Console.WriteLine(Detectada creacin de {0}, cambio.Name,));
break;
case WatcherChangeTypes.Deleted:
Console.WriteLine(Detectado borrado de {0}, cambio.Name));
break;
}
}
}

Ntese que el problema de utilizar un mecanismo como este para detectar los cambios
es que deja bloqueado al hilo que llama a WaitForChanged() hasta que se produzca el
cambio, lo que puede reducir mucho el rendimiento de la aplicacin si ste tarda en
producirse (o incluso bloquearla si nunca llega a producirse)
Para evitar esto y dar la posibilidad de que se puedan realizar otras tareas mientras llega
el evento puede especificarse un como segundo parmetro de tipo int al llamar a
WaitForChanged() que para indicar el tiempo mximo, en ms., que se de esperarse a que
ocurra el cambio, y si no ocurre pasado ese tiempo finalizar la ejecucin del mtodo y
se marcar a true la propiedad bool TimedOut del WaitForChangedResult para indicarlo.
Deteccin asncrona de cambios
La opcin anterior de acotar el tiempo mximo de espera de WaitForChanged() para
poder realizar otras tareas mientras llegue el cambios esperado permite muchas veces
notables mejoras de eficiencia. Sin embargo, una solucin asncrona donde se indique al
FileSystemWatcher qu ha hacerse ante cada tipo de cambio y se le deje funcionando en
un hilo aparte que se encargar de detectarlos y realizar las acciones apropiadas puede
ser mejor, pues en mquinas con varios procesadores es ms eficiente y adems da lugar
a cdigos ms claros donde no se intercalan continuas llamadas a WaitForChanged() con
sus consiguientes esperas acotadas y comprobaciones de TimedOut asociadas.
Para asociar cdigos a ejecutar ante cada tipo de cambio detectado FileSystemWatcher
proporciona los siguientes eventos:
Evento
Changed
Renamed
Created
Deleted

Su cdigo asociado se ejecutar cada vez que se produzca...


Cambio en atributos, fechas, tamao o permisos de seguridad de fichero
Cambio en nombre de fichero o directorio
Creacin de fichero o directorio (no incluye renombrado de preexistentes)
Borrado de fichero o directorio

Tabla 9: Eventos de FileSystemWatcher para respuesta a cambios en el sistema de archivos

La mayora de ellos del tipo delegado FileSystemEventHandler definido como sigue:


void FileSystemEventHandler(object emisor, FileSystemEventArgs args)

El emisor es el FileSystemWatcher que detect el evento, mientras que args es donde se


informacin sobre el fichero o directorio al que afect el cambio. Esta informacin
incluye su nombre en una propiedad string Name {get;}, su ruta completa en string
FullPath {get;} y el tipo de cambio detectado en WatcherChangeTypes ChangeType {get}
En realidad no todos los eventos de la Tabla 9 son del tipo FileSystemEventHandler,
sino que en el caso concreto de Renamed su tipo delegado es la subclase del anterior:
void RenameEventHandler(object emisor, RenameEventArgs args)

Esto se debe a que aparte de la informacin proporcionada por el tipo delegado anterior,
ante un renombrado puede convenir disponer tambin de informacin sobre el fichero o
directorio afectado antes de ser renombrardo. Eso es precisamente lo que se aporta el

segundo parmetro de este tipo de delegado, que es de un tipo RenameEventArgs hijo de


FileSystemEventArgs, pues aade propiedades destinadas a almacenar el nombre (string
OldValue {get;}) y ruta completa (string OldFullPath {get;}) del fichero o directorio antes
de ser renombrado.
Un ejemplo de cmo mostrar un mensaje cada vez que se detecte la creacin de ficheros
en c:\pruebas con la ruta completa del fichero creado es el siguiente trozo de cdigo:
FileSystemWatcher observador = new FileSystemWatcher(@c:\pruebas);
observador.Created += new FileSystemEventHandler(muestraMensaje);

donde el mtodo muestraMensaje() estara definido como sigue en alguna parte dentro
de la misma definicin de tipo a la que perteneciese el cdigo anterior:
void muestraMensaje(object emisor, FileSystemEventArgs args)
{
Console.WriteLine(Creado fichero {0}, args.FullPath);
}

Hay que tener en cuenta que una vez asociado los cdigos de respuesta a los diferentes
eventos del FileSystemWatcher que queramos controlar hay que poner true su propiedad
bool EnableRaisingEvents para lazar el hilo que har las detecciones de cambios y
llamadas a los cdigos asociados a los eventos. Adems, en cualquier momento es
posible parar dicho hilo sin ms que volver a poner a false esta propiedad.
ha sido definido como un componente4, por lo que tambin podr
crear objetos suyos arrastrndolos desde la caja de herramientas de Visual Studio.NET
hasta su ventana de diseo. Sin embargo, si lo hace tenga cuidado ya que entonces el
valor que se le dar por defecto a su propiedad EnableRaisingEvents es true.
FileSystemWatcher

Ejemplo: Cifrador de directorios


A modo de ejemplo de cmo usar FileSystemWatcher asncronamente a continuacin se
muestra un ejemplo de aplicacin que encripta todos los ficheros que coloquen en el
directorio que se le pase como parmetro al lanzarla:
using System;
using System.IO;
class Cifrador
{
static void Main(string[] args)
{
try
{
FileSystemWatcher observador = new FileSystemWatcher(args[0]);
observador.Created+=new FileSystemEventHandler(ficheroCreado);
observador.EnableRaisingEvents=true;
Console.WriteLine("Cifrado de {0} activado", args[0]);
Console.ReadLine();
Un componente es cualquier tipo que derive de Component. Sin embargo, no es el objetivo de este
tema estudiar cmo se crean componentes y su explicacin se posterga a temas posteriores.
4

Console.WriteLine("Cifrado de {0} desactivado", args[0]);


}
catch (DirectoryNotFoundException)
{ Console.WriteLine("{0} no es un directorio vlido"); }
catch (IndexOutOfRangeException)
{
Console.WriteLine("Error: Llamada incorrecta. Formato correcto de uso:");
Console.WriteLine("Cifrador <rutaDirectorio>");
}
}
static void ficheroCreado(object objeto, FileSystemEventArgs args)
{
if (File.Exists(args.FullPath)) // Slo ciframos los ficheros. Los directorios no.
{
BinaryReader lectorFichero = new BinaryReader(File.OpenRead(args.FullPath));
long tamaoFichero = lectorFichero.BaseStream.Length;
int[] cifradoFichero = new int[tamaoFichero];
for (int i=0; i<tamaoFichero; i++)
cifradoFichero[i] = lectorFichero.ReadByte() + 3;
lectorFichero.Close();
BinaryWriter escritorFichero = new BinaryWriter(File.OpenWrite(args.FullPath));
for (int i=0; i<tamaoFichero; i++)
escritorFichero.Write((byte) cifradoFichero[i]);
escritorFichero.Close();
}
}
}

Como puede observar el cdigo del programa es muy sencillo, simplemente comprueba
que se le haya pasado como argumento la ruta de algn directorio; y si es as crea un
observador que vigila dicho directorio y que cada vez que se crea algn fichero sobre el
mismo lo cifra incrementado en tres unidades el valor cada uno de sus bytes (el clsico
criptsosistema de Csar)
Problemas de desbordamientos del buffer de cambios
Cada FileSystemWatcher cuenta con un buffer interno donde encola temporalmente
informacin sobre cambios producidos en el directorio que vigila mientras estaba
procesando, para as procesarlos en orden en cuanto termine con el que estaba. Ahora
bien, si se encolasen tantos que se saturarse ese buffer y se perdiese la deteccin de los
que no cupiesen en l, el observador lanzara un evento ErrorEventHandler Error al que
para tratarlo podramos asociarle un mtodo con la signatura que sigue:
delegate void ErrorEventHandler(object emisor, ErrorEventArgs args );

Tenga especial cuidado al activar la propiedad IncludeSubdirectories, pues como se ha


dicho implica que se vigile todo el contenido del sistema de archivos ubicado a partir
del directorio indicado en la propiedad Path, y como eso pueden ser muchos archivos es
ms fcil que ocurran simultneamente tantos cambios en ellos que se sature el buffer.

Una forma de evitar errores en estos casos puede ser cambiar el tamao de este buffer
interno, que por defecto es de 8192 bytes. Para ello puede usarse la propiedad int
InternalBufferSize del FileSystemWatcher, a la que se le ha de dar un valor superior a
4096 y recomendablemente (al menos en mquinas Intel) mltiplo de dicha cantidad.
Otra forma para evitarlos es modificar la propiedad NotifyFilter para restringir el tipo de
cambios a detectar y por tanto reducir as su nmero. Lo que no se le ocurra nunca es
intentar reducirlos usando Filter para filtrar slo ciertos archivos de la ruta indicada en
Path, pues el buffer en realidad almacena todos los cambios producidos en dicha ruta,
incluidos los que ocurran sobre los ficheros que no entren en el filtro (slo que cuando
toque procesar estos sern descartados en lugar de procesados)
Ficheros temporales
Muchos sistemas operativos suelen incluir un directorio que ofrecen a las aplicaciones
como almacn temporal de ficheros que slo son tiles mientras la aplicacin se est
ejecutando pero que una vez ejecutada carecen de sentido y puede eliminarse.
Como segn el tipo de sistema operativo que tenga instalado el usuario o segn cmo lo
tenga configurado la ruta de ste puede variar, la BCL incluye un mtodo string
GetTempPath() mediante el que puede obtenerse esta ruta para cada mquina concreta.
Por otra parte, como este directorio es compartido entre mltiples aplicaciones, no es
muy recomendable que creemos en l ficheros con nombres elegidos por nosotros
mismos ya que podran entrar en conflicto con los ya instalados por otras aplicaciones.
En su lugar, es mejor usar el mtodo string GetTempFileName () de Path, que devuelve
un nombre de fichero temporal que seguro que est libre. Por ejemplo, dado el cdigo:
Console.WriteLine(Path.GetTempFileName());

El resultado de ejecutarlo en mi mquina bajo Windows NT 4.0 es que se muestra por la


ventana de consola c:\TEMP\tmp9C5.tmp, si lo vuelvo a ejecutar sin modificar mi
directorio temporal obtendr c:\TEMP\tmp9C6.tmp, y si lo sigo ejecutando ir
obteniendo nombres similares pero con valores diferente en sus tres dgitos que en cada
caso sern una unidad superior al ltimo obtenido.
En realidad el formato de estos ficheros temporales y su ubicacin es lo de menos, pues
son dependientes del sistema operativo y del estado del directorio temporal en cada
momento. Lo que es importante que vea es que precisamente porque no siguen un
patrn fijo es por lo que para trabajar con ellos debera usar los mtodos arriba citados.

---------------------------------------------------------------------------------------------------------FileShare.Inheritable no especificado y no vlido al usarlo (provoca excepcin de


argumento fuera de rango vlido)
----------------------------------------------------------------------------------------------------------

Aadir lo del paso por valor y paso por referencia


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Aadir consejos sobre cmo crear nuevos tipos de excepciones
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Aadir que la clase padre ha de ir antes que los interfaces
---------------------------------------------------------------------------------------------------------Uso de this para resolver acceder a campos que coincidan con el nombre de variables
locales.
---------------------------------------------------------------------------------------------------------Uso de get/set para tratar datos en formato diferente al de su representacin interna
Desde el cdigo de una clase puede llamarase a mtodos privados de otros objetos de su
misma clase. til para acceder a campos privados en copy-constructors
Otra ventaja de propiedades: al igual que los mtodos, ocultan la implementacin
interna y facilitan la introduccin de cambios en ella.
---------------------------------------------------------------------------------------------------------El this ya no es obligatorio para llamar a los mtodos de un mismo tipo
---------------------------------------------------------------------------------------------------------Los static readonly no pueden inicializarse en constructores no static

También podría gustarte