Acceso de datos
• InputStream: se caracteriza por ser una superclase abstracta que se encarga de
leer un stream de bytes. Solo es capaz de leer un byte a la vez, por lo que lo hace
una opción bastante lenta. Al ser una superclase, no es útil por sí misma, es por
ese motivo que se usan sus subclases.
Se considera una superclase a aquella clase padre de la que derivan
diferentes clases, también conocidas como subclases. Las subclases derivan
de la clase padre y heredan todas sus propiedades y métodos.
Una clase abstracta es un tipo de clase que permite la declaración de
métodos, pero no su implementación. La implementación será realizada
por las clases que implementen esa clase.
Si queréis refrescar conceptos, podéis consultar la documentación de Java,
donde se explica mucho más detallado:
https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html
InputStream
FileInputStream BufferedInputStream
Ilustración 5. Esquema de la estructura de InputStream.
Esta clase tiene diferentes métodos que serán útiles si queremos leer datos de
un stream de datos:
Método Descripción
avaliable() Este método se encarga de devolver los bytes disponibles en
el InputStream.
30
Acceso de datos
close() Es el método que se encarga de cerrar el stream.
mark() Este método se encarga de marcar la posición de los bytes
leídos del stream.
read() Se encarga de leer los bytes de datos uno a uno.
read(byte[] array) Se encarga de leer los bytes del stream y los almacena en un
array.
reset() Método que se encarga de quitar la marca de los bytes leídos
por el método mark().
skips() Este método se encarga de descartar un numero especificado
de bytes del stream que este leyendo en ese momento.
Tabla 4. Tabla de métodos importantes de InputStream.
• FileInputStream: es una subclase de InputStream que se utiliza para leer streams
de bytes. Está enfocada al tratamiento de bytes sin procesar, como datos de
imágenes, vídeo o audio. También puede leer ficheros de caracteres, pero no es
el más adecuado, es mejor FileReader.
public static void lecturaFileInputStream() throws IOException{
byte[] array = new byte[100];
try {
//Utilizamos para leer el fichero un archivo en la
carpeta resources
InputStream archivo = new
FileInputStream("src/main/resources/ejemplo.txt");
//Lectura de bytes desde el Input Stream
archivo.read(array);
// Convierte los bytes en un string
String datos = new String(array);
System.out.println(datos);
// Cerramos el inputStream
archivo.close();
} catch (FileNotFoundException e) {
System.out.println("No se ha encontrado el
archivo");
e.printStackTrace();
}
}
31
Acceso de datos
En el ejemplo de arriba, primero hemos creado un InputStream con una nueva
instancia de su subclase FileInputStream con la ruta relativa del fichero que se
encuentra en nuestro proyecto, pero puede ser cualquier fichero de bytes. Para
leer los datos del fichero, hemos implementado el método read(), que irá
guardando los bytes en el array de bytes que hemos creado al principio. Para
imprimir por consola los bytes, hemos creado un string pasándole el array de
bytes. Una vez realizado todo esto, tendremos que cerrar el stream con el
método close().
• BufferedInputStream: es una clase que extiende (es decir, que implementa todos
los métodos) de InputStream y que se utiliza para leer streams de datos en bytes
de manera más eficiente. Esta clase se caracteriza por tener un búfer interno de
8192 bytes. Durante la operación de lectura, BufferedInputStream se encargará
de leer una porción de bytes del fichero que se encuentra en el disco y
almacenará los bytes en el búfer interno. Una vez almacenados en el búfer
interno, se leerán los bytes individualmente.
try {
// creamos un InputStream para coger el fichero de la ruta de
nuestro proyecto
FileInputStream fichero = new
FileInputStream("src/main/resources/ejemplo.txt");
// Creamos un BufferedInputStream y le pasamos el archivo al
constructor
BufferedInputStream bufer = new
BufferedInputStream(fichero);
// Se encarga de leer el primer byte del fichero
int i = bufer.read();
while (i != -1) {
System.out.print((char) i);
// Va leyendo cada byte del buffer
i = bufer.read();
}
bufer.close();
} catch (Exception e) {
System.out.println("Se ha producido un error");
e.getStackTrace();
}
En este ejemplo, podemos ver que hemos creado en primer lugar un fichero con
una nueva instancia FileInputStream a la cual le pasaremos la ruta del fichero que
queremos leer. A continuación, creamos un búfer BufferedInputStream, al cual
32
Acceso de datos
le pasaremos el fichero creado en la línea anterior a través del constructor.
Seguidamente, tendremos que procesar el fichero, para ello utilizaremos el
método read() del búfer e iremos leyendo byte a byte el contenido. Mientras el
byte no equivalga a -1 se irá recorriendo el fichero e imprimiendo por consola el
contenido. Una vez terminado el proceso, cerraremos el búfer. Todo el ejercicio
está envuelto en un try-catch para controlar los posibles errores que puedan
ocurrir, como que no encuentre el fichero o se produzca un error leyendo el búfer
de bytes. Tenemos que tener en cuenta que siempre se deben controlar los
errores
1.3.1.2. Escritura de datos
Para la escritura de datos, también podemos diferenciar dos grupos: el de escritura de
ficheros de caracteres y el de escritura de ficheros de bytes. Primero detallaremos las
clases de escritura de ficheros de caracteres. El proceso es muy parecido al de lectura
de datos, pero se usarán las clases que detallaremos a continuación:
Ficheros de texto
Para la escritura de ficheros de texto, usaremos las clases que podemos encontrar en el
paquete java.io.
33
Acceso de datos
• Writer: es una superclase abstracta que no se usa sola por sí misma, sino
que siempre va acompañada de sus subclases, como hemos visto con
InputStream.
BufferedWriter
CharArrayWriter
PrintWriter
Writer FilterWriter
OutputStreamWrit
FileWriter
er
PipedWriter
StringWriter
Ilustración 6. Esquema de las subclases de la clase Writer.
Estos son los métodos de la clase más destacables:
Método Descripción
append(char c) Este método añade el carácter que le indiquemos
close() El método cierra el stream que está en uso.
El método obliga escribir toda la información que tenga el objeto
flush()
OutputStreamWriter al destino correspondiente.
Este método nos indica qué encoding se está utilizando para
getEncoding()
escribir los datos en el fichero.
Este método escribe en un fichero un carácter que le pasemos
write(char c)
por parámetro.
Este método escribe en un fichero el array que le pasemos por
write(char[] array)
parámetro.
34
Acceso de datos
Este método escribe en un fichero el string que le pasemos por
write(String data)
parámetro.
Tabla 5. Métodos más destacables de Writer.
• OutputStreamWriter: es una subclase de Writer que se usa para convertir
streams de caracteres, pero también se utiliza para streams de bytes.
Como es capaz de tratar tanto caracteres como bytes, se usa como puente
entre estos dos tipos de datos.
A continuación, veremos un ejemplo de escritura de datos. Primero, es
necesario la creación de un nuevo fichero FileOutputStream, creando una
nueva instancia y pasándole al constructor la ruta del fichero que
queremos escribir. Este objeto será un auxiliar que pasarle al constructor
para poder crear un OutputStreamWriter. Seguidamente, para escribir
información, deberemos tener un string con los caracteres que escribir y
hacer una llamada al método write() pasándole por parámetro el string.
Una vez finalizado el proceso, deberemos cerrar el stream con el método
close().
String data = "Ejemplo de escritura de datos con
FileOutputStream";
try {
//Creamos un FileOutputStream
FileOutputStream archivo = new
FileOutputStream("src/main/resources/outputStream.
txt");
// Creamos el stream que nos va a ayudar a escribir los
datos en el fichero indicado
OutputStreamWriter escribirDatos = new
OutputStreamWriter(archivo);
// Con este método escribiremos datos en el fichero
escribirDatos.write(data);
// Cerramos la el writer
escribirDatos.close();
}catch (Exception e) {
System.out.println("Se ha producido un error");
e.getStackTrace();
}
En este ejemplo, tendremos que realizar la operación con un try-catch para
poder controlar los posibles errores que puedan producirse. Los más usuales
siempre son un FileNotFoundException cuando no encuentra la ruta del fichero
35
Acceso de datos
indicado o un error en el proceso de escritura de los datos. Este método lanza un
IOException, por lo tanto, tendremos que tenerlo controlado en un catch.
• FileWriter: es una clase que nos permitirá escribir caracteres en un
archivo. Es una subclase de OutputStreamWriter.
String data = "Ejemplo de escritura de datos con
FileWriter";
try {
// Creamos un FileWriter
FileWriter output = new
FileWriter("src/main/resources/fileWriter.txt");
// Con este método escribiremos datos en el fichero
output.write(data);
// Cerramos la el writer
output.close();
}catch (Exception e) {
System.out.println("Se ha producido un error");
e.getStackTrace();
}
El funcionamiento para escribir datos con esta clase es muy parecido al
anterior, usan los mismos métodos, ya que las dos clases heredan métodos
de la clase padre Writer. En primer lugar, deberemos definir la información
que queremos escribir en el fichero. Crearemos un FileWriter definiendo la
ruta del fichero que escribir. Seguidamente, haciendo uso del método write(),
pasaremos por parámetro el string con la frase que hemos definido. Una vez
escrito, cerraremos el Writer con el método close().
• BufferedWriter: es una subclase de Writer que también permite
almacenar datos en el búfer. Esta es la clase más eficiente para escribir
datos en un archivo, ya que permite escribir los datos en el búfer y no en
el disco. Una vez que el búfer esté lleno o se cierre, los datos se escriben
en el disco.
Como vamos a ver en el siguiente ejemplo, en primer lugar, deberemos
crear un FileWriter creando una nueva instancia y pasándole al
constructor la ruta del fichero que queremos usar. A continuación,
crearemos el BufferedWriter y le pasaremos el archivo creado en la línea
anterior. Seguidamente, podremos escribir los datos en el fichero y al
acabar cerraremos el búfer. Como se puede apreciar, el proceso es igual
al ser subclases de Writer.
36
Acceso de datos
String data = "Ejemplo de escritura de datos con
BufferedWriter";
try {
// Creamos un FileWriter
FileWriter file = new
FileWriter("src/main/resources/output.txt");
// Creates a BufferedWriter
BufferedWriter output = new BufferedWriter(file);
// Con este método escribiremos datos en el
fichero
output.write(data);
// Cerramos la el writer
output.close();
}catch (Exception e) {
System.out.println("Se ha producido un error");
e.getStackTrace();
}
Ficheros binarios
La escritura de ficheros binarios consiste en escribir datos de tipo byte en un fichero.
Este tipo de fichero podrá ser un fichero de datos codificado, un archivo de audio, uno
de vídeo, una foto, etcétera.
Para la escritura de ficheros binarios, usaremos las clases que podemos encontrar en el
paquete java.io.
• OutputStream: pertenece al paquete java.io y es una superclase abstracta
que se utiliza para escribir streams de bytes.
OutputStream
FileOutputStream
Ilustración 7. Esquema de las subclases de la clase OutputStream.
Estos son los métodos de la clase más destacables:
37
Acceso de datos
Método Descripción
close() El método cierra el stream que está en uso.
flush() Este método libera los datos del stream.
write(int b) Este método se encarga de escribir un byte indicado al fichero.
write(byte[]
Este método escribe todo el array en un fichero.
array)
Tabla 6. Métodos más importantes de la clase OutputStream.
• FileOutputStream: esta clase se encargará de escribir streams de bytes en
los ficheros. Es una subclase que hereda de OutputStream.
String data = "Ejemplo de escritura de datos con
FileOutputStream";
try {
FileOutputStream output = new
FileOutputStream("src/main/resources/escrituraBytes/file
Output.txt");
byte[] array = data.getBytes();
//Escribimos los datos en el archivo
output.write(array);
// Cerramos el writer
output.close();
} catch (Exception e) {
System.out.println("Se ha producido un error");
e.getStackTrace();
}
El procedimiento para usar las clases y sus métodos sigue la dinámica explicada
ya en diferentes apartados. En primer lugar, crearemos una nueva instancia de
la clase FileOutputStream y le pasaremos al constructor la ruta del fichero que
queremos escribir. Como el método write() necesita un array de bytes para
escribir los datos, transformaremos el string que queremos escribir en un array
de bytes con el método getBytes() que tiene la clase String. A continuación, si no
se produce ningún error, cerraremos el stream con el método close().
38
Acceso de datos
• ByteArrayOutputStream: es una clase que se utiliza para envolver
OutputStream para dar soporte a las capacidades del búfer. Se trata de
una de las clases más eficientes de escritura de datos.
Estos son solo algunas de las más importantes, pero en realidad existen muchas más
clases para escribir datos en archivos de texto. Aquí solo mostramos una muestra de lo
que podemos llegar a hacer con Java, pero existen infinidad de posibilidades.
String data = "Ejemplo de escritura de datos con
BufferedOutputStream";
try {
BufferedOutputStream bufer = new
BufferedOutputStream(new
FileOutputStream("src/main/resources/escrituraBytes/outp
ut.txt"));
bufer.write(data.getBytes());
bufer.close();
} catch (IOException e) {
System.out.println("Se ha producido un error");
e.getStackTrace();
}
1.4. Trabajo con archivos XML
En este tema, trabajaremos con archivos XML. Para refrescar la memoria, los XML
(lenguaje de marcas extensible) es un fichero de texto simple de metalenguaje
extensible que consta de diferentes etiquetas que contienen los datos.
Podemos entender el concepto metalenguaje como el código que utilizaremos
para describir la información que queremos transmitir en un fichero XML. Es un
lenguaje especializado para describir nuestro lenguaje natural en código:
mediante símbolos, iremos representando la estructura de lo que se quiera
representar.
Un XML se compone de la declaración del XML:
<?xml versión= “1.0” encoding= “UTF-8”?>
39
Acceso de datos
En esta parte se declara el XML, la versión del documento y se define el encoding que se
utilizará en el fichero. Debemos definir esta línea como nuestra primera línea de
cualquier fichero XML. Los campos versión y encoding deben estructurarse en este
orden para considerarse una estructura correcta. De todos modos, las declaraciones son
opcionales, pero si se establece encoding se deberá añadir también la declaración
versión. La versión nos servirá para indicar en qué momento se realizó el documento y
seguir una evolución del estándar si se modifica el archivo en un futuro. El encoding,
como hemos explicado en apartados anteriores, nos permitirá definir qué caracteres
vamos a utilizar. Por defecto, utilizaremos UTF-8.
A continuación, añadiremos el cuerpo del XML, que es la parte más importante del
fichero. Este documento adquiere una estructura de árbol, compuesto por un elemento
raíz o principal dentro del cual añadiremos el resto de los elementos.
<raíz>
<tronco>
<rama1></rama1>
<rama2></rama2>
</tronco>
</raíz>
Como vemos en este ejemplo de cuerpo, podemos ver que se estructura por un
elemento padre "raíz" del cual se desprenden diferentes hijos, en este caso, el elemento
"tronco", o subhijos, que serían los elementos "rama". Podrá tener tantos hijos como
sea necesario, pero el elemento padre no se podrá repetir. Dentro de cada etiqueta, se
podrá encontrar la información de cada elemento.
Para finalizar, nuestro XML debería parecerse a algo más o menos así:
40