1. Información general
En este tutorial, exploraremos diferentes formas de leer desde un archivo en Java .
Primero, veremos cómo cargar un archivo desde classpath, una URL o desde un archivo JAR, usando clases estándar de Java.
En segundo lugar, veremos cómo leer el contenido con BufferedReader , Scanner , StreamTokenizer , DataInputStream , SequenceInputStream y FileChannel . Además, analizaremos cómo leer un archivo codificado en UTF-8.
Finalmente, exploraremos las nuevas técnicas para cargar y leer un archivo en Java 7 y Java 8.
Este artículo es parte de la serie "Java - Back to Basic" aquí en Baeldung.
2. Configuración
2.1 Archivo de entrada
En la mayoría de los ejemplos de este artículo, leeremos un archivo de texto con el nombre de archivo fileTest.txt que contiene una línea:
Hello, world!
En algunos ejemplos, usaremos un archivo diferente. En estos casos, mencionaremos el archivo y su contenido explícitamente.
2.2 Método de ayuda
Usaremos un conjunto de ejemplos de prueba usando solo clases centrales de Java, y en las pruebas usaremos aserciones usando comparadores Hamcrest.
Las pruebas compartirán un método readFromInputStream común que transforma un InputStream en String para facilitar la afirmación de los resultados:
private String readFromInputStream(InputStream inputStream) throws IOException { StringBuilder resultStringBuilder = new StringBuilder(); try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) { String line; while ((line = br.readLine()) != null) { resultStringBuilder.append(line).append("\n"); } } return resultStringBuilder.toString(); }
Tenga en cuenta que existen otras formas de lograr el mismo resultado. Puede consultar este artículo para conocer algunas alternativas.
3. Leer un archivo desde Classpath
3.1. Usando Java estándar
Esta sección explica cómo leer un archivo que está disponible en una ruta de clases. Leeremos el " fileTest.txt " disponible en src / main / resources :
@Test public void givenFileNameAsAbsolutePath_whenUsingClasspath_thenFileData() { String expectedData = "Hello, world!"; Class clazz = FileOperationsTest.class; InputStream inputStream = clazz.getResourceAsStream("/fileTest.txt"); String data = readFromInputStream(inputStream); Assert.assertThat(data, containsString(expectedData)); }
En el fragmento de código anterior, usamos la clase actual para cargar un archivo usando el método getResourceAsStream y pasamos la ruta absoluta del archivo a cargar.
El mismo método también está disponible en una instancia de ClassLoader :
ClassLoader classLoader = getClass().getClassLoader(); InputStream inputStream = classLoader.getResourceAsStream("fileTest.txt"); String data = readFromInputStream(inputStream);
Obtenemos el classLoader de la clase actual usando getClass (). GetClassLoader () .
La principal diferencia es que cuando se usa getResourceAsStream en una instancia de ClassLoader , la ruta se trata como absoluta comenzando desde la raíz de la ruta de clases.
Cuando se usa contra una instancia de Class , la ruta puede ser relativa al paquete, o una ruta absoluta, que está indicada por la barra inclinada.
Por supuesto, tenga en cuenta que, en la práctica, los flujos abiertos siempre deben estar cerrados , como InputStream en nuestro ejemplo:
InputStream inputStream = null; try { File file = new File(classLoader.getResource("fileTest.txt").getFile()); inputStream = new FileInputStream(file); //... } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
3.2. Uso de la biblioteca commons-io
Otra opción común es usar la clase FileUtils del paquete commons-io :
@Test public void givenFileName_whenUsingFileUtils_thenFileData() { String expectedData = "Hello, world!"; ClassLoader classLoader = getClass().getClassLoader(); File file = new File(classLoader.getResource("fileTest.txt").getFile()); String data = FileUtils.readFileToString(file, "UTF-8"); assertEquals(expectedData, data.trim()); }
Aquí pasamos el objeto File al método readFileToString () de la clase FileUtils . Esta clase de utilidad logra cargar el contenido sin la necesidad de escribir ningún código repetitivo para crear una instancia de InputStream y leer datos.
La misma biblioteca también ofrece IOUtilsclase:
@Test public void givenFileName_whenUsingIOUtils_thenFileData() { String expectedData = "Hello, world!"; FileInputStream fis = new FileInputStream("src/test/resources/fileTest.txt"); String data = IOUtils.toString(fis, "UTF-8"); assertEquals(expectedData, data.trim()); }
Aquí pasamos el objeto FileInputStream al método toString () de la clase IOUtils . Esta clase de utilidad logra cargar el contenido sin la necesidad de escribir ningún código repetitivo para crear una instancia InputStream y leer datos.
4. Leer con BufferedReader
Centrémonos ahora en diferentes formas de analizar el contenido de un archivo.
Comenzaremos con una forma sencilla de leer un archivo usando BufferedReader:
@Test public void whenReadWithBufferedReader_thenCorrect() throws IOException { String expected_value = "Hello, world!"; String file; BufferedReader reader = new BufferedReader(new FileReader(file)); String currentLine = reader.readLine(); reader.close(); assertEquals(expected_value, currentLine); }
Tenga en cuenta que readLine () devolverá un valor nulo cuando se alcance el final del archivo.
5. Lectura de un archivo con Java NIO
En JDK7, el paquete NIO se actualizó significativamente.
Veamos un ejemplo usando la clase Files y el método readAllLines . El método readAllLines acepta una ruta.
La clase de ruta se puede considerar como una actualización del archivo java.io.File con algunas operaciones adicionales implementadas.
5.1. Leer un archivo pequeño
El siguiente código muestra cómo leer un archivo pequeño usando la nueva clase Archivos :
@Test public void whenReadSmallFileJava7_thenCorrect() throws IOException { String expected_value = "Hello, world!"; Path path = Paths.get("src/test/resources/fileTest.txt"); String read = Files.readAllLines(path).get(0); assertEquals(expected_value, read); }
Note that you can use the readAllBytes() method as well if you need binary data.
5.2. Reading a Large File
If we want to read a large file with Files class, we can use the BufferedReader:
The following code reads the file using the new Files class and BufferedReader:
@Test public void whenReadLargeFileJava7_thenCorrect() throws IOException { String expected_value = "Hello, world!"; Path path = Paths.get("src/test/resources/fileTest.txt"); BufferedReader reader = Files.newBufferedReader(path); String line = reader.readLine(); assertEquals(expected_value, line); }
5.3. Reading a File Using Files.lines()
JDK8 offers the lines() method inside the Files class. It returns a Stream of String elements.
Let’s look at an example of how to read data into bytes and decode using UTF-8 charset.
The following code reads the file using the new Files.lines():
@Test public void givenFilePath_whenUsingFilesLines_thenFileData() { String expectedData = "Hello, world!"; Path path = Paths.get(getClass().getClassLoader() .getResource("fileTest.txt").toURI()); Stream lines = Files.lines(path); String data = lines.collect(Collectors.joining("\n")); lines.close(); Assert.assertEquals(expectedData, data.trim()); }
Using Stream with IO channels like file operations, we need to close the stream explicitly using the close() method.
As we can see, the Files API offers another easy way to read the file contents into a String.
In the next sections, let's have a look at other, less common methods of reading a file, that may be appropriate in some situations.
6. Reading with Scanner
Next, let's use a Scanner to read from the File. Here, we'll use whitespace as the delimiter:
@Test public void whenReadWithScanner_thenCorrect() throws IOException { String file = "src/test/resources/fileTest.txt"; Scanner scanner = new Scanner(new File(file)); scanner.useDelimiter(" "); assertTrue(scanner.hasNext()); assertEquals("Hello,", scanner.next()); assertEquals("world!", scanner.next()); scanner.close(); }
Note that the default delimiter is the whitespace, but multiple delimiters can be used with a Scanner.
The Scanner class is useful when reading content from the console, or when the content contains primitive values, with a known delimiter (eg: a list of integers separated by space).
7. Reading with StreamTokenizer
Next, let's read a text file into tokens using a StreamTokenizer.
The way the tokenizer works is – first, we need to figure out what the next token is – String or number; we do that by looking at the tokenizer.ttype field.
Then, we'll read the actual token based on this type:
- tokenizer.nval – if the type was a number
- tokenizer.sval – if the type was a String
In this example we'll use a different input file which simply contains:
Hello 1
The following code reads from the file both the String and the number:
@Test public void whenReadWithStreamTokenizer_thenCorrectTokens() throws IOException { String file = "src/test/resources/fileTestTokenizer.txt"; FileReader reader = new FileReader(file); StreamTokenizer tokenizer = new StreamTokenizer(reader); // token 1 tokenizer.nextToken(); assertEquals(StreamTokenizer.TT_WORD, tokenizer.ttype); assertEquals("Hello", tokenizer.sval); // token 2 tokenizer.nextToken(); assertEquals(StreamTokenizer.TT_NUMBER, tokenizer.ttype); assertEquals(1, tokenizer.nval, 0.0000001); // token 3 tokenizer.nextToken(); assertEquals(StreamTokenizer.TT_EOF, tokenizer.ttype); reader.close(); }
Note how the end of file token is used at the end.
This approach is useful for parsing an input stream into tokens.
8. Reading with DataInputStream
We can use DataInputStream to read binary or primitive data type from a file.
The following test reads the file using a DataInputStream:
@Test public void whenReadWithDataInputStream_thenCorrect() throws IOException { String expectedValue = "Hello, world!"; String file; String result = null; DataInputStream reader = new DataInputStream(new FileInputStream(file)); int nBytesToRead = reader.available(); if(nBytesToRead > 0) { byte[] bytes = new byte[nBytesToRead]; reader.read(bytes); result = new String(bytes); } assertEquals(expectedValue, result); }
9. Reading with FileChannel
If we are reading a large file, FileChannel can be faster than standard IO.
The following code reads data bytes from the file using FileChannel and RandomAccessFile:
@Test public void whenReadWithFileChannel_thenCorrect() throws IOException { String expected_value = "Hello, world!"; String file = "src/test/resources/fileTest.txt"; RandomAccessFile reader = new RandomAccessFile(file, "r"); FileChannel channel = reader.getChannel(); int bufferSize = 1024; if (bufferSize > channel.size()) { bufferSize = (int) channel.size(); } ByteBuffer buff = ByteBuffer.allocate(bufferSize); channel.read(buff); buff.flip(); assertEquals(expected_value, new String(buff.array())); channel.close(); reader.close(); }
10. Reading a UTF-8 Encoded File
Now, let's see how to read a UTF-8 encoded file using BufferedReader. In this example, we'll read a file that contains Chinese characters:
@Test public void whenReadUTFEncodedFile_thenCorrect() throws IOException { String expected_value = "青空"; String file = "src/test/resources/fileTestUtf8.txt"; BufferedReader reader = new BufferedReader (new InputStreamReader(new FileInputStream(file), "UTF-8")); String currentLine = reader.readLine(); reader.close(); assertEquals(expected_value, currentLine); }
11. Reading Content from URL
To read content from a URL, we will use “/” URL in our example as:
@Test public void givenURLName_whenUsingURL_thenFileData() { String expectedData = "Baeldung"; URL urlObject = new URL("/"); URLConnection urlConnection = urlObject.openConnection(); InputStream inputStream = urlConnection.getInputStream(); String data = readFromInputStream(inputStream); Assert.assertThat(data, containsString(expectedData)); }
There are also alternative ways of connecting to a URL. Here we used the URL and URLConnection class available in the standard SDK.
12. Reading a File from a JAR
To read a file which is located inside a JAR file, we will need a JAR with a file inside it. For our example we will read “LICENSE.txt” from the “hamcrest-library-1.3.jar” file:
@Test public void givenFileName_whenUsingJarFile_thenFileData() { String expectedData = "BSD License"; Class clazz = Matchers.class; InputStream inputStream = clazz.getResourceAsStream("/LICENSE.txt"); String data = readFromInputStream(inputStream); Assert.assertThat(data, containsString(expectedData)); }
Here we want to load LICENSE.txt that resides in Hamcrest library, so we will use the Matcher's class that helps to get a resource. The same file can be loaded using the classloader too.
13. Conclusion
As you can see, there are many possibilities for loading a file and reading data from it using plain Java.
You can load a file from various locations like classpath, URL or jar files.
Luego, puede usar BufferedReader para leer línea por línea, Scanner para leer usando diferentes delimitadores, StreamTokenizer para leer un archivo en tokens, DataInputStream para leer datos binarios y tipos de datos primitivos, SequenceInput Stream para vincular varios archivos en un flujo, FileChannel para leer más rápido desde archivos grandes, etc.
Puede encontrar el código fuente en el siguiente repositorio de GitHub.