Java Flujos (Stream), Archivos (File) e IO

Los flujos (Stream), archivos (File) e IO (entrada/salida) en Java son la infraestructura para manejar la lectura y escritura de datos, y permiten que los programas interactúen con datos externos (como archivos, redes, entrada del sistema, etc.).

El paquete java.io es un paquete central en la biblioteca estándar de Java; proporciona clases para la entrada y salida del sistema, e incluye herramientas para manejar flujos de datos (flujos de bytes y de caracteres), lectura y escritura de archivos, serialización y formato de datos.

java.io es el paquete base para manejar operaciones de archivos, operaciones de flujos y operaciones de IO de bajo nivel.

Los flujos en el paquete java.io admiten muchos formatos, como: tipos primitivos, objetos, conjuntos de caracteres localizados, etc.

Un flujo puede entenderse como una secuencia de datos. Un flujo de entrada representa la lectura de datos desde un origen, y un flujo de salida representa la escritura de datos hacia un destino.


Lectura de entrada de consola

La entrada de consola en Java se realiza mediante System.in.

Para obtener un flujo de caracteres vinculado a la consola, puedes envolver System.in en un objeto BufferedReader para crear un flujo de caracteres.

La siguiente es la sintaxis básica para crear un BufferedReader:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

Una vez creado el objeto BufferedReader, podemos usar el método read() para leer un carácter desde la consola, o el método readLine() para leer una cadena.


Leer entrada de múltiples caracteres desde la consola

Para leer un carácter de un objeto BufferedReader se utiliza el método read(), cuya sintaxis es la siguiente:

int read( ) throws IOException

Cada vez que se llama al método read(), este lee un carácter del flujo de entrada y lo devuelve como un valor entero. Cuando el flujo llega a su fin, devuelve -1. Este método lanza IOException.

El siguiente programa demuestra el uso del método read() para leer caracteres desde la consola hasta que el usuario ingreseq。

Código del archivo BRRead.java:

//Usar BufferedReader para leer caracteres en la consola import java.io.*; public class BRRead { public static void main(String[] args) throws IOException { char c; //Usar System.in para crear un BufferedReader BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("Ingrese caracteres, presione la tecla 'q' para salir."); //Leer caracteres do { c = (char) br.read(); System.out.println(c); } while (c != 'q'); } }

El resultado de compilar y ejecutar el ejemplo anterior es el siguiente:

输入字符, 按下 'q' 键退出。
ejemplo
r
u
n
o
o
b


q
q

Leer cadenas desde la consola

Para leer una cadena desde la entrada estándar se necesita usar el método readLine() de BufferedReader.

Su formato general es:

String readLine( ) throws IOException

El siguiente programa lee y muestra líneas de caracteres hasta que ingreses la palabra "end".

Código del archivo BRReadLines.java:

//Usar BufferedReader para leer caracteres en la consola import java.io.*; public class BRReadLines { public static void main(String[] args) throws IOException { //Usar System.in para crear un BufferedReader BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str; System.out.println("Enter lines of text."); System.out.println("Enter 'end' to quit."); do { str = br.readLine(); System.out.println(str); } while (!str.equals("end")); } }

El resultado de compilar y ejecutar el ejemplo anterior es el siguiente:

Enter lines of text.
Enter 'end' to quit.
This is line one
This is line one
This is line two
This is line two
end
end

En versiones posteriores a JDK 5 también podemos usarJava Scannerla clase para obtener la entrada de consola.


Salida de consola

Como se ha explicado anteriormente, la salida de consola se realiza con print() y println(). Estos métodos están definidos por la clase PrintStream, y System.out es una referencia a un objeto de esa clase.

PrintStream hereda de la clase OutputStream e implementa el método write(). De esta manera, write() también se puede usar para escribir en la consola.

El formato más simple de write() definido por PrintStream se muestra a continuación:

void write(int byteval)

Este método escribe el byte de orden inferior de byteval en el flujo.

Ejemplo

