Cum se citește un fișier în Java

1. Prezentare generală

În acest tutorial, vom explora diferite moduri de a citi dintr-un fișier în Java .

În primul rând, vom vedea cum să încărcați un fișier din classpath, un URL sau dintr-un fișier JAR, utilizând clase Java standard.

În al doilea rând, vom vedea cum să citiți conținutul cu BufferedReader , Scanner , StreamTokenizer , DataInputStream , SequenceInputStream și FileChannel . De asemenea, vom discuta despre modul de citire a unui fișier codat UTF-8.

În cele din urmă, vom explora noile tehnici de încărcare și citire a unui fișier în Java 7 și Java 8.

Acest articol face parte din seria „Java - Înapoi la bază” aici de pe Baeldung.

2. Configurare

2.1 Fișier de intrare

În cele mai multe exemple din acest articol, vom citi un fișier text cu numele de fișier fileTest.txt care conține un rând:

Hello, world!

În câteva exemple, vom folosi un fișier diferit. În aceste cazuri, vom menționa în mod explicit fișierul și conținutul acestuia.

2.2 Metoda de ajutor

Vom folosi un set de exemple de testare folosind doar clase Java de bază, iar în teste vom folosi afirmații folosind potrivitori Hamcrest.

Testele vor partaja o metodă comună readFromInputStream care transformă un InputStream în șir pentru afirmarea mai ușoară a rezultatelor:

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

Rețineți că există și alte modalități de a obține același rezultat. Puteți consulta acest articol pentru câteva alternative.

3. Citirea unui fișier din Classpath

3.1. Folosind Java standard

Această secțiune explică modul de citire a unui fișier care este disponibil pe o cale de clasă. Vom citi „ fileTest.txt ” disponibil sub 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)); }

În fragmentul de cod de mai sus, am folosit clasa curentă pentru a încărca un fișier folosind metoda getResourceAsStream și am trecut calea absolută a fișierului de încărcat.

Aceeași metodă este disponibilă și pentru o instanță ClassLoader :

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

Obținem classLoader al clasei curente folosind getClass (). GetClassLoader () .

Principala diferență este că, atunci când se utilizează getResourceAsStream pe o instanță ClassLoader , calea este tratată ca absolută pornind de la rădăcina clasei.

Atunci când este utilizat împotriva unei instanțe de clasă , calea ar putea fi relativă la pachet sau o cale absolută, care este sugerată de slash-ul principal.

Desigur, rețineți că, în practică, fluxurile deschise ar trebui întotdeauna închise , cum ar fi InputStream în exemplul nostru:

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. Utilizarea Bibliotecii commons-io

O altă opțiune comună este utilizarea clasei FileUtils a pachetului 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()); }

Aici trecem obiectul File la metoda readFileToString () a clasei FileUtils . Această clasă de utilități reușește să încarce conținutul fără a fi necesar să scrieți un cod boilerplate pentru a crea o instanță InputStream și a citi date.

Aceeași bibliotecă oferă și IOUtilsclasă:

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

Aici trecem obiectul FileInputStream la metoda toString () a clasei IOUtils . Această clasă de utilități reușește să încarce conținutul fără a fi necesar să scrieți un cod boilerplate pentru a crea o instanță InputStream și a citi date.

4. Citirea cu BufferedReader

Să ne concentrăm acum asupra diferitelor moduri de a analiza conținutul unui fișier.

We'll start with a simple way to read from a file using 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); }

Note that readLine() will return null when the end of the file is reached.

5. Reading from a File Using Java NIO

In JDK7 the NIO package was significantly updated.

Let’s look at an example using the Files class and the readAllLines method. The readAllLines method accepts a Path.

Path class can be considered as an upgrade of the java.io.File with some additional operations in place.

5.1. Reading a Small File

The following code shows how to read a small file using the new Files class:

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

Apoi puteți utiliza BufferedReader pentru a citi rând cu rând, Scanner pentru a citi folosind diferite delimitatori, StreamTokenizer pentru a citi un fișier în jetoane, DataInputStream pentru a citi date binare și tipuri de date primitive, SequenceInput Stream pentru a lega mai multe fișiere într-un singur flux, FileChannel pentru a citi mai repede din fișiere mari etc.

Puteți găsi codul sursă în următoarea repo GitHub.