Java I/O 按「裝飾者模式」層層包裝:
File → FileInputStream (字節流)
→ InputStreamReader (字節→字符,指定編碼)
→ BufferedReader (緩衝,按行讀取)
public static List<String> readLines(String filePath, String encoding) throws IOException {
List<String> lines = new ArrayList<>();
// try-with-resources 自動關閉資源 (JDK 7+)
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(filePath), encoding))) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
}
return lines;
}
StandardCharsets.UTF_8 或 "UTF-8"juniversalchardet 或 Apache Tika 自動檢測避免一次性 readAllLines() 導致 OOM:
try (Stream<String> stream = Files.lines(Paths.get(filePath), StandardCharsets.UTF_8)) {
stream.forEach(line -> {
// 流式處理每行,不佔用全量內存
processLine(line);
});
}
// 一次性讀取小文件
List<String> lines = Files.readAllLines(Paths.get(filePath), StandardCharsets.UTF_8);
// 寫入文件
Files.write(Paths.get(outPath), lines, StandardCharsets.UTF_8,
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
| 問題 | 原因 | 解決 |
|---|---|---|
| 中文亂碼 | 編碼不匹配 | 顯式指定 UTF-8/GBK,統一項目編碼 |
| 最後一行無換行符丟失 | readLine() 不包含換行符 | 非行結構數據用 read(char[]) 或 Files.readString() |
| 資源洩漏 | 異常拋出前未 close | 必須使用 try-with-resources |
| BOM 頭干擾 | UTF-8 with BOM | 讀取首行時檢查並移除 \uFEFF |
public final class FileUtils {
private FileUtils() {}
public static List<String> readLines(File file, Charset charset) throws IOException {
if (!file.exists() || !file.isFile()) throw new FileNotFoundException(file.getAbsolutePath());
try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), charset))) {
return r.lines().collect(Collectors.toList());
}
}
public static String readAll(File file, Charset charset) throws IOException {
return Files.readString(file.toPath(), charset);
}
public static void writeLines(File file, Collection<String> lines, Charset charset) throws IOException {
Files.write(file.toPath(), lines, charset, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
}
}