El siguiente ejemplo usa write() para enviar a la pantalla el carácter 'A' y el salto de línea que le sigue:

Código del archivo WriteDemo.java:

import java.io.*; //Demuestra System.out.write(). public class WriteDemo { public static void main(String[] args) { int b; b = 'A'; System.out.write(b); System.out.write('\n'); } }

Al ejecutar el ejemplo anterior, se muestra el carácter 'A' en la ventana de salida.

A

Nota:El método write() no se usa con frecuencia, porque los métodos print() y println() son más convenientes de usar.


Leer y escribir archivos

Como se mencionó anteriormente, un flujo se define como una secuencia de datos. El flujo de entrada se usa para leer datos de un origen, y el flujo de salida para escribir datos en un destino.

La siguiente imagen es un diagrama de jerarquía de clases que describe los flujos de entrada y salida.

Flujos de bytes (procesamiento de datos binarios)

Los flujos de bytes se utilizan para procesar datos binarios, como archivos, imágenes, videos, etc.

Nombre de claseTipoDescripción
InputStreamClase abstracta (flujo de entrada)Superclase de todos los flujos de entrada de bytes; maneja operaciones de entrada de bytes.
OutputStreamClase abstracta (flujo de salida)Superclase de todos los flujos de salida de bytes; maneja operaciones de salida de bytes.
FileInputStreamFlujo de entradaLee datos de bytes de archivos.
FileOutputStreamFlujo de salidaEscribe datos de bytes en archivos.
BufferedInputStreamFlujo de entradaProporciona funcionalidad de buffer para los flujos de entrada de bytes, mejorando la eficiencia de lectura.
BufferedOutputStreamFlujo de salidaProporciona funcionalidad de buffer para los flujos de salida de bytes, mejorando la eficiencia de escritura.
ByteArrayInputStreamFlujo de entradaUtiliza un arreglo de bytes en memoria como origen de entrada.
ByteArrayOutputStreamFlujo de salidaEscribe datos en un arreglo de bytes en memoria.
DataInputStreamFlujo de entradaPermite leer tipos de datos primitivos de Java desde el flujo de entrada (comoint、float、boolean)。
DataOutputStreamFlujo de salidaPermite escribir tipos de datos primitivos de Java en el flujo de salida.
ObjectInputStreamFlujo de entradaLee objetos serializados desde el flujo de entrada.
ObjectOutputStreamFlujo de salidaSerializa objetos y los escribe en el flujo de salida.
PipedInputStreamFlujo de entradaSe utiliza para leer datos de bytes en una tubería, normalmentePipedOutputStreamse usa en conjunto.
PipedOutputStreamFlujo de salidaSe utiliza para escribir datos de bytes en una tubería, normalmentePipedInputStreamse usa en conjunto.
FilterInputStreamFlujo de entradaClase envoltorio de los flujos de entrada de bytes, utilizada para filtrar otros flujos de entrada.
FilterOutputStreamFlujo de salidaClase envoltorio de los flujos de salida de bytes, utilizada para filtrar otros flujos de salida.
SequenceInputStreamFlujo de entradaConcatena varios flujos de entrada en uno solo para su procesamiento.

Flujos de caracteres (procesamiento de datos de texto)

Los flujos de caracteres se utilizan para procesar datos de texto, como leer y escribir cadenas o archivos.

