Cómo leer un archivo en una cadena Java: análisis de una pregunta popular de Stack Overflow
\n\nSi alguna vez te has preguntado «How do I create a Java string from the contents of a file?» — no estás solo. Esta pregunta ha acumulado miles de visitas en Stack Overflow, y con razón. Una operación aparentemente trivial — leer un archivo de texto y colocar su contenido en una cadena — esconde numerosas trampas: desde fugas de memoria hasta problemas de codificación.
\n\nEn este artículo no solo daremos recetas ya preparadas. Analizaremos el funcionamiento interno de cada método: cómo funciona BufferedReader, por qué Files.readString() apareció solo en Java 11, y en qué se diferencia Scanner de InputStream. ¡Allá vamos!
¿Por qué es más complejo de lo que parece?
\n\nA primera vista, la tarea es trivial: hay un archivo, hay una cadena. Pero Java es un lenguaje con tipado estricto y gestión de memoria. Para leer un archivo, se necesita:
\n\n- \n
- Abrir un flujo de datos (InputStream, Reader) \n
- Bufferizar la lectura (para no acceder al disco por cada byte) \n
- Recolectar los bytes/caracteres en un búfer único \n
- Convertir a cadena teniendo en cuenta la codificación \n
- Cerrar los recursos (para evitar fugas de descriptores de archivo) \n
Cada uno de estos pasos es un punto potencial de fallo. Analicemos los enfoques más populares.
\n\nMétodo 1: El clásico — BufferedReader (Java 1.1+)
\n\nEl método más antiguo y probado. Utiliza BufferedReader para la lectura línea por línea. Internamente, un búfer (por defecto de 8 KB) acumula datos, reduciendo la cantidad de llamadas al sistema read().
public static String readFileBufferedReader(String filePath) throws IOException {\n StringBuilder content = new StringBuilder();\n try (BufferedReader reader = new BufferedReader(new FileReader(filePath, StandardCharsets.UTF_8))) {\n String line;\n while ((line = reader.readLine()) != null) {\n content.append(line).append(System.lineSeparator());\n }\n }\n return content.toString();\n}\n\n¿Cómo funciona internamente?
\nFileReader abre el archivo y crea un FileInputStream. BufferedReader lo envuelve, añadiendo un búfer. Al llamar a readLine(), se lee un bloque de datos (por ejemplo, 8 KB) y la línea se ensambla desde el búfer. Si la línea es más larga que el búfer, este se relee. try-with-resources garantiza el cierre del flujo incluso si ocurre una excepción.
Ventajas: Funciona en cualquier versión de Java, lectura línea por línea controlada.
\nDesventajas: Añade un separador de línea extra al final, no es eficiente para archivos enormes (cadena en memoria).
Método 2: Scanner — cómodo pero lento (Java 5+)
\n\nMuchos aman Scanner por su simplicidad. Puede dividir la entrada en tokens, pero para leer un archivo completo se usa así:
public static String readFileScanner(String filePath) throws IOException {\n try (Scanner scanner = new Scanner(new File(filePath), StandardCharsets.UTF_8.name())) {\n scanner.useDelimiter("\\\\A"); // Inicio del archivo como delimitador\n return scanner.hasNext() ? scanner.next() : "";\n }\n}\n\nLa magia de \\\\A: Es una expresión regular que denota el inicio de los datos de entrada. Al establecerlo como delimitador, obligamos a Scanner a leer todo el contenido de una sola vez. Internamente, Scanner utiliza Pattern para buscar el delimitador, lo que añade una sobrecarga por las expresiones regulares.
Ventajas: Código muy conciso.
\nDesventajas: Más lento que BufferedReader debido al trabajo con expresiones regulares, no es adecuado para archivos binarios.
Método 3: Files.readAllLines() — la comodidad de Java 8 (NIO)
\n\nA partir de Java 8, el paquete java.nio.file ofreció una elegante