Come leggere un file in Java

1. Panoramica

In questo tutorial, esploreremo diversi modi per leggere da un file in Java .

Per prima cosa, vedremo come caricare un file dal classpath, un URL o da un file JAR, utilizzando le classi Java standard.

In secondo luogo, vedremo come leggere il contenuto con BufferedReader , Scanner , StreamTokenizer , DataInputStream , SequenceInputStream e FileChannel . Inoltre, discuteremo come leggere un file codificato UTF-8.

Infine, esploreremo le nuove tecniche per caricare e leggere un file in Java 7 e Java 8.

Questo articolo fa parte della serie "Java - Back to Basic" qui su Baeldung.

2. Configurazione

2.1 File di input

Nella maggior parte degli esempi in questo articolo, leggeremo un file di testo con nome file fileTest.txt che contiene una riga:

Hello, world!

In alcuni esempi, useremo un file diverso. In questi casi, menzioneremo esplicitamente il file e il suo contenuto.

2.2 Metodo di aiuto

Useremo una serie di esempi di test usando solo le classi Java principali e nei test useremo asserzioni usando i matcher di Hamcrest.

I test condivideranno un metodo readFromInputStream comune che trasforma un InputStream in String per una più facile asserzione dei risultati:

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(); }

Nota che ci sono altri modi per ottenere lo stesso risultato. Puoi consultare questo articolo per alcune alternative.

3. Lettura di un file dal Classpath

3.1. Utilizzo di Java standard

Questa sezione spiega come leggere un file disponibile su un classpath. Leggeremo il " fileTest.txt " disponibile in 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)); }

Nello snippet di codice sopra, abbiamo usato la classe corrente per caricare un file usando il metodo getResourceAsStream e passato il percorso assoluto del file da caricare.

Lo stesso metodo è disponibile anche su un'istanza ClassLoader :

ClassLoader classLoader = getClass().getClassLoader(); InputStream inputStream = classLoader.getResourceAsStream("fileTest.txt"); String data = readFromInputStream(inputStream);

Otteniamo il classLoader della classe corrente utilizzando getClass (). GetClassLoader () .

La differenza principale è che quando si utilizza getResourceAsStream su un'istanza ClassLoader , il percorso viene considerato assoluto a partire dalla radice del percorso di classe.

Quando viene utilizzato su un'istanza di Class , il percorso potrebbe essere relativo al pacchetto o un percorso assoluto, suggerito dalla barra iniziale.

Ovviamente, nota che in pratica, i flussi aperti dovrebbero essere sempre chiusi , come InputStream nel nostro esempio:

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. Utilizzo della libreria commons-io

Un'altra opzione comune è usare la classe FileUtils del pacchetto 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()); }

Qui passiamo l' oggetto File al metodo readFileToString () della classe FileUtils . Questa classe di utilità riesce a caricare il contenuto senza la necessità di scrivere alcun codice boilerplate per creare un'istanza InputStream e leggere i dati.

La stessa libreria offre anche gli IOUtilsclasse:

@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()); }

Qui passiamo l' oggetto FileInputStream al metodo toString () della classe IOUtils . Questa classe di utilità riesce a caricare il contenuto senza la necessità di scrivere alcun codice boilerplate per creare un'istanza InputStream e leggere i dati.

4. Lettura con BufferedReader

Concentriamoci ora su diversi modi per analizzare il contenuto di un file.

Inizieremo con un modo semplice per leggere da un file utilizzando 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); }

Nota che readLine () restituirà null quando viene raggiunta la fine del file.

5. Lettura da un file utilizzando Java NIO

In JDK7 il pacchetto NIO è stato aggiornato in modo significativo.

Diamo un'occhiata a un esempio usando la classe Files e il metodo readAllLines . Il metodo readAllLines accetta un Path.

La classe Path può essere considerata come un aggiornamento di java.io.File con alcune operazioni aggiuntive in atto.

5.1. Leggere un piccolo file

Il codice seguente mostra come leggere un file di piccole dimensioni utilizzando la nuova classe Files :

@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.

Quindi puoi usare BufferedReader per leggere riga per riga, Scanner per leggere usando diversi delimitatori, StreamTokenizer per leggere un file in token, DataInputStream per leggere dati binari e tipi di dati primitivi, SequenceInput Stream per collegare più file in un flusso, FileChannel per leggere più velocemente da file di grandi dimensioni, ecc.

Puoi trovare il codice sorgente nel seguente repository GitHub.