Nombre de claseTipoDescripción
ReaderClase abstracta (flujo de entrada)Superclase de todos los flujos de entrada de caracteres; maneja operaciones de entrada de caracteres.
WriterClase abstracta (flujo de salida)Superclase de todos los flujos de salida de caracteres; maneja operaciones de salida de caracteres.
FileReaderFlujo de entradaLee datos de caracteres de archivos.
FileWriterFlujo de salidaEscribe datos de caracteres en archivos.
BufferedReaderFlujo de entradaProporciona funcionalidad de buffer para los flujos de entrada de caracteres, admite lectura por líneas y mejora la eficiencia de lectura.
BufferedWriterFlujo de salidaProporciona funcionalidad de buffer para los flujos de salida de caracteres, admite escritura por líneas y mejora la eficiencia de escritura.
CharArrayReaderFlujo de entradaUtiliza un arreglo de caracteres como origen de entrada.
CharArrayWriterFlujo de salidaEscribe datos en un arreglo de caracteres.
StringReaderFlujo de entradaUtiliza una cadena como origen de entrada.
StringWriterFlujo de salidaEscribe datos en un búfer de cadena.
PrintWriterFlujo de salidaFlujo de salida de caracteres conveniente, compatible con actualización automática y salida formateada.
PipedReaderFlujo de entradaSe utiliza para leer datos de caracteres en una tubería, generalmente conPipedWriterusarse en combinación.
PipedWriterFlujo de salidaSe utiliza para escribir datos de caracteres en una tubería, generalmente conPipedReaderusarse en combinación.
LineNumberReaderFlujo de entradaFlujo de entrada de caracteres con búfer y números de línea, que permite realizar un seguimiento del número de línea leído.
PushbackReaderFlujo de entradaPermite devolver caracteres al flujo después de leerlos, para poder leerlos nuevamente.

Clases auxiliares (otras clases importantes)

Las clases auxiliares brindan soporte para archivos, directorios y acceso a archivos aleatorios.

Nombre de claseTipoDescripción
FileOperaciones de archivos y directoriosSe utiliza para representar un archivo o directorio, y proporciona operaciones de archivo como crear, eliminar, renombrar, etc.
RandomAccessFileArchivo de acceso aleatorioAdmite acceso aleatorio a archivos; se pueden leer y escribir datos desde cualquier posición del archivo.
ConsoleEntrada y salida de consolaProporciona soporte de entrada y salida para la consola del sistema.

Los dos flujos importantes que se discutirán a continuación sonFileInputStreamyFileOutputStream。


FileInputStream

Este flujo se utiliza para leer datos de un archivo; su objeto se puede crear con la palabra clave new.

Hay varios métodos de construcción que se pueden usar para crear objetos.

Se puede usar un nombre de archivo de tipo cadena para crear un objeto de flujo de entrada y leer el archivo:

InputStream f = new FileInputStream("C:/java/hello");

También se puede usar un objeto File para crear un objeto de flujo de entrada y leer el archivo. Primero debemos usar el método File() para crear un objeto File:

File f = new File("C:/java/hello"); InputStream in = new FileInputStream(f);

Una vez creado el objeto InputStream, se pueden usar los siguientes métodos para leer el flujo o realizar otras operaciones de flujo.

MétodoDescripciónCódigo de ejemplo
int read()Lee un byte de datos y devuelve un entero entre 0 y 255. Si se llega al final del flujo, devuelve -1.int data = inputStream.read();
int read(byte[] b)Lee bytes del flujo de entrada y los almacena en la matriz de bytesby devuelve el número real de bytes leídos. Si se llega al final del flujo, devuelve -1.byte[] buffer = new byte[1024]; int bytesRead = inputStream.read(buffer);
int read(byte[] b, int off, int len)Lee como máximolenbytes del flujo de entrada y los almacena en la matriz de bytesbdeoffen la posición de desplazamiento, y devuelve el número real de bytes leídos. Si se llega al final del flujo, devuelve -1.byte[] buffer = new byte[1024]; int bytesRead = inputStream.read(buffer, 0, buffer.length);
long skip(long n)Omite y descartanbytes del flujo de entrada y devuelve el número real de bytes omitidos.long skippedBytes = inputStream.skip(100);
int available()Devuelve el número de bytes que se pueden leer (sin bloquear).int availableBytes = inputStream.available();
void close()Cierra el flujo de entrada y libera todos los recursos asociados con él.inputStream.close();
void mark(int readlimit)Establece una marca en la posición actual del flujo;readlimites el límite superior de bytes que se pueden leer.inputStream.mark(1024);
void reset()Reposiciona el flujo en la última posición marcada; si no hay marca o esta no es válida, lanzaIOException。inputStream.reset();
boolean markSupported()Comprueba si el flujo de entrada actual admitemark()yreset()la operación.boolean isMarkSupported = inputStream.markSupported();

