Leer y escribir archivos en Java

En este tutorial, leeremos y escribiremos archivos en Java usando FileReader, FileWriter, BufferedReader, BufferedWriter, FileInputStream, FileOutputStream, etc.

Introducción

En este artículo, nos sumergiremos en Lectura y escritura de archivos en Java.

Al programar, ya sea que esté creando una aplicación móvil, una aplicación web o simplemente escribiendo scripts, a menudo tiene la necesidad de leer o escribir datos en un archivo. Estos datos pueden ser datos de caché, datos que recuperó para un conjunto de datos, una imagen o cualquier otra cosa que se le ocurra.

En este tutorial, mostraremos las formas más comunes en las que puede leer y escribir archivos en Java.

Java proporciona varias API (también conocidas como E/S de Java) para leer y escribir archivos desde sus versiones iniciales. Con versiones posteriores, Java I/O se mejoró, simplificó y mejoró para admitir nuevas funciones.

Antes de entrar en algunos ejemplos reales, sería útil comprender las clases disponibles que manejarán la lectura y escritura de datos en archivos. En las siguientes secciones, proporcionaremos una breve descripción general de las clases de E/S de Java y explicaremos lo que hacen, luego echaremos un vistazo a Java NIO Streams y, finalmente, mostraremos algunos ejemplos de lectura y escribir datos en archivos.

Flujos de E/S

Hay dos tipos de flujos que puede usar para interactuar con archivos:

  1. Flujos de personajes
  2. Flujos de bytes

Para cada uno de los tipos de flujo anteriores, hay varias clases de soporte incluidas con Java, que veremos rápidamente a continuación.

Flujos de caracteres

Los flujos de caracteres se utilizan para leer o escribir el tipo de datos de caracteres. Veamos las clases más utilizadas. Todas estas clases están definidas en el paquete java.io.

Aquí hay algunas clases que debe saber que se pueden usar para leer datos de caracteres:

  • Lector: una clase abstracta para leer un flujo de caracteres.
  • InputStreamReader: clase utilizada para leer el flujo de bytes y convertirlo en flujo de caracteres.
  • Lector de archivos: Una clase para leer los caracteres de un archivo.
  • BufferedReader: este es un contenedor sobre la clase Reader que admite capacidades de almacenamiento en búfer. En muchos casos, esta es la clase más preferible para leer datos porque se pueden leer más datos del archivo en una llamada read(), lo que reduce la cantidad de operaciones de E/S reales con el sistema de archivos.

Y aquí hay algunas clases que puede usar para escribir datos de caracteres en un archivo:

  • Escritor: Esta es una clase abstracta para escribir los flujos de caracteres.
  • OutputStreamWriter: esta clase se usa para escribir flujos de caracteres y también convertirlos en flujos de bytes.
  • FileWriter: una clase para escribir caracteres en el archivo.
  • BufferedWriter: este es un contenedor sobre la clase Writer, que también admite capacidades de almacenamiento en búfer. Esta es la clase más preferible para escribir datos en un archivo, ya que se pueden escribir más datos en el archivo en una llamada write(). Y al igual que BufferedReader, esto reduce el número total de operaciones de E/S con el sistema de archivos.

