Elaboró: Alexis Romero Mendoza
Manejo de Archivos en los lenguajes de programación de alto nivel: Java y C#
Introducción
Las computadoras utilizan archivos para la retención a largo
plazo de grandes cantidades de datos. Las personas utilizamos a diario
archivos, en tareas como ensayos, hojas de cálculo, etc.
Sabemos que el almacenamiento de datos en variables o arreglos
es temporal, pues se pierden generalmente cuando termina el programa. Nos vamos
a referir a los datos que se mantienen en archivos como datos persistentes, ya
que existen más allá de la duración de la ejecución del programa. Las
computadoras almacenan los archivos en dispositivos de almacenamiento
secundario como discos duros, discos ópticos, etc.
El procesamiento de archivos es una herramienta muy importante
con la que debe de contar un lenguaje de programación para que pueda soportar
aplicaciones comerciales que procesan cantidades masivas de datos persistentes.
Una computadora procesa
todo los elementos de datos como combinaciones de ceros y unos, los circuitos
de las maquinas realizan varias manipulaciones simples de bits. Lo anterior
resulta muy difícil, en vez de ello los programadores prefieren trabajar con
datos en formatos como dígitos decimales. Los dígitos, letras y símbolos
especiales se conocen como caracteres, el conjunto de caracteres de la
computadora es el conjunto de todos los caracteres utilizados para escribir
programas y representar elementos de datos. Las computadoras pueden procesar
solamente 1s y 0s, un conjunto de caracteres ésta compuesto de bits, los
campos están compuestos de caracteres o bytes, un campo es un grupo de
caracteres o bytes que transmiten cierto significado, varios campos forman un
registro, un archivo es un grupo de
registros relacionados, un archivo puede verse como una colección de bytes.
Se hablara en esta investigación sobre el procesamiento de
archivos (leer y escribir) mediante los lenguajes de programación Java y C#.
1.-Manejo de archivos en
Java
Java
considera a cada archivo como un flujo secuencial de bytes.
1.1.-Archivos y flujos
Los flujos de
archivos se pueden utilizar para la entrada y salida de datos, ya sea como
caracteres o bytes. Los flujos que reciben y envían bytes a archivos se conocen
como flujos basados en bytes, y almacenan datos en su formato binario. Los flujos
que reciben y envían caracteres de/a los archivos se conocen como flujos basados
en caracteres, y almacenan datos como una secuencia de caracteres.
Los archivos que se crean usando flujos
basados en bytes se conocen como archivos binarios, mientras que los archivos
que se crean usando flujos basados en caracteres se conocen como archivos de
texto. Los archivos de texto se pueden leer con editores de texto, mientras que
los archivos binarios se leen mediante un programa que convierte los datos en
un formato que pueden leer los humanos.
Un programa de Java abre un archivo
creando un objeto y asociándole un flujo de bytes o de caracteres. Los
programas de Java realizan el procesamiento de archivos utilizando clases del
paquete java.io. Este
paquete incluye definiciones para las clases de flujo como FileInputStream (para
la entrada basada en bytes desde un archivo), FileOutputStream (para la salida basada en bytes
hacia un archivo), FileReader
(para la entrada basada en caracteres desde un archivo) y FileWriter (para la salida
basada en caracteres hacia un archivo). Los archivos se abren creando objetos
de estas clases de flujos, que heredan de las clases InputStream, OutputStream, Reader y Writer.
La clase Scanner se utiliza en
forma extensa para recibir datos del teclado. Como veremos, esta clase también
puede leer datos de un archivo. La clase Formatter
permite mostrar datos con formato en la pantalla, o enviarlos a un archivo, en
forma similar a System.out.printf.
1.2.-La clase File
Los objetos de
la clase File
no abren archivos ni proporcionan herramientas para procesarlos. No obstante,
los objetos File se utilizan frecuentemente con objetos de otras clases de
java.io para especificar los archivos o directorios que van a manipularse.
Creación de objetos File
La
clase File proporciona cuatro constructores.
1 1) El
constructor: public File( String
nombre )
Especifica
el nombre de un archivo o directorio que se asociará con el objeto File. El
nombre puede contener información sobre la ruta, así como el nombre de un
archivo o directorio.
2 2) El
constructor: public File( String
rutaAlNombre, String nombre )
Usa
el argumento rutaAlNombre
(una ruta absoluta o relativa) para localizar el archivo o directorio
especificado por nombre.
3 3) El
constructor: public File( File
directorio, String nombre )
Usa
un objeto File
existente llamado directorio (una ruta absoluta o relativa) para localizar el
archivo o directorio especificado por nombre.
4 4) El
constructor: public File( URI
uri )
Usa
el objeto URI
dado para localizar el archivo. Un Identificador uniforme de recursos (URI) es
una forma más general de un Localizador uniforme de recursos (URL), el cual se
utiliza comúnmente para localizar sitios Web.
Puede utilizar
la clase File
para determinar muchas cosas sobre archivos, como su tamaño, si se puede
escribir sobre ellos, si un elemento es un archivo, directorio o conexión de
nombres, más aún. Se puede utilizar isFile para
determinar si un elemento es un archivo verdadero u otra cosa, como un
directorio (lo puede comprobar con isDirectory)
o una conexión de nombres. Utilice toURL
para convertir la vía de acceso de un archivo a un URL, renombrar archivos y
otros con la clase File.
Se puede utilizar uno de los siguientes métodos para reunir
información sobre el archivo:
1)
Nombres de archivo:
String getName()
String getPath()
String getAbsolutePath()
String getParent()
boolean renameTo( File nuevoNombre )
2)
Comprobaciones:
boolean exists()
boolean canWrite()
boolean canRead()
boolean isFile()
boolean isDirectory()
boolean isAbsolute()
3)
Información general del archivo:
long lastModified()
long length()
4)
Utilidades de directorio:
boolean mkdir()
String[] list()
1.3. - Las clases FIleInputStream y FileOutputStream
Gran parte de
la entrada y salida de los programas se realiza basándose en archivos y para
ello java.io aporta
dos clases. Una para archivos de entrada, FileInputStream,
y otra para archivos de salida, FileOutputStream.
La diferencia entre archivos de entrada y de salida es fundamental, pues
situaciones como intentar leer de un archivo de salida o viceversa pueden
generar en errores.
1.2.1.-Apertura de un FileInputStream
Para abrir un FileInputStream sobre
un archivo, se le da al constructor un String
o un objeto File: El ejemplo siguiente crea dos FileInputStreams que están utilizando el mismo
archivo de disco real. Cualquiera de los dos constructores disponibles en esta
clase puede lanzar una FileNotFoundException.
InputStream f1 = new FileInputStream("/archivo.txt");
File f = new file("/archive.txt");
InputStream f2 = new FileInputStream(f);
FileInputStream fichero=new
FileInputStream("/carpeta/archive.txt");
File fichero = new File( "/carpeta/archive.txt" );
FileInputStream ficheroSt= new FileInputStream( fichero );
Aunque probablemente el primer constructor es el que más se
utiliza habitualmente, el segundo permite examinar el archivo más de cerca
utilizando sus métodos antes de asignarlo a un flujo de entrada. Cuando se crea
un FileInputStream,
también se abre para lectura. FileInputStream
sobrescribe seis de los métodos de la clase abstracta InputStream. Si se intentan utilizar
los métodos mark o
reset en un FileInputStream se generará
una IOException.
1.2.2.-Lectura de un FileInputStream
Una vez abierto el FileInputStream, se puede leer de él. El método read() tiene muchas opciones:
1)
int
read();
Lee un byte y devuelve -1 al final del
stream.
2)
int
read( byte b[] );
Llena todo el array,
si es posible. Devuelve el número de bytes leídos o -1 si se alcanzó el final
del stream.
3)
int
read( byte b[],int offset,int longitud );
Lee longitud bytes
en b comenzando por b[offset]. Devuelve el número de bytes leídos o -1 si se alcanzó el final del
stream.
1.2.3.-Cierre
de FileInputStream
Cuando se termina con un archivo, existen dos opciones para
cerrarlo: explícitamente, o implícitamente cuando se recicla el objeto (el
garbage collector se encarga de ello).
Para cerrarlo explícitamente, se utiliza el método close():
miArchivoSt.close();
1.2.4.-Apertura de un FileOutputStream
Para abrir un objeto FileOutputStream, se tienen las mismas posibilidades que
para abrir un archivo stream de entrada. Se le da al constructor un String o un objeto
File.
FileOutputStream miArchivoSt;
miArchivoSt = new
FileOutputStream( "/etc/kk" );
Como con los streams de entrada, también se puede utilizar:
File miArchivo FileOutputStream miArchivoSt;
miArchivo = new File(
"/etc/kk" );
miArchivoSt = new
FileOutputStream( miArchivo );
1.2.5.-Escritura en un FileOutputStream
Una vez abierto el archivo, se pueden escribir bytes de datos
utilizando el método write().
Como con el método read() de los streams de
entrada, tenemos tres posibilidades:
void write( int b );//Escribe un byte.
void write( byte b[] );//Escribe todo el array, si
es posible.
void write( byte b[],int offset,int longitud
);//Escribe longitud bytes en b comenzando por b[offset]
1.2.6.-Cierre de FileOutputStream
Cerrar un stream de
salida es similar a cerrar streams de entrada. Se puede utilizar el método
explícito: miArchivoSt.close();
O, se puede dejar que
el sistema cierre el archivo cuando se recicle miArchivoSt.
2.-Manejo
de archivos en C#
La manera de almacenar y recuperar información que perdure en
el tiempo se basa en el uso de “memoria secundaria”, compuesta esencialmente
por discos (diskettes, discos duros, CD, DVD, etc.) y ocasionalmente cintas. En
cualquiera de estos medios, la unidad de almacenamiento de información se
denomina archivo.
2.1.-Streams
La lectura y escritura a un archivo son hechas usando un
concepto genérico llamado stream. La
idea detrás del stream existe hace
tiempo, cuando los datos son pensados como una transferencia de un punto a
otro, es decir, como un flujo de datos. En el ambiente .NET se puede encontrar
muchas clases que representan este concepto que trabaja con archivos o con
datos de memoria.
Un stream es como se denomina a un objeto utilizado para
transferir datos. Estos datos pueden ser transferidos en dos posibles
direcciones:
1)
Si los
datos son transferidos desde una fuente externa al programa, entonces se habla
de “leer desde el stream”.
2)
Si los datos son transferidos desde el programa
a alguna fuente externa, entonces se habla de “escribir al stream”.
Frecuentemente, la fuente externa será un archivo, pero eso no
necesariamente es el caso, por lo que el concepto es utilizado ampliamente con
fuentes de información externas de diversos tipos.
-Leer o escribir
datos a una red utilizando algún protocolo de red, donde la intención es que
estos datos sean recibidos o enviados por otro computador.
-Lectura o escritura a
un área de memoria., otros.
Algunas clases que C# provee para resolver este acceso a
fuentes diversas incluyen las clases de tipo: Reader
y Writer.
2.1.1.-BufferedStream
Esta clase se utiliza para leer y para escribir a otro stream.
El uso de streams para la lectura y escritura de archivo es directa pero lenta.
Para operaciones de archivo es posible utilizar FileStream, donde el buffering
está ya incluido.
Las clases más relacionadas con la escritura y lectura de
archivos (File
Input/Output o File I/O) son:
1) FileStream, cuyo
propósito es lectura y escritura de datos binarios (no de texto legible), a
cualquier archivo de tipo binario, aunque se puede utilizar para acceder a
cualquier tipo de archivo, inclusive los de texto.
2) StreamReader y StreamWriter, las cuales están diseñadas para
lectura y escritura de archivos de texto. Estas clases se asumen como de un
nivel más alto que FileStream.
En la declaración de nombres/rutas de archivos en C#. Se
utiliza una sintaxis particular, anteponiendo el símbolo ‘@’ antes del string
con la ruta del archivo. Es decir:
string rutaarchivo = @”C:\Temp\archivo.txt”;
2.2.-Using System.IO
Para el uso de estas clases, es necesario referenciar el uso
del namespace System.IO,
ya que System
no contiene los elementos para el manejo de archivos. Por ello, los programas
con acceso a archivos deben incluir la línea:
using
System.IO;
2.2.1.-Constructor de StreamReader
El más simple de los constructores toma sólo el nombre/ruta del
archivo a abrir para lectura:
StreamReader
ob = new StreamReader(@”C:\Temp\archivo.txt”);
El constructor deja abierto el stream para
poder recuperar la información del archivo desde la instancia de StreamReader
declarada. Para cerrar un stream
o archivo, se invoca el método Close():
ob.Close();
2.2.2.-Lectura con StreamReader
Son básicamente tres los métodos propios de StreamReader que
permiten efectuar lectura desde el stream (archivo) declarado.
1) ReadLine()
Al igual que el conocido Console.ReadLine(), este método lee una línea
completa de un archivo de texto hasta el cambio de línea más próximo. Al igual
que su equivalente de consola, StreamReader.ReadLine()
no incluye en el string
el carácter de cambio de línea.
string
linea = ob.ReadLine()
2) ReadToEnd()
Este método, por su parte, se encarga de acumular la
información que hay desde la lectura anterior (que pudo haberse hecho con ReadLine(), por
ejemplo) hasta el final del archivo, todo en el mismo string.
string
linea = ob.ReadToEnd()
3) Read ()
Finalmente, el método simple Read() se
encarga de leer un caracter a la vez, lo que permite procesar símbolo por
símbolo el contenido del archivo. Convenientemente, este método reconoce el
cambio de línea y se lo salta como si no existiese. Cuando se encuentra con el
fin de archivo, retorna un valor –1, considerando que su retorno es siempre un int (y no un char).
int
SigCaracter = ob.Read();
Este mismo método ofrece una declaración alternativa
(sobrecarga), donde es posible leer una cantidad específica de caracteres y
almacenarlos en un arreglo de enteros.
char[] CharArray = new char[100];
int[] nChars = ob.Read(CharArray, 0, 100);
nChars
es un arreglo con los enteros retornados por el método, y será menor si es que
la cantidad de caracteres que quedan en el archivo es menor de 100.
2.2.3.- Escritura con StreamWriter
Esta clase funciona prácticamente de la misma manera que StreamReader, excepto
que su propósito es únicamente para escribir dentro de un archivo (u otro stream). Es relevante distinguir que en este caso, el
proceso de apertura para escritura considera que:
1)
Si el archivo no existe lo crea vacío para
comenzar a escribir.
2)
Si el archivo ya existe, lo deja vacío para
comenzar a escribir.
3) Si el archivo ya existe, es posible
abrirlo en forma “Append” (agregar) para escribir al final.
2.2.4.-Constructor de StreamWriter
El más simple de los constructores toma sólo el nombre/ruta del
archivo a abrir para escritura.
StreamWriter sw = new StreamWriter
(@”C:\Temp\archivo.txt”);
De la misma manera que en el caso de la lectura, para cerrar un
stream o
archivo, se invoca el método Close():
ob.Close();
2.2.5.-
Escritura con StreamWriter
Son básicamente dos los métodos propios de StreamWriter que
permiten escribir hacia el stream
(archivo) declarado y son los mismos que se usan para escribir en la consola: Write() y WriteLine().
1) WriteLine()
Totalmente equivalente a Console.WriteLine(), se utiliza la misma idea, y el mismo formato, sabiendo que se
estará escribiendo el texto no a la consola, sino que al stream abierto con el
constructor.
string
linea = “Texto de prueba”;
ob.WriteLine(linea);
ob.WriteLine(“Los
valores posibles son: {0} y {1}”, 3, 5);
2) Write ()
También presente, el método simple Write(), permite escribir texto en el stream, de la misma forma que su
equivalente método de la clase Console.
En este caso se reconocen las siguientes alternativas de uso:
Imprimir un string:
string
linea = “Texto de prueba”;
ob.Write(linea);
Imprimir un carácter:
char
caracter = ‘T’;
ob.Write(caracter);
Imprimir un arreglo de caracteres:
char[]
caracteres = new char[100];
for(int
i=0; i<100; i++) caracteres[i] = ‘+‘;
ob.Write(caracteres);
Imprimir una porción de un arreglo de caracteres:
char[]
caracteres = new char[100];
for(int
i=0; i<100; i++) caracteres[i] = ‘+‘;
ob.Write(caracteres,
25, 50);
//
Desde posición 25 se escriben 50 caracteres