Además de InputStream, hay otros flujos de entrada. Para más detalles, consulte el siguiente enlace:


FileOutputStream

Esta clase se utiliza para crear un archivo y escribir datos en él.

Si el archivo de destino no existe antes de que el flujo abra el archivo para salida, el flujo creará el archivo.

Hay dos métodos de construcción que se pueden usar para crear objetos FileOutputStream.

Use un nombre de archivo de tipo cadena para crear un objeto de flujo de salida:

OutputStream f = new FileOutputStream("C:/java/hello")

También se puede usar un objeto File para crear un flujo de salida y escribir en el archivo. Primero debemos usar el método File() para crear un objeto File:

File f = new File("C:/java/hello"); OutputStream fOut = new FileOutputStream(f);

Una vez creado el objeto OutputStream, se pueden usar los siguientes métodos para escribir en el flujo o realizar otras operaciones de flujo.

MétodoDescripciónCódigo de ejemplo
void write(int b)Escribe el byte especificado en el flujo de salida;bsus 8 bits más bajos se escribirán en el flujo.outputStream.write(255);
void write(byte[] b)Escribe todos los bytes de la matriz de bytesben el flujo de salida.byte[] data = "Hello".getBytes(); outputStream.write(data);
void write(byte[] b, int off, int len)De la matriz de bytes,bdesde el desplazamientooffespecificado,lenescribe bytes en el flujo de salida.byte[] data = "Hello".getBytes(); outputStream.write(data, 0, data.length);
void flush()Vacía el flujo de salida y fuerza la escritura de todos los datos almacenados en el búfer, asegurando que los datos se escriban inmediatamente en el destino de salida.outputStream.flush();
void close()Cierra el flujo de salida y libera todos los recursos asociados con él. Después de cerrarlo, no se puede escribir.outputStream.close();

Además de OutputStream, hay otros flujos de salida. Para más detalles, consulte el siguiente enlace:

Ejemplo

A continuación se muestra un ejemplo que demuestra el uso de InputStream y OutputStream:

Código del archivo fileStreamTest.java:

