Java 寫入檔案
Java 提供多種方式寫入檔案,適用於不同的使用場景。
使用 Files(推薦)
Java 7+ 的 Files 類別提供簡潔的 API:
寫入字串
import java.nio.file.Files;
import java.nio.file.Paths;
// 寫入字串(Java 11+)
Files.writeString(Paths.get("file.txt"), "Hello, World!");
// 附加模式
Files.writeString(Paths.get("file.txt"), "New content",
StandardOpenOption.APPEND);
// 指定選項
Files.writeString(Paths.get("file.txt"), "Content",
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
寫入位元組
byte[] data = "Hello".getBytes(StandardCharsets.UTF_8);
Files.write(Paths.get("file.txt"), data);
// 附加模式
Files.write(Paths.get("file.txt"), data, StandardOpenOption.APPEND);
寫入多行
List<String> lines = Arrays.asList("Line 1", "Line 2", "Line 3");
Files.write(Paths.get("file.txt"), lines);
// 指定編碼
Files.write(Paths.get("file.txt"), lines, StandardCharsets.UTF_8);
使用 BufferedWriter
適合寫入大量文字:
import java.io.BufferedWriter;
import java.io.FileWriter;
// 基本用法
try (BufferedWriter writer = new BufferedWriter(new FileWriter("file.txt"))) {
writer.write("Hello, World!");
writer.newLine(); // 寫入換行
writer.write("Second line");
}
// 附加模式
try (BufferedWriter writer = new BufferedWriter(new FileWriter("file.txt", true))) {
writer.write("Appended content");
}
// 使用 Files.newBufferedWriter(推薦)
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get("file.txt"))) {
writer.write("Content");
}
// 指定編碼和選項
try (BufferedWriter writer = Files.newBufferedWriter(
Paths.get("file.txt"),
StandardCharsets.UTF_8,
StandardOpenOption.APPEND)) {
writer.write("Content");
}
使用 FileOutputStream
寫入二進位檔案:
import java.io.FileOutputStream;
// 寫入位元組
try (FileOutputStream fos = new FileOutputStream("file.bin")) {
byte[] data = {0x48, 0x65, 0x6C, 0x6C, 0x6F};
fos.write(data);
}
// 附加模式
try (FileOutputStream fos = new FileOutputStream("file.bin", true)) {
fos.write(data);
}
使用 PrintWriter
方便格式化輸出:
import java.io.PrintWriter;
try (PrintWriter writer = new PrintWriter("file.txt")) {
writer.println("Line 1");
writer.println("Line 2");
writer.printf("Name: %s, Age: %d%n", "Alice", 25);
writer.print("No newline");
}
// 指定編碼
try (PrintWriter writer = new PrintWriter("file.txt", StandardCharsets.UTF_8)) {
writer.println("UTF-8 content");
}
// 自動 flush
try (PrintWriter writer = new PrintWriter(
new BufferedWriter(new FileWriter("file.txt")), true)) {
writer.println("Auto-flushed");
}
使用 FileWriter
簡單的字元寫入:
import java.io.FileWriter;
try (FileWriter writer = new FileWriter("file.txt")) {
writer.write("Hello, World!");
writer.write('\n');
writer.write("Second line");
}
// 附加模式
try (FileWriter writer = new FileWriter("file.txt", true)) {
writer.write("Appended");
}
// 指定編碼(Java 11+)
try (FileWriter writer = new FileWriter("file.txt", StandardCharsets.UTF_8)) {
writer.write("UTF-8 content");
}
寫入選項
StandardOpenOption 常用選項:
| 選項 | 說明 |
|---|---|
CREATE | 不存在則建立 |
CREATE_NEW | 建立新檔案(存在則失敗) |
APPEND | 附加模式 |
TRUNCATE_EXISTING | 清空現有內容 |
WRITE | 開啟寫入 |
SYNC | 同步寫入 |
// 組合使用
Files.writeString(Paths.get("file.txt"), "Content",
StandardOpenOption.CREATE,
StandardOpenOption.WRITE,
StandardOpenOption.TRUNCATE_EXISTING);
實際範例
寫入日誌
public class SimpleLogger {
private final Path logFile;
private final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public SimpleLogger(String filename) {
this.logFile = Paths.get(filename);
}
public void log(String message) {
String timestamp = LocalDateTime.now().format(formatter);
String logEntry = String.format("[%s] %s%n", timestamp, message);
try {
Files.writeString(logFile, logEntry,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
}
}
寫入 CSV
public void writeCSV(String filename, List<String[]> data) throws IOException {
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename))) {
for (String[] row : data) {
writer.write(String.join(",", row));
writer.newLine();
}
}
}
// 使用
List<String[]> data = Arrays.asList(
new String[]{"Name", "Age", "City"},
new String[]{"Alice", "25", "Taipei"},
new String[]{"Bob", "30", "Kaohsiung"}
);
writeCSV("data.csv", data);
寫入 JSON(使用 Jackson)
public <T> void writeJson(String filename, T object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.writerWithDefaultPrettyPrinter()
.writeValue(new File(filename), object);
}
// 使用
User user = new User("Alice", 25);
writeJson("user.json", user);
複製檔案
public void copyFile(String source, String target) throws IOException {
try (InputStream in = new FileInputStream(source);
OutputStream out = new FileOutputStream(target)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
}
// 或使用 Files
Files.copy(Paths.get(source), Paths.get(target),
StandardCopyOption.REPLACE_EXISTING);
產生報表
public void generateReport(String filename, List<SalesRecord> records)
throws IOException {
try (PrintWriter writer = new PrintWriter(filename)) {
// 標題
writer.println("=".repeat(50));
writer.println("銷售報表");
writer.println("=".repeat(50));
writer.println();
// 表頭
writer.printf("%-20s %10s %15s%n", "商品", "數量", "金額");
writer.println("-".repeat(50));
// 資料
double total = 0;
for (SalesRecord r : records) {
writer.printf("%-20s %10d %15.2f%n",
r.getProduct(), r.getQuantity(), r.getAmount());
total += r.getAmount();
}
// 合計
writer.println("-".repeat(50));
writer.printf("%-20s %10s %15.2f%n", "合計", "", total);
}
}
批次寫入
public void batchWrite(String filename, List<String> data, int batchSize)
throws IOException {
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename))) {
int count = 0;
for (String line : data) {
writer.write(line);
writer.newLine();
count++;
if (count % batchSize == 0) {
writer.flush(); // 定期 flush
}
}
}
}
方法比較
| 方法 | 適用場景 | 特點 |
|---|---|---|
Files.writeString() | 小型文字 | 最簡潔 |
Files.write() | 位元組或多行 | 彈性大 |
BufferedWriter | 大型文字 | 效能好 |
PrintWriter | 格式化輸出 | 方便格式化 |
FileOutputStream | 二進位 | 位元組寫入 |
注意事項
處理編碼
// 明確指定編碼
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("file.txt"), StandardCharsets.UTF_8))) {
writer.write("中文內容");
}
// 使用 Files(預設 UTF-8)
Files.writeString(Paths.get("file.txt"), "中文內容");
確保寫入完成
// 使用 flush
writer.flush();
// 使用 SYNC 選項
Files.writeString(path, content, StandardOpenOption.SYNC);
原子寫入
// 先寫入暫存檔案,再重新命名
Path temp = Files.createTempFile("temp", ".txt");
Files.writeString(temp, content);
Files.move(temp, target, StandardCopyOption.ATOMIC_MOVE);
重點整理
- 小檔案:使用
Files.writeString()或Files.write() - 大檔案:使用
BufferedWriter - 格式化輸出:使用
PrintWriter - 二進位:使用
FileOutputStream - 一定要使用 try-with-resources 關閉資源
- 明確指定編碼避免問題
- 考慮使用原子寫入確保資料完整性