Flujos de bytes {#flujos de bytes}

Los flujos de bytes se utilizan para leer o escribir datos de bytes con archivos. Esto es diferente de antes en la forma en que tratan los datos. Aquí trabaja con bytes sin formato, que pueden ser caracteres, datos de imagen, datos Unicode (que requieren 2 bytes para representar un carácter), etc.

En esta sección vamos a echar un vistazo a las clases más utilizadas. Todas estas clases están definidas en el paquete java.io.

Estas son las clases utilizadas para leer los datos de bytes:

  • Flujo de entrada: Una clase abstracta para leer flujos de bytes.
  • FileInputStream: una clase para simplemente leer bytes de un archivo.
  • BufferedInputStream: este es un contenedor sobre InputStream que admite capacidades de almacenamiento en búfer. Como vimos en los flujos de caracteres, este es un método más eficiente que FileInputStream.

Y aquí están las clases utilizadas para escribir los datos de bytes:

  • Salida de corriente: Una clase abstracta para escribir flujos de bytes.
  • FileOutputStream: una clase para escribir bytes sin formato en el archivo.
  • ByteOutputStream: esta clase es un contenedor sobre OutputStream para admitir capacidades de almacenamiento en búfer. Y nuevamente, como vimos en los flujos de caracteres, este es un método más eficiente que FileOutputStream gracias al almacenamiento en búfer.

Flujos Java NIO

NIO de Java es una API de E/S sin bloqueo que se introdujo en Java 4 y se puede encontrar en el paquete java.nio. En términos de rendimiento, esta es una gran mejora en la API para operaciones de E/S.

Los búferes, los selectores y los canales son los tres componentes principales de Java NIO, aunque en este artículo nos centraremos estrictamente en el uso de las clases NIO para interactuar con archivos, y no necesariamente en los conceptos detrás de la API.

Dado que este tutorial trata sobre la lectura y escritura de archivos, solo analizaremos las clases relacionadas en esta breve sección:

  • Sendero: esta es una estructura jerárquica de una ubicación de archivo real y se usa normalmente para ubicar el archivo con el que desea interactuar.
  • Caminos: Esta es una clase que proporciona varios métodos de utilidad para crear una Ruta a partir de un URI de cadena dado.
  • archivos: esta es otra clase de utilidad que tiene varios métodos para leer y escribir archivos sin bloquear la ejecución en hilos.

Usando estas pocas clases, puede interactuar fácilmente con los archivos de una manera más eficiente.

La diferencia entre Java I/O y NIO {#thedifference betweenjavaioandnio}

La principal diferencia entre estos dos paquetes es que los métodos read() y write() de Java IO son llamadas de bloqueo. Con esto queremos decir que el subproceso que llama a uno de estos métodos se bloqueará hasta que los datos se hayan leído o escrito en el archivo.

Por otro lado, en el caso de NIO, los métodos son de no bloqueo. Esto significa que los subprocesos de llamada pueden realizar otras tareas (como leer/escribir datos de otra fuente o actualizar la interfaz de usuario) mientras que los métodos de “lectura” o “escritura” esperan a que se complete su operación. Esto puede dar lugar a un aumento significativo del rendimiento si se trata de muchas solicitudes de E/S o muchos datos.

Ejemplos de lectura y escritura de archivos de texto {#ejemplos de lectura y escritura de archivos de texto}

En las secciones anteriores, hemos discutido las diferentes API proporcionadas por Java y ahora es el momento de usar estas clases de API en algún código.

El código de ejemplo a continuación maneja la lectura y escritura de archivos de texto utilizando las diversas clases que detallamos anteriormente. Para simplificar las cosas y proporcionar una mejor comparación de los métodos reales que se utilizan, la entrada y la salida seguirán siendo las mismas entre los ejemplos.

Nota: Para evitar cualquier confusión sobre la ruta del archivo, el código de ejemplo leerá y escribirá desde un archivo en el directorio de inicio del usuario. El directorio de inicio del usuario se puede encontrar usando System.getProperty("user.home");, que es lo que usamos en nuestros ejemplos.

Leer y escribir con FileReader y FileWriter {#leer y escribir con filereaderandfilewriter}

Comencemos usando las clases FileReader y FileWriter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
String directory = System.getProperty("user.home");
String fileName = "sample.txt";
String absolutePath = directory + File.separator + fileName;

// Write the content in file 
try(FileWriter fileWriter = new FileWriter(absolutePath)) {
    String fileContent = "This is a sample text.";
    fileWriter.write(fileContent);
    fileWriter.close();
} catch (IOException e) {
    // Cxception handling
}

// Read the content from file
try(FileReader fileReader = new FileReader(absolutePath)) {
    int ch = fileReader.read();
    while(ch != -1) {
        System.out.print((char)ch);
        fileReader.close();
    }
} catch (FileNotFoundException e) {
    // Exception handling
} catch (IOException e) {
    // Exception handling
}

Ambas clases aceptan una Cadena, que representa la ruta al archivo en sus constructores. También puede pasar un objeto File así como un FileDescriptor.

El método write() escribe una secuencia de caracteres válida, ya sea un String, un char[]. Además, puede escribir un único char representado como un int.

El método read() lee y devuelve carácter por carácter, lo que nos permite usar los datos leídos en un bucle while, por ejemplo.

¡No olvide cerrar ambas clases después de usarlas!

Lectura y escritura con BufferedReader y BufferedWriter

Usando las clases BufferedReader y BufferedWriter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
String directory = System.getProperty("user.home");
String fileName = "sample.txt";
String absolutePath = directory + File.separator + fileName;

// Write the content in file 
try(BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(absolutePath))) {
    String fileContent = "This is a sample text.";
    bufferedWriter.write(fileContent);
} catch (IOException e) {
    // Exception handling
}

// Read the content from file
try(BufferedReader bufferedReader = new BufferedReader(new FileReader(absolutePath))) {
    String line = bufferedReader.readLine();
    while(line != null) {
        System.out.println(line);
        line = bufferedReader.readLine();
    }
} catch (FileNotFoundException e) {
    // Exception handling
} catch (IOException e) {
    // Exception handling
}

Lectura y escritura con FileInputStream y FileOutputStream

Usando las clases FileInputStream y FileOutputStream:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
String directory = System.getProperty("user.home");
String fileName = "sample.txt";
String absolutePath = directory + File.separator + fileName;

// write the content in file 
try(FileOutputStream fileOutputStream = new FileOutputStream(absolutePath)) {
    String fileContent = "This is a sample text.";
    fileOutputStream.write(fileContent.getBytes());
} catch (FileNotFoundException e) {
    // exception handling
} catch (IOException e) {
    // exception handling
}

// reading the content of file
try(FileInputStream fileInputStream = new FileInputStream(absolutePath)) {
    int ch = fileInputStream.read();
    while(ch != -1) {
        System.out.print((char)ch);
        ch = fileInputStream.read();
    }
} catch (FileNotFoundException e) {
    // exception handling
} catch (IOException e) {
    // exception handling
}

Lectura y escritura con Buffered InputStream y BufferedOutputStream {#lectura y escritura con Buffered InputStream y Buffered OutputStream}

Usando las clases BufferedInputStream y BufferedOutputStream:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
String directory = System.getProperty("user.home");
String fileName = "sample.txt";
String absolutePath = directory + File.separator + fileName;

// write the content in file 
try(BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(absolutePath))) {
    String fileContent = "This is a sample text.";
    bufferedOutputStream.write(fileContent.getBytes());
} catch (IOException e) {
    // exception handling
}

// read the content from file
try(BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(absolutePath))) {
    int ch = bufferedInputStream.read();
    while(ch != -1) {
        System.out.print((char)ch);
        ch = bufferedInputStream.read();
    }
} catch (FileNotFoundException e) {
    // exception handling
} catch (IOException e) {
    // exception handling
}

Leer y escribir con clases de Java.nio {#leer y escribir con clases de Java.nio}

Usando las clases java.nio:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
String directory = System.getProperty("user.home");
String fileName = "sample.txt";

String content = "This is a sample text.";
Path path = Paths.get(directory, fileName);

try {
    Files.write(path, content.getBytes(), StandardOpenOption.CREATE);
} catch (IOException e) {
    // exception handling
}

try {
    List<String> list = Files.readAllLines(path);
    list.forEach(line -> System.out.println(line));
} catch (IOException e) {
    // exception handling
}

Otra forma de recuperar el contenido a través de la clase Files, que es más importante si no está leyendo datos de texto, es usar el método readAllBytes para leer los datos en una matriz de bytes:

1
2
3
4
5
6
try { 
    byte[] data = Files.readAllBytes(path);
    System.out.println(new String(data));
} catch (IOException e) {
    // exception handling
}

En caso de que esté interesado en usar flujos con java.nio, también puede usar los siguientes métodos provistos por la clase Files, que funcionan igual que los flujos que cubrimos anteriormente en este artículo:

1
2
3
4
Files.newBufferedReader(path)
Files.newBufferedWriter(path, options)
Files.newInputStream(path, options)
Files.newOutputStream(path, options)

Conclusión

En este artículo, hemos cubierto las formas más comunes de leer y escribir datos en un archivo usando el paquete Java I/O y el paquete Java NIO más nuevo. Siempre que sea posible, recomendamos usar las clases Java NIO para operaciones de archivos debido a su API sin bloqueo y, además, el código es un poco más mantenible y legible.

Licensed under CC BY-NC-SA 4.0