import java.io.*; public class fileStreamTest { public static void main(String[] args) { try { byte bWrite[] = { 11, 21, 3, 40, 5 }; OutputStream os = new FileOutputStream("test.txt"); for (int x = 0; x < bWrite.length; x++) { os.write(bWrite[x]); // writes the bytes } os.close(); InputStream is = new FileInputStream("test.txt"); int size = is.available(); for (int i = 0; i < size; i++) { System.out.print((char) is.read() + " "); } is.close(); } catch (IOException e) { System.out.print("Exception"); } } }

El programa anterior primero crea el archivo test.txt, escribe los números dados en el archivo en forma binaria y, al mismo tiempo, los muestra en la consola.

El código anterior, al ser una escritura binaria, puede contener caracteres ilegibles. Puede usar el siguiente ejemplo de código para resolver el problema de los caracteres ilegibles:

Código del archivo fileStreamTest2.java:

//Nombre de archivo: fileStreamTest2.java import java.io.*; public class fileStreamTest2 { public static void main(String[] args) throws IOException { File f = new File("a.txt"); FileOutputStream fop = new FileOutputStream(f); //Construir objeto FileOutputStream; si el archivo no existe, se creará automáticamente. OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8"); //Construir objeto OutputStreamWriter; el parámetro puede especificar la codificación. Por defecto, se usa la codificación predeterminada del sistema operativo; en Windows es gbk. writer.append("Entrada en chino"); //Escribir en el búfer writer.append("\r\n"); //Salto de línea writer.append("English"); //Vaciar el búfer y escribir en el archivo. Si no hay más contenido para escribir a continuación, llamar directamente a close también escribirá. writer.close(); //Cerrar el flujo de escritura; al mismo tiempo, el contenido del búfer se escribirá en el archivo, por lo que la línea anterior está comentada. fop.close(); //Cerrar el flujo de salida y liberar los recursos del sistema. FileInputStream fip = new FileInputStream(f); //Construir objeto FileInputStream InputStreamReader reader = new InputStreamReader(fip, "UTF-8"); //Construir objeto InputStreamReader; la codificación es la misma que la de escritura. StringBuffer sb = new StringBuffer(); while (reader.ready()) { sb.append((char) reader.read()); //Convertir a char y agregar al objeto StringBuffer. } System.out.println(sb.toString()); reader.close(); //Cerrar el flujo de lectura fip.close(); //Cerrar el flujo de entrada y liberar los recursos del sistema. } }

Archivos e I/O

También hay algunas clases sobre archivos y E/S que debemos conocer:


Directorios en Java

Crear directorios:

En la clase File hay dos métodos que se pueden usar para crear carpetas:

  • mkdir( )El método crea una carpeta; si tiene éxito, devuelve true; si falla, devuelve false. El fallo indica que la ruta especificada por el objeto File ya existe, o que la carpeta no se puede crear porque la ruta completa aún no existe.
  • mkdirs()El método crea una carpeta y todas sus carpetas padre.

El siguiente ejemplo crea la carpeta "/tmp/user/java/bin":

Código del archivo CreateDir.java:

import java.io.File; public class CreateDir { public static void main(String[] args) { String dirname = "/tmp/user/java/bin"; File d = new File(dirname); //Ahora creando el directorio d.mkdirs(); } }

Compile y ejecute el código anterior para crear el directorio "/tmp/user/java/bin".

Nota:Java distingue automáticamente los separadores de ruta de archivo según la convención en UNIX y Windows. Si usa el separador (/) en la versión de Java para Windows, la ruta aún se puede analizar correctamente.


Leer directorios

Un directorio es en realidad un objeto File que contiene otros archivos y carpetas.

Si se crea un objeto File y este es un directorio, al llamar al método isDirectory() devolverá true.

Se puede extraer la lista de archivos y carpetas que contiene llamando al método list() de ese objeto.

El siguiente ejemplo muestra cómo usar el método list() para verificar el contenido de una carpeta:

Código del archivo DirList.java:

import java.io.File; public class DirList { public static void main(String args[]) { String dirname = "/tmp"; File f1 = new File(dirname); if (f1.isDirectory()) { System.out.println("Directorio" + dirname); String s[] = f1.list(); for (int i = 0; i < s.length; i++) { File f = new File(dirname + "/" + s[i]); if (f.isDirectory()) { System.out.println(s[i] + "es un directorio"); } else { System.out.println(s[i] + "es un archivo"); } } } else { System.out.println(dirname + "no es un directorio"); } } }

El resultado de compilar y ejecutar el ejemplo anterior es el siguiente:

目录 /tmp
bin 是一个目录
lib 是一个目录
demo 是一个目录
test.txt 是一个文件
README 是一个文件
index.html 是一个文件
include 是一个目录

Eliminar directorios o archivos

Para eliminar archivos se puede usarjava.io.File.delete()el método.

El siguiente código eliminará el directorio/tmp/java/. Tenga en cuenta que al eliminar un directorio, debe asegurarse de que no haya otros archivos en ese directorio para poder eliminarlo correctamente; de lo contrario, la eliminación fallará.

Estructura del directorio de prueba:

/tmp/java/
|-- 1.log
|-- test

Código del archivo DeleteFileDemo.java:

import java.io.File; public class DeleteFileDemo { public static void main(String[] args) { //Aquí cámbielo a su propio directorio de prueba File folder = new File("/tmp/java/"); deleteFolder(folder); } //Eliminar archivos y directorios public static void deleteFolder(File folder) { File[] files = folder.listFiles(); if (files != null) { for (File f : files) { if (f.isDirectory()) { deleteFolder(f); } else { f.delete(); } } } folder.delete(); } }
Otras extensiones