1. 讀取整個檔案
讀取所有位元組
有時候需要一次性把檔案的所有內容原封不動讀出來——無論是圖片、壓縮檔,還是某種二進位格式。為此,Java 提供了方法 Files.readAllBytes(path)。它會回傳位元組陣列(byte[]),也就是檔案的原始資料,之後你可以任意使用。
import java.nio.file.*;
public class ReadBytesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("example.txt");
byte[] allBytes = Files.readAllBytes(path);
System.out.println("檔案的位元組長度: " + allBytes.length);
}
}
注意:如果檔案很大(數 GB),這種做法可能導致記憶體問題——因為所有內容都會一次載入到記憶體中。
讀取所有行
對於文字檔,有一種更友善的方式:Files.readAllLines(path)。它會回傳字串清單(List<String>),其中每個元素對應檔案中的一行(會自動處理換行符)。
import java.nio.file.*;
import java.util.List;
public class ReadLinesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("example.txt");
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
}
}
重要:預設會使用系統編碼。若想明確指定編碼(例如 UTF-8),請使用多載的方法:
List<String> lines = Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8);
範例:讀取檔案並顯示在螢幕上
把它加入我們的小型應用(例如「記事本」或「待辦清單」):
import java.nio.file.*;
import java.util.List;
public class TodoReader {
public static void main(String[] args) throws Exception {
Path todoPath = Paths.get("todo.txt");
if (Files.exists(todoPath)) {
List<String> tasks = Files.readAllLines(todoPath);
System.out.println("您今天的待辦事項:");
for (String task : tasks) {
System.out.println("- " + task);
}
} else {
System.out.println("找不到檔案 todo.txt。請先建立待辦清單!");
}
}
}
2. 寫入檔案
寫入位元組陣列
如果你已經有一個位元組陣列(例如序列化結果、影像或音訊),可使用 Files.write(path, bytes)。此方法會在檔案不存在時建立檔案;若存在,則覆寫之。
import java.nio.file.*;
public class WriteBytesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("output.bin");
byte[] data = {1, 2, 3, 4, 5};
Files.write(path, data);
System.out.println("位元組已寫入檔案 output.bin");
}
}
寫入字串清單
對文字檔而言,使用字串清單更方便——每個元素會各寫一行,並自動加入換行。
import java.nio.file.*;
import java.util.Arrays;
import java.util.List;
public class WriteLinesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("todo.txt");
List<String> tasks = Arrays.asList(
"買牛奶",
"打電話給奶奶",
"做 Java 作業"
);
Files.write(path, tasks);
System.out.println("待辦清單已寫入 todo.txt");
}
}
注意:預設會覆寫檔案!如果它已存在,舊的內容會消失。(如何避免遺失舊內容——我們稍後會說明。)
將單一字串寫入檔案
若只需要寫入一行字串——可以建立只含一個元素的清單,或使用 Files.write(path, string.getBytes()):
import java.nio.file.*;
public class WriteStringExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("hello.txt");
String message = "哈囉,Java!";
Files.write(path, message.getBytes());
System.out.println("字串已寫入 hello.txt");
}
}
明確指定編碼
為了避免處理中文或其他語言文字時出現亂碼,請務必明確指定編碼:
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
public class WriteLinesUtf8Example {
public static void main(String[] args) throws Exception {
Path path = Paths.get("hello.txt");
List<String> lines = List.of("你好,世界!", "這是 Java。");
Files.write(path, lines, StandardCharsets.UTF_8);
}
}
3. 錯誤處理
處理檔案時,Java 可能會拋出例外 IOException。這是 I/O 層面「出了點狀況」的通用訊號。成因很多:檔案不存在、權限不足、磁碟滿了或被佔用,有時甚至是隨身碟在最不適合的時刻被拔掉了。
因此,讀寫檔案的方法總是要求我們準備好處理這些情況。作法是使用 try-catch:
import java.nio.file.*;
import java.util.List;
import java.io.IOException;
public class SafeReadExample {
public static void main(String[] args) {
Path path = Paths.get("todo.txt");
try {
List<String> lines = Files.readAllLines(path);
System.out.println("檔案內容:");
for (String line : lines) {
System.out.println(line);
}
} catch (IOException ex) {
System.out.println("讀取檔案時發生錯誤: " + ex.getMessage());
}
}
}
為什麼要這麼做?
try-catch能把潛在的程式崩潰轉化為可控的情境。若檔案不存在——我們可以從容地告知使用者。若系統不允許存取——指出問題所在。就算磁碟在操作途中消失,程式也不會直接因可怕的堆疊追蹤而當掉,至少能以正常訊息提醒。換句話說:IOException 不是敵人,它只是 Java 在告訴我們:「嘿,外部發生了問題,想想接下來怎麼處理。」
4. 實用範例
讀取檔案並輸出內容
import java.nio.file.*;
import java.util.List;
import java.io.IOException;
public class PrintFileExample {
public static void main(String[] args) {
Path path = Paths.get("notes.txt");
try {
if (!Files.exists(path)) {
System.out.println("找不到檔案 notes.txt。");
return;
}
List<String> lines = Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8);
System.out.println("notes.txt 的內容:");
for (String line : lines) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("讀取檔案時發生錯誤: " + e.getMessage());
}
}
}
將字串寫入新檔案
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
public class WriteFileExample {
public static void main(String[] args) {
Path path = Paths.get("greeting.txt");
String content = "歡迎來到 Java IO 的世界!";
try {
Files.write(path, content.getBytes(StandardCharsets.UTF_8));
System.out.println("字串已寫入檔案 greeting.txt");
} catch (IOException e) {
System.out.println("寫入檔案時發生錯誤: " + e.getMessage());
}
}
}
將字串清單寫入檔案
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.io.IOException;
public class WriteListExample {
public static void main(String[] args) {
Path path = Paths.get("shopping.txt");
List<String> items = List.of("麵包", "牛奶", "起司");
try {
Files.write(path, items, StandardCharsets.UTF_8);
System.out.println("購物清單已寫入 shopping.txt");
} catch (IOException e) {
System.out.println("寫入檔案時發生錯誤: " + e.getMessage());
}
}
}
5. 關於串流的簡述
方法 Files.readAllBytes、Files.readAllLines、Files.write 都是「一次性處理」:要嘛整個檔案進記憶體,要嘛一次把全部內容寫到磁碟。對小檔方便,但對大檔就不太適合(可能導致 OutOfMemoryError,或讀取數 GB 的日誌時整個卡住)。
處理大檔或需要逐行讀取時,應改用串流:
- 文字:BufferedReader、BufferedWriter
- 位元組:InputStream、OutputStream
關於串流的細節我們會在後續講座中再談,這裡先給個小小預告:
import java.nio.file.*;
import java.io.*;
public class BufferedReaderExample {
public static void main(String[] args) {
Path path = Paths.get("bigfile.txt");
try (BufferedReader reader = Files.newBufferedReader(path)) {
String line;
while ((line = reader.readLine()) != null) {
// 處理每一行
System.out.println(line);
}
} catch (IOException e) {
System.out.println("讀取檔案時發生錯誤: " + e.getMessage());
}
}
}
6. 實用小重點
- 覆寫檔案:所有 Files.write 方法預設都會覆寫檔案。若需要在末尾追加,請使用額外選項(這部分會在下一堂課說明)。
- 編碼:處理非 ASCII 文字時,請務必明確指定編碼。這能避免輸出亂碼。建議使用 StandardCharsets.UTF_8。
- 路徑:請使用 Paths.get(...) 以確保跨平台性。千萬不要手動硬編碼斜線(/ 或 \)。
- 檔名:請不要在檔名中使用不允許的字元(如 ?、*、:、<、>、| 等),特別是你的程式要在 Windows 上執行時。
7. 讀寫檔案的常見錯誤
錯誤 1:未處理 IOException。新手很常直接呼叫 Files.readAllLines(path) 而沒有搭配 try-catch,一旦出現問題(找不到檔案、沒有權限、磁碟毀損),程式就會當掉。請務必處理例外!
錯誤 2:用 readAllBytes/readAllLines 處理大檔。如果檔案大小達數百 MB 甚至 GB,嘗試整個載入記憶體可能會「擊垮」你的程式。這種情況應改用串流(BufferedReader)。
錯誤 3:未在讀寫文字時明確指定編碼。若不指定編碼,在不同電腦與作業系統上可能得到不同結果。此問題在處理非拉丁字母的文字時特別常見。請明確使用 StandardCharsets.UTF_8 或你需要的編碼。
錯誤 4:以為 File/Path 就是檔案本身。File 或 Path 類別只是指向檔案的「標記」或「參照」,並非檔案本體。建立物件並不會在磁碟上建立檔案。若要建立檔案,請使用 Files.createFile、Files.write 等方法。
錯誤 5:使用串流時未關閉。如果你手動使用串流(例如 BufferedReader),務必將它們關閉(最好使用 try-with-resources)。否則檔案可能會被鎖住,導致其他程式無法存取。
GO TO FULL VERSION