Cara Membaca Fail di Java

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan meneroka pelbagai cara untuk membaca dari Fail di Java .

Pertama, kita akan melihat bagaimana memuatkan fail dari classpath, URL atau dari fail JAR, menggunakan kelas Java standard.

Kedua, kita akan melihat cara membaca kandungan dengan BufferedReader , Scanner , StreamTokenizer , DataInputStream , SequenceInputStream, dan FileChannel . Juga, kita akan membincangkan cara membaca fail yang dikodkan UTF-8.

Akhirnya, kami akan meneroka teknik baru untuk memuat dan membaca fail di Java 7 dan Java 8.

Artikel ini adalah sebahagian daripada siri "Java - Back to Basic" di Baeldung.

2. Persediaan

2.1 Fail Input

Dalam kebanyakan contoh dalam artikel ini, kita akan membaca fail teks dengan nama fail fileTest.txt yang mengandungi satu baris:

Hello, world!

Dalam beberapa contoh, kami akan menggunakan fail yang berbeza. Dalam kes ini, kami akan menyebut fail dan kandungannya secara eksplisit.

2.2 Kaedah Pembantu

Kami akan menggunakan sekumpulan contoh ujian hanya menggunakan kelas inti Java, dan dalam ujian, kami akan menggunakan penegasan menggunakan pemadan Hamcrest.

Ujian akan berkongsi kaedah readFromInputStream biasa yang mengubah InputStream ke String untuk penegasan hasil yang lebih mudah:

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

Perhatikan bahawa ada cara lain untuk mencapai hasil yang sama. Anda boleh merujuk artikel ini untuk beberapa alternatif.

3. Membaca Fail dari Classpath

3.1. Menggunakan Java Standard

Bahagian ini menerangkan cara membaca fail yang tersedia di classpath. Kami akan membaca " fileTest.txt " yang tersedia di bawah 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)); }

Dalam coretan kod di atas, kami menggunakan kelas semasa untuk memuat fail menggunakan kaedah getResourceAsStream dan melewati jalur mutlak fail untuk dimuat.

Kaedah yang sama terdapat pada instance ClassLoader juga:

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

Kami memperoleh classLoader dari kelas semasa menggunakan getClass (). GetClassLoader () .

Perbezaan utamanya ialah apabila menggunakan getResourceAsStream pada instance ClassLoader , jalan tersebut dianggap sebagai mutlak bermula dari root classpath.

Ketika digunakan terhadap instance Kelas , jalur dapat relatif dengan paket, atau jalur mutlak, yang diisyaratkan oleh garis miring utama.

Sudah tentu, perhatikan bahawa dalam praktiknya, aliran terbuka harus selalu ditutup , seperti InputStream dalam contoh kami:

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. Menggunakan Perpustakaan commons-io

Pilihan lain yang biasa adalah menggunakan kelas FileUtils pakej 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()); }

Di sini kita lulus Fail objek kepada kaedah readFileToString () daripada FileUtils kelas. Kelas utiliti ini berjaya memuatkan kandungan tanpa perlu menulis kod boilerplate untuk membuat instance InputStream dan membaca data.

Perpustakaan yang sama juga menawarkan IOUtilskelas:

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

Di sini kita lulus FileInputStream objek kepada kaedah toString () daripada IOUtils kelas. Kelas utiliti ini berjaya memuatkan kandungan tanpa perlu menulis kod boilerplate untuk membuat instance InputStream dan membaca data.

4. Membaca dengan BufferedReader

Sekarang mari kita fokus pada pelbagai cara untuk menguraikan kandungan fail.

Kita akan mulakan dengan cara mudah untuk membaca dari fail menggunakan 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); }

Perhatikan bahawa readLine () akan kembali nol apabila akhir fail dicapai.

5. Membaca dari Fail Menggunakan Java NIO

Dalam JDK7, pakej NIO telah dikemas kini dengan ketara.

Mari lihat contoh menggunakan kelas Files dan kaedah readAllLines . The readAllLines kaedah menerima a Path.

Kelas laluan boleh dianggap sebagai peningkatan fail java.io. dengan beberapa operasi tambahan yang ada.

5.1. Membaca Fail Kecil

Kod berikut menunjukkan cara membaca fail kecil menggunakan kelas Fail baru :

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

Kemudian anda boleh menggunakan BufferedReader untuk membaca baris demi baris, Pengimbas untuk membaca menggunakan pembatas yang berbeza, StreamTokenizer untuk membaca fail menjadi token, DataInputStream untuk membaca data binari dan jenis data primitif, Aliran SequenceInput untuk menghubungkan beberapa fail ke dalam satu aliran, FileChannel untuk membaca lebih cepat dari fail besar, dll.

Anda boleh mendapatkan kod sumber di repo GitHub berikut.