CodeGym /課程 /JAVA 25 SELF /檔案讀寫:基本操作

檔案讀寫:基本操作

JAVA 25 SELF
等級 35 , 課堂 2
開放

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.readAllBytesFiles.readAllLinesFiles.write 都是「一次性處理」:要嘛整個檔案進記憶體,要嘛一次把全部內容寫到磁碟。對小檔方便,但對大檔就不太適合(可能導致 OutOfMemoryError,或讀取數 GB 的日誌時整個卡住)。

處理大檔或需要逐行讀取時,應改用串流:

  • 文字:BufferedReaderBufferedWriter
  • 位元組:InputStreamOutputStream

關於串流的細節我們會在後續講座中再談,這裡先給個小小預告:

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 就是檔案本身。FilePath 類別只是指向檔案的「標記」或「參照」,並非檔案本體。建立物件並不會在磁碟上建立檔案。若要建立檔案,請使用 Files.createFileFiles.write 等方法。

錯誤 5:使用串流時未關閉。如果你手動使用串流(例如 BufferedReader),務必將它們關閉(最好使用 try-with-resources)。否則檔案可能會被鎖住,導致其他程式無法存取。

1
任務
JAVA 25 SELF, 等級 35, 課堂 2
上鎖
您的「秘密」文件佔用多少空間? 📜
您的「秘密」文件佔用多少空間? 📜
1
任務
JAVA 25 SELF, 等級 35, 課堂 2
上鎖
檢視系統事件日誌 💻
檢視系統事件日誌 💻
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION