# Python 標準函式庫中的資料儲存與序列化:`json`、`pickle`、`csv` 若要將資料寫入檔案或透過網路傳輸,必須先進行**序列化(Serialization)**。Python 為此提供了三種特性各異的標準工具:**`json`、`pickle`、`csv`**。 ![資料傳輸格式供應商](/media/editor_temp/6/82b2d285-16b0-473a-b88a-cd9a6f6582f2.png) * **`json`**:可讀文本格式,跨語言交換友好 * **`pickle`**:將 Python 物件直接存成二進位格式,功能強大但存在風險 * **`csv`**:最簡單的表格資料文本格式,通用性高 本文將根據「資料用途」,整理並說明這三個模組的選擇時機與考量。 --- ## 1. JSON {#sec-c0452ee3f8b9} ### 1.1 概述 {#sec-573894aef250} JSON(JavaScript Object Notation)是**基於文字**且**語言獨立**的格式。設定檔、API 回應/請求、日誌等「適合與其他系統交換」的資料常用此格式。Python 直接使用 `json` 模組處理。 ### 1.2 核心 API(常用) {#sec-7388e1478aff} * `json.dump(obj, fp, ...)` / `json.load(fp, ...)`:寫入/讀取檔案 * `json.dumps(obj, ...)` / `json.loads(s, ...)`:轉成字串/從字串還原 常用選項: * `ensure_ascii=False`:不將非 ASCII 字元轉成 \\uXXXX,直接保留原文 * `indent=2`:美化輸出 * `default=...`:提供一個「轉換」函式,處理 JSON 無法序列化的型別,將其轉換為可序列化的形式。 ### 1.3 使用範例(基本) {#sec-57d0c0653337} ```python import json data = { "name": "Alice", "age": 30, "skills": ["Python", "Data Science"] } with open("data.json", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) with open("data.json", "r", encoding="utf-8") as f: loaded = json.load(f) print(loaded) ``` ### 1.4 方便處理 JSON 的「型別限制」 {#sec-a43d0fe8a199} JSON 只天然支援 `dict`、`list`、`str`、`int/float`、`bool`、`None`。例如 `datetime` 需要額外處理。可透過 `default` 參數傳入函式,將不支援型別轉成 JSON 可接受的型別。 ```python import json from datetime import datetime, timezone payload = {"created_at": datetime.now(timezone.utc)} def to_jsonable(obj): if isinstance(obj, datetime): return obj.isoformat() raise TypeError(f"Not JSON serializable: {type(obj)!r}") s = json.dumps(payload, default=to_jsonable, ensure_ascii=False) print(s) ``` ### 1.5 優缺點 {#sec-667d6fad184b} **優點** * 語言獨立,易於交換與分享 * 文字格式,易於除錯與版本控制 * 不像 `pickle` 那樣在載入時執行程式碼 **缺點** * 型別表達有限(如 `datetime`、`set`、`Decimal`、二進位資料) * 大量資料時,容量與速度可能不如二進位格式 --- ## 2. Pickle {#sec-7417b1955ccc} ### 2.1 概述 {#sec-1889607ea8cb} `pickle` 是一種旨在「完整保留 Python 物件原始狀態」進行序列化的方式。輸出為二進位資料(bytes),再次載入時可得到近乎相同的物件。 `pickle` 特別適用於: * 想保留自訂類別實例、巢狀資料結構、已訓練模型/設定物件等 * 不需要與其他語言交換 相反,若需跨語言或信任度低的來源,應避免使用。 --- ### 2.2 核心 API(最常用 4 個) {#sec-a0a97fe87a25} * `pickle.dump(obj, file, protocol=...)`:寫入檔案 * `pickle.load(file)`:讀取檔案 * `pickle.dumps(obj, protocol=...)`:轉成 bytes * `pickle.loads(data)`:從 bytes 讀取 ```python import pickle data = {"a": [1, 2, 3], "b": ("x", "y")} with open("data.pkl", "wb") as f: pickle.dump(data, f) with open("data.pkl", "rb") as f: loaded = pickle.load(f) print(loaded) ``` --- ### 2.3 `protocol` 是什麼?為何要注意? {#sec-1f8e5565848c} `protocol` 是 pickle 存檔時使用的「格式版本」。不同版本會影響檔案大小、速度與支援功能。 * 未指定時,Python 會選擇「最適合目前環境」的預設值 * 只在以下兩種情況下需要手動設定: 1. 與非常舊的 Python 版本保持相容 2. 想明確使用最新、最快的格式 常見做法是使用 `pickle.HIGHEST_PROTOCOL`(目前最高的協定號)。 ```python import pickle with open("data.pkl", "wb") as f: pickle.dump({"x": 1}, f, protocol=pickle.HIGHEST_PROTOCOL) ``` --- ### 2.4 Pickle 的主要注意事項(重要) {#sec-007b70a5e9ec} `pickle.load()` / `pickle.loads()` 會執行資料中包含的「反序列化邏輯」。因此,**載入不可信的 pickle 可能導致惡意程式執行**。 * 安全做法:**不要載入未知來源的 `.pkl` 檔** * 若需交換,建議改用 `json` 等文字格式 ### 2.5 優缺點 {#sec-bbd5a24bec31} **優點** * 能存任何 Python 物件(幾乎無限制) * 通常比 `json` 更快、更方便(尤其是複雜物件) **缺點** * 安全風險(不可信來源不可載入) * 只適用於 Python,無法跨語言 * 物件定義變更可能破壞舊 pickle(版本相容問題) --- ## 3. CSV {#sec-99e590b24e50} ### 3.1 概述 {#sec-1265cc44a461} CSV(Comma-Separated Values)是最簡單的表格資料格式。常見於試算表、資料匯出/匯入、簡易日誌等。 ### 3.2 核心 API(常用) {#sec-5b2e4c37cce5} * `csv.reader`、`csv.writer`:基於列表 * `csv.DictReader`、`csv.DictWriter`:基於字典(大多使用此方式) ### 3.3 使用範例(DictWriter/DictReader) {#sec-524921270351} ```python import csv data = [ {"name": "Alice", "age": 30, "city": "Seoul"}, {"name": "Bob", "age": 25, "city": "Busan"}, ] with open("people.csv", "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=["name", "age", "city"]) writer.writeheader() writer.writerows(data) with open("people.csv", "r", encoding="utf-8") as f: reader = csv.DictReader(f) loaded = list(reader) print(loaded) ``` ### 3.4 CSV 的三點注意事項 {#sec-3db1b7ed8444} 1. **一定要加 `newline=""`**:避免 Windows 產生雙行 2. **所有資料皆為字串**:讀取後需自行轉型 3. **分隔符/引號/換行可能出現在資料中**:使用 `csv` 模組可自動處理 ### 3.5 優缺點 {#sec-6a8ca4891a8e} **優點** * 輕量、通用(大多工具可讀寫) * 對表格資料極簡易處理 **缺點** * 無法表達巢狀結構(如列表內列表、字典巢狀) * 缺少型別資訊,需手動轉換 --- ## 4. 比較 & 選擇指引 {#sec-6304ec188cc0} | 項目 | JSON | Pickle | CSV | |------|------|--------|-----| | 語言相容 | 非常好 | 只適用 Python | 非常好 | | 可讀性 | 高 | 低(二進位) | 高 | | 型別表達 | 限制 | 非常高 | 限制 | | 安全性 | 相對安全 | **需注意** | 相對安全 | | 資料結構 | 樹狀(巢狀) | 原始 Python 物件 | 表格(行/列) | 一句話總結: * **跨系統交換** → `json` * **欲完整保留 Python 物件狀態** → `pickle`(但來源必須可信) * **表格輸出/輸入** → `csv` --- ## 5. 同一資料用三種方式儲存/還原 {#sec-21f8c2a6d6c9} 以下示範同一資料分別以 `json`、`pickle`、`csv` 儲存並讀取。 ```python import json, pickle, csv data = [ {"id": 1, "name": "Alice", "score": 95.5}, {"id": 2, "name": "Bob", "score": 88.0}, ] # 1) JSON with open("data.json", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) with open("data.json", "r", encoding="utf-8") as f: json_loaded = json.load(f) # 2) Pickle with open("data.pkl", "wb") as f: pickle.dump(data, f) with open("data.pkl", "rb") as f: pickle_loaded = pickle.load(f) # 3) CSV with open("data.csv", "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=["id", "name", "score"]) writer.writeheader() writer.writerows(data) with open("data.csv", "r", encoding="utf-8") as f: reader = csv.DictReader(f) csv_loaded = [ {"id": int(row["id"]), "name": row["name"], "score": float(row["score"])} for row in reader ] print(json_loaded) print(pickle_loaded) print(csv_loaded) ``` 重點: * CSV 讀取後需手動轉型 * Pickle 方便但須注意來源安全 --- ## 6. 結語 {#sec-f88db3766ed6} 僅靠 Python 標準函式庫,就能滿足資料儲存與序列化的廣泛需求。 * **需要跨語言或人可讀** → `json` * **想保留 Python 物件完整** → `pickle` * **想簡單輸出表格** → `csv` 根據「資料形態」與「目的」選擇合適模組,即可快速、乾淨地完成儲存與還原。