更新時間:2022-10-31 10:32:44 來源:動力節點 瀏覽5981次
在本教程中,我們將探索從 Java 中讀取文件的不同方法。
首先,我們將學習如何從類路徑、URL 或使用標準 Java 類的 JAR 文件加載文件。
其次,我們將了解如何使用 BufferedReader、Scanner、StreamTokenizer、DataInputStream、SequenceInputStream和FileChannel讀取內容。我們還將討論如何讀取 UTF-8 編碼文件。
最后,我們將探索在 Java 7 和 Java 8 中加載和讀取文件的新技術。
1.輸入文件
在本文的大多數示例中,我們將讀取一個文件名為 fileTest.txt的文本文件,其中包含一行:
Hello, world!
對于一些示例,我們將使用不同的文件;在這些情況下,我們將明確提及文件及其內容。
2.輔助方法
我們將使用一組僅包含核心 Java 類的測試示例,在測試中,我們將使用帶有Hamcrest匹配器的斷言。
測試將共享一個通用的readFromInputStream方法,該方法將InputStream轉換為String以便更輕松地斷言結果:
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();
}
請注意,還有其他方法可以實現相同的結果。
1.使用標準 Java
本節說明如何讀取類路徑中可用的文件。我們將閱讀src/main/resources下的“ fileTest.txt ” :
@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));
}
在上面的代碼片段中,我們使用當前類使用getResourceAsStream方法加載文件,并傳遞要加載的文件的絕對路徑。
ClassLoader實例也可以使用相同的方法:
ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("fileTest.txt");
String data = readFromInputStream(inputStream);
我們使用getClass().getClassLoader()獲取當前類的類加載器。
主要區別在于,在ClassLoader實例上使用getResourceAsStream時,路徑被視為從類路徑的根開始的絕對路徑。
當用于Class instance時,路徑可以是相對于包的路徑,也可以是絕對路徑,由前導斜杠暗示。
當然,請注意,在實踐中,打開的流應該始終是關閉的,例如我們示例中的InputStream :
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();
}
}
}
2.使用commons-io庫
另一個常見的選項是使用commons-io包的FileUtils類:
@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());
}
這里我們將File對象傳遞給FileUtils類的readFileToString()方法。該實用程序類管理加載內容,而無需編寫任何樣板代碼來創建InputStream實例并讀取數據。
同一個庫還提供IOUtils 類:
@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());
}
里我們將FileInputStream對象傳遞給IOUtils類的toString()方法。此實用程序類的行為與前一個相同,以創建InputStream實例并讀取數據。
現在讓我們關注解析文件內容的不同方法。
我們將從使用BufferedReader 讀取文件的簡單方法開始:
@Test
public void whenReadWithBufferedReader_thenCorrect()
throws IOException {
String expected_value = "Hello, world!";
String file ="src/test/resources/fileTest.txt";
BufferedReader reader = new BufferedReader(new FileReader(file));
String currentLine = reader.readLine();
reader.close();
assertEquals(expected_value, currentLine);
}
請注意,當到達文件末尾時, readLine()將返回null 。
在 JDK7 中,NIO 包進行了重大更新。
讓我們看一個使用Files類和readAllLines 方法的示例。readAllLines方法 接受一個路徑。
Path類可以被認為是對java.io.File的升級,其中包含一些額外的操作。
1.讀取小文件
以下代碼顯示了如何使用新的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);
}
請注意,如果我們需要二進制數據,我們也可以使用readAllBytes()方法。
2.讀取大文件
如果我們想用Files類讀取一個大文件,我們可以使用BufferedReader。
以下代碼使用新的Files類和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);
}
3.使用Files.lines()讀取文件
JDK8在Files類中提供了lines()方法。它返回一個字符串元素流。
讓我們看一個如何將數據讀入字節并使用 UTF-8 字符集對其進行解碼的示例。
以下代碼使用 new Files.lines()讀取文件:
@Test
public void givenFilePath_whenUsingFilesLines_thenFileData() {
String expectedData = "Hello, world!";
Path path = Paths.get(getClass().getClassLoader()
.getResource("fileTest.txt").toURI());
Stream<String> lines = Files.lines(path);
String data = lines.collect(Collectors.joining("\n"));
lines.close();
Assert.assertEquals(expectedData, data.trim());
}
將 Stream 與文件操作等 IO 通道一起使用,我們需要使用 close()方法顯式關閉流。
正如我們所見,Files API 提供了另一種將文件內容讀入字符串的簡單方法。
在接下來的部分中,我們將了解其他不太常見的讀取文件的方法,這些方法在某些情況下可能是合適的。
接下來讓我們使用掃描儀從文件中讀取。在這里,我們將使用空格作為分隔符:
@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();
}
請注意,默認分隔符是空格,但多個分隔符可以與Scanner一起使用。
Scanner類在從控制臺讀取內容時很有用,或者當內容包含帶有已知分隔符的原始值(例如:用空格分隔的整數列表)時。
現在讓我們使用StreamTokenizer將文本文件讀入令牌。
標記器首先確定下一個標記是什么,字符串或數字。我們通過查看tokenizer.ttype 字段來做到這一點。
然后我們將根據這種類型讀取實際的令牌:
tokenizer.nval – 如果類型是數字
tokenizer.sval – 如果類型是字符串
在這個例子中,我們將使用一個不同的輸入文件,它只包含:
Hello 1
以下代碼從文件中讀取字符串和數字:
@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();
}
請注意最后如何使用文件結束標記。
這種方法對于將輸入流解析為標記很有用。
我們可以使用DataInputStream從文件中讀取二進制或原始數據類型。
以下測試使用DataInputStream讀取文件:
@Test
public void whenReadWithDataInputStream_thenCorrect() throws IOException {
String expectedValue = "Hello, world!";
String file ="src/test/resources/fileTest.txt";
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);
}
如果我們正在讀取一個大文件,FileChannel可以比標準 IO 更快。
以下代碼使用FileChannel和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();
}
現在讓我們看看如何使用BufferedReader 讀取 UTF-8 編碼的文件。在這個例子中,我們將讀取一個包含中文字符的文件:
@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);
}
要從 URL 讀取內容,我們將在示例中使用“ / ” URL:
@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));
}
還有其他連接到 URL 的方法。在這里,我們使用了標準 SDK 中可用的URL和URLConnection類。
要讀取位于 JAR 文件中的文件,我們需要一個包含文件的 JAR。對于我們的示例,我們將從“ hamcrest-library-1.3.jar ”文件中讀取“ LICENSE.txt ”:
@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));
}
在這里,我們要加載位于 Hamcrest 庫中的LICENSE.txt ,因此我們將使用有助于獲取資源的Matcher類。也可以使用類加載器加載相同的文件。如果大家想了解更多相關知識,不妨來關注一下本站的Java在線學習,里面的課程內容細致全面,很適合沒有基礎的小伙伴學習,希望對大家能夠有所幫助哦。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習