Bienvenidos sean a este post, hoy hablaremos sobre como manejar archivos desde Java tanto para la lectura como para la escritura.
El paquete encargado de este es java.io, en este paquete tendremos todos los streams necesarios para estas tareas, permitiendonos manipular todo tipo de archivos, los streams pueden ser de dos tipos:
- InPutStream, se usa para leer desde el origen
- OutPutStream, se usa para escribir en un destino
Estas funciones soportan mucho tipo de datos y redes pero nosotros nos centraremos en los mas utilizados.
Byte streams
Se usan para crear o leer por medio de bytes de 8-bit, si bien hay muchas clases relacionadas con este tipo de stream las mas usadas son: FileInputStream y FileOutputStream; estos metodos nos permitirian leer segmentos y poder escribirlos en su destino. Veamos un ejemplo:
import java.io.*;
public class CopyFile
{
public static void main(String[] args)
throws IOException
{
FileInputStream in = null;
FileOutputStream out = null;
try
{
in = new FileInputStream("entrada.txt");
out = new FileOutputStream("salida.txt");
int c;
while(( c = in.read()) != -1)
{
out.write(c);
}
}
finally
{
if (in!=null)
in.close();
if (out != null)
out.close();
}
}
}
En este ejemplo, primero vamos a crear una clase donde nos permitira copiar el contenido de un archivo en otro. Observen como utilizamos el paquete java.io.* para que usen todas las clases pertenecientes a ella, despues para poder utilizarla debemos hacer una modificacion en nuestro main para especificarle que utilice el IOException para poder tener acceso a las clases necesarias. Lo siguiente sera crear dos objetos: uno derivado de la clase FileInputStream (in) y otro de la clase FileOutputStream (out); a ambos le daremos un valor de null. Despues utilizaremos un metodo try para capturar algun posible error. Lo siguiente es crear nuestros objetos in y out, y mediante sus respectivos constructores les pasaremos los archivos a leer y escribir respectivamente. Despues creareamos una variable de tipo int llamada c, mediante un while ingresaremos todo el contenido a c y lo leera mientras el resultado de read sea distinto de -1. Es decir, que sea distinto al final del archivo y una vez terminado tendremos esto ya tenemos todo listo. Solo nos resta el finally donde usaremos dos condicionales que si in y out son distintas de null cierra los objetos y por ende liberando los archivos.
Nota:
Antes de compilarlo y ejecutarlo generen un archivo llamado entrada.txt y pueden ponerle el siguiente texto:
Este sera un año espectacular para aprender algo nuevo!
Con el archivo generado podemos compilar nuestro programa, ejecutarlo pero no nos dara ningun mensaje de notificacion sino simplemente creara un nuevo archivo llamado salida.txt con el mismo mensaje utilizado en entrada.txt pasemos al siguiente tipo de stream
Character Stream
Este es otro tipo de stream muy similar al anterior pero en lugar de usar bytes de 8-bits utilizamos unicode de 16 bits, al igual que el caso anterior tiene varios metodos para poder trabajar pero los mas utilizados son FileReader y FileWriter. A su vez estos dos para efectuar sus tareas utilizan los metodos FileInputStream y FileOutputStream respectivamente pero la mayor diferencia es que los metodos FileReader y FileWriter utilizan dos bytes simultaneamente. Tomemos el ejemplo anterior y efectuemos las siguientes modificaciones:
FileInputStream in = null;FileOutputStream out = null;
Por estas dos lineas:
FileReader in = null;FileWriter out = null;
Reemplazamos las clases de los objetos para poder ser utilizados por los stream de caracteres, nuestra siguiente modificacion sera en estas lineas:
in = new FileInputStream("entrada.txt");
out = new FileOutputStream("salida.txt");
Por estas otras lineas:
in = new FileReader("entrada.txt");
out = new FileWriter("salida.txt");
Como pueden ver le informamos a los nuevos constructores los archivos para leer y el de salida, nuestro codigo quedara de la siguiente manera:
import java.io.*;
public class CopyFile
{
public static void main(String[] args)
throws IOException
{
FileReader in = null;
FileWriter out = null;
try
{
in = new FileReader("entrada.txt");
out = new FileWriter("salida.txt");
int c;
while(( c = in.read()) != -1)
{
out.write(c);
}
}
finally
{
if (in!=null)
in.close();
if (out != null)
out.close();
}
}
}
El funcionamiento es exactamente al mismo pero ahora en lugar de usar una forma de copiar el archivo utiliza otra, al igual que el caso anterior deberan tener un archivo llamado entrada.txt, les recomiendo borrar el archivo antes generado, compilenlo y ejecutenlo, recuerden que no devuelve ningun mensaje solamente nos hace una copia del archivo de entrada (entrada.txt), si se los copio (nuevamente) vamos por el buen camino, verifiquen que tenga el mismo texto como se ve a continuacion:
$ cat entrada.txt
Este sera un año espectacular para aprender algo nuevo!
$ cat salida.txt
Este sera un año espectacular para aprender algo nuevo!
$
Con esto hemos cubierto dos de los dos streams mas comunes que podremos utilizar para manipular archivos, pasemos a ver uno para manejar la Entrada/Salida.
Standard Streams
Todos los lenguajes poseen un standard para manejar la E/S, Java tambien posee un soporte para permitir al usuario ingresar por medio de un teclado y mostrarlo en una pantalla. En C daba la posibilidad de enviarlo a la impresora pero era otra epoca, en C o C++ se utilizaba STDIN, STDOUT y STDERR pero en Java utilizaremos los siguientes:
- System.in, Standard Input, es la interfaz de ingresar datos al programa, en general es el teclado
- System.out, Standard Output, es donde se mostrara la informacion generada por el usuario, en general es el monitor
- System.err, Standard Error, es el encargado de mostrar el error producido por la data del usuario, en general es el monitor
Para entender un poco mejor este concepto veamos el siguiente ejemplo:
import java.io.*;
public class ReadConsole
{
public static void main(String[] args) throws IOException
{
InputStreamReader cin = null;
try
{
cin = new InputStreamReader(System.in);
System.out.println("Ingresa Caracteres, 'q' para salir");
char c;
do
{
c = (char) cin.read();
System.out.print(c);
} while(c != 'q');
}
finally
{
if (cin != null) cin.close();
}
}
}
El inicio es similar a los casos anteriores pero esta vez utilizaremos la clase InputStreamReader con la cual crearemos un objeto llamado cin. Esta clase se encarga de leer todos los datos que ingresemos para este caso por teclado, dentro del bloque try a cin le asignaremos la forma de ingreso, en este caso System.in, para luego mostrar un mensaje para ingresar los caracteres e indicando que «q» sera la salida. Despues crearemos una variable de tipo char llamada c, y un bucle de do…while, donde primero asignara a c todo el contenido dentro de cin mediante el metodo read. La siguiente linea mostrara el contenido de c, y el while se encargara de chequear si c es distinto de q para hacer otro bucle. Terminado el bucle en el bloque de finally primero verificara si cin es distinto de null y en caso de ser verdadero procedera a cerrarlo y finalizando el programa.
Si lo compilamos y ejecutamos podremos hacer lo mismo que se hace en el siguiente video
Como pueden ver a medida que ingresamos los caracteres y presionamos Enter este nos lo muestra abajo siempre y cuando no ingresemos el caracter q donde saldra del programa.
Vamos a hablar sobre un tema que mencionamos al inicio, porque es uno de los mas utilizados con mas frecuencia FileInputStreamer. Como dijimos, con esta clase podremos leer cualquier origen que le informemos. En general, van a ser archivos pero tambien como vimos antes se puede crear un nuevo objeto con el metodo new y algunos de los constructores disponibles. Luego le informaremos a traves de un String el archivo y/o su ubicacion, repasemos un ejemplo:
FileInputStream f = new FileInputStream("/home/tinchicus/hello.txt");
Con esto le informamos donde esta el archivo a cual debe acceder para leer toda la informacion almacenada en ella. Otra forma de hacerlo podria hacerse de la siguiente forma:
File a = new File("/home/tinchicus/hello.txt");
FileInputStream f = new FileInputStream(a);
En este caso crearemos un objeto de la clase File y la llamaremos a para luego asignarselo a nuestro objeto creado de la clase FileInputStream. A continuacion, veremos algunos metodos utiles para poder trabajar con esta clase:
- public void close(), este metodo se encargara de cerrar el stream que hayamos abierto y libera cualquier elemento del sistema asociado
- protected void finalize(), este metodo se utiliza para limpiar la conexion con el objeto y en general es llamado para asegurarse de que no hay mas conexiones a ese objeto
- public int read(int r), este metodo lee el byte de dato especificado del InputStream, devuelve un valor entero, el proximo byte de data y cuando devuelve -1 significa el fin del origen
- public int read(byte[] r), toma la longitud en bytes de r y lo almacena en un array, devuelve el numero total de bytes leido, si devuelve -1 significa que es el fin del origen
- public int available(), devuelve el numero de bytes disponibles en nuestro stream de entrada.
Todos los metodos anteriormente descriptos deben ser usados con throws IOException que como vimos en uno de los primeros ejemplos, podemos declararla en el comienzo del main y este bajara a todos los metodos que corresponda. Tambien existen otras dos clases importantes:
- ByteArrayInputStream, permite tener un buffer en memoria para ser usado como un InputStream, les dejo este link para mas informacion y ejemplos.
- DataInputStream, es usada en el contexto de DataOutputStream y nos permite leer primitivos, les dejo este link para mas informacion y ejemplos.
La clase anterior era la encargada de leer informacion de un InputStream, pasemos ahora a la clase FileOutputStream la cual se encargara de escribir en un destino la informacion recibida, por lo general el destino es un archivo y si dicho archivo no existe primero lo creara para poder escribir en el mismo. La forma de definirlo es similar al anterior:
FileOutputStream f = new FileOutputStream("/home/tinchicus/salida");
O como en el caso anterior tambien podemos hacerlo de esta forma:
File a = new File("/home/tinchicus/salida");
FileOutputStream f = new FileOutputStream(a);
Pasemos a ver algunos metodos disponibles para poder trabajar con esta clase:
- public void close(), este metodo se encargara de cerrar el stream que hayamos abierto y libera cualquier elemento del sistema asociado
- protected void finalize(), este metodo se utiliza para limpiar la conexion con el objeto y en general es llamado para asegurarse de que no hay mas conexiones a ese objeto
- public void write(int w), este metodo escribe el byte especificado al stream de salida (Output stream)
- public void write(byte[] w), escribe la longitud de w en bytes desde el array de bytes mencionado al stream de salida (Output stream)
Al igual que la clase anterior esta tambien son a traves de throws IOException, el cual se puede declarar en el main y este sera utilizado por los metodos que correspondan. Tambien dispondremos de dos metodos interesantes:
- ByteArrayOutputStream, crea un buffer en memoria y toda la informacion enviada al stream es almacenada en este buffer, para mas informacion usa este link
- DataOutputStream, te permite escribir los primitivos en una salida, para mas informacion usa este link
Para entender un poco mejor este concepto veamos el siguiente ejemplo:
import java.io.*;
public class fileStreamTest
{
public static void main(String[] args)
{
try
{
byte bWrite[] = { 11,21,3,40,5 };
OutputStream os = new FileOutputStream("prueba.txt");
for(int x = 0; x < bWrite.length; x++)
os.write( bWrite[x] );
os.close();
InputStream is = new FileInputStream("prueba.txt");
int tamano = is.available();
for(int i = 0; i < tamano; i++)
System.out.print((char)is.read() + " ");
System.out.print("\n");
is.close();
}
catch(IOException e)
{
System.out.print("Excepcion");
}
}
}
En este ejemplo, crearemos un Array de tipo byte donde almacenaremos algunos valores, despues mediante la clase OutputStream crearemos un objeto llamado os. Le diremos cual va a ser su archivo de destino, despues con un bucle iremos escribiendo en ese archivo los valores almacenados en bWrite. Lo siguiente es cerrar ese archivo, creamos otro objeto pero en este caso de tipo InputStream donde le diremos cual va a ser nuestro origen. Es decir, el archivo recien creado y definimos una variable llamada tamano donde almacenaremos el tamaño de nuestro origen. Este actuara como limite para nuestro bucle y mostraremos todos los valores almacenados. Para una vez finalizado, generar un Enter, cerrar el objeto y liberar el archivo. En el bloque del catch, si surge un error mostrara un mensaje, si lo compilamos y ejecutamos obtendremos la siguiente salida:
tinchicus@dbn001vrt:~/programacion/java/program$ java fileStreamTest
(
PuTTYtinchicus@dbn001vrt:~/programacion/java/program$ PuTTY
Como pueden ver no es la salida que deseamos ver, para ello debemos buscar la siguiente linea:
System.out.print((char)is.read() + " ");
La cual reemplazaremos con la siguiente linea:
System.out.print((byte)is.read() + " ");
En lugar de byte puede ser int, en ambos casos si lo compilamos y ejecutamos obtendremos esta salida:
tinchicus@dbn001vrt:~/programacion/java/program$ java fileStreamTest
11 21 3 40 5
tinchicus@dbn001vrt:~/programacion/java/program$
Observen como ahora funciono, esto puede deberse al motivo de porque al guardar datos en formato de byte al intentar recuperarlo puede no coincidir con el tipo char pero si al tipo de como se guardo, byte, o del formato original de los mismos, int, porque para esto podria entrar el tema de los segmentos de memoria pero sobre esto no hablaremos en este momento.
Pasemos a ver la navegacion de archivos y E/S, para conocer los conceptos basicos existen estas tres clases:
- File, es el encargado de todos los metodos relacionados con las acciones para directorios, archivos, etc. Para mas informacion les dejo este link.
- FileReader, esta clase es utilizada para leer stream de caracteres. Para mas informacion les dejo este link.
- FileWriter, esta clase es utilizada para escribir stream de caracteres. Para mas informacion les dejo este link,
Mediante la clase File podemos manipular los archivos y directorios de nuestro ordenador. Para mas informacion usaremos otro post pero aqui veremos como crear directorios y como listarlos. Para crear un directorio debemos usar alguno de estos dos metodos:
- mkdir: es para crear un directorio, si se creo correctamente devolvera un verdadero de lo contrario devuelve un falso, esto puede deberse a dos motivos, uno es la carencia de los permisos, el path fue mal informado o no existe
- mkdirs: este metodo sirve para crear un directorio y todos sus parientes
En java a diferencia de Windows para diferenciar un directorio de otro se utiliza la barra de division (/) como en Unix o Linux, tanto para las plataformas Unix o Windows asi por ejemplo para indicar un path debemos usar la siguiente forma:
/home/tinchicus/hola.txt
Esto es para las plataformas Unix, en cambio para las plataformas Windows se deberia hacer asi:
c:/java/prueba/hola.txt
Este formato es valido para Java en Windows. Continuemos con el tema de los directorios, tomemos como ejemplo poder listar todos el contenido de un directorio y para ello se utiliza el metodo list. Para entender mejor esto, veamos el siguiente ejemplo:
Archivos.java
import java.io.*;
public class Archivos
{
public static void main(String[] args)
{
try
{
String dirs = "./test";
String archivos;
File d = new File(dirs);
d.mkdirs();
for(int i=0; i < 3; i++)
{
archivos = dirs + "/archivo" + i + ".txt";
OutputStream sal = new FileOutputStream(archivos);
sal.close();
}
String[] contenido = d.list();
for(String cont : contenido)
System.out.println(cont);
}
catch(IOException e)
{
System.out.println("Oh no! Algo ocurrio!!!");
}
}
}
La primer variable o propiedad, el nombre correcto en una clase, nos servira para establecer el nombre del directorio de trabajo. Luego solamente declaramos una variable que usaremos en un momento. Lo siguiente es la instancia que representara a nuestro directorio de trabajo, y podamos manipularlo. Con la instancia definida, le aplicamos a mkdirs para crear al directorio, en caso de no existir, y tener todo listo para crear el contenido en este. Para ello, usamos un bucle y este generara tres archivos. Para definir los nombres de cada uno usamos a archivos y en el concatenaremos a: el directorio de trabajo (dirs), un nombre identificador, el valor de i en cada pasada para diferenciarlos y finalmente la extension del mismo. Definimos una instancia u objeto de OutputStream y para ello usamos a FileOutputStream y como identificacion le pasamos la cadena que generamos anteriormente. Para finalmente, cerrar el objeto creado y por ende al archivo. Como mencionamos, esto nos va a generar tres archivos vacios dentro del directorio test.
Con esta tarea realizada, lo siguiente es crear un array de tipo String para almacenar el contenido devuelto por el metodo list. Este lo aplicaremos sobre la instancia de File que generamos al inicio como directorio de trabajo (d). Y el ultimo bucle es para mostrar todo lo ingresado en el array anterior. Antes de finalizar, el bloque try/catch lo agregamos porque OutputStream necesita manejar excepciones ante la posibilidad de que se genere alguna. Con todo esto comentado, compilemos y ejecutemos para ver como es su salida:
$ java Archivos
archivo2.txt
archivo1.txt
archivo0.txt
$
En este caso podemos ver que no solo genero el directorio sino que tambien le agrego los archivos que le solicitamos. Con esto completamos un vistazo general a todo lo necesario para poder manipular nuestros archivos y tambien los streams mas usados para ingresar o recuperar y mostrar informacion.
En resumen, hoy hemos visto como manipular archivos, por medio del byte stream, tambien mediante character stream, hemos visto como se puede leer informacion de un archivo, como escribirla en un archivo, como crear un programa de copia de archivo, tambien el standard stream, el cual es encargado de poder interactuar con los sistemas de entrada y salida como un teclado (entrada) y un monitor (salida), hemos visto un ejemplo y como funciona con un video, asi como tambien otros elementos complementarios para manipular directorios. Espero les haya sido de utilidad y les dejo un link a GitHub donde estan los codigos creados hoy:
GitHub / Archivos y Entrada/Salida
Les dejo algunas de mis redes sociales para seguirme o recibir una notificacion cada vez que subo un nuevo post:


Donación
Es para mantenimento del sitio, gracias!
$1.50





