跳至主要內容
問題與解答
CSV、Excel 與資料處理的中文編碼問題

CSV、Excel 與資料處理的中文編碼問題

問:
CSV 用 Excel 開啟是亂碼怎麼辦? 怎麼判斷檔案的編碼? 轉成 Big5 有些字會出錯 JSON 存出來中文變成亂碼
答:

中文 CSV 的編碼問題

這是實務上最常遇到的坑。同一個檔案,用不同軟體開啟可能出現亂碼。

問題的來源

  • Excel 在部分環境下預設用系統編碼開啟 CSV,而非 UTF-8
  • 舊系統可能匯出 Big5 編碼
  • 不同來源的檔案編碼不一致

讓 Excel 正確開啟的做法

在 UTF-8 檔案開頭加上位元組順序記號,Excel 就能正確辨識:

import csv

with open('output.csv', 'w', newline='', encoding='utf-8-sig') as f:
    writer = csv.writer(f)
    writer.writerow(['標題', '內容', '日期'])
    writer.writerow(['測試', '中文內容', '2026-08-05'])

關鍵是 encoding='utf-8-sig'——它會自動加上記號。

兩個必要的參數

  • newline=''——不加的話在某些系統會產生多餘的空行
  • encoding——明確指定,不要依賴系統預設

讀取來源不明的檔案

def read_csv_auto(path):
    encodings = ['utf-8-sig', 'utf-8', 'cp950', 'big5']
    for enc in encodings:
        try:
            with open(path, newline='', encoding=enc) as f:
                return list(csv.DictReader(f)), enc
        except UnicodeDecodeError:
            continue
    raise ValueError('無法判斷編碼')

注意 cp950 要放在 big5 前面——它是 Big5 的擴充,涵蓋較多字元。

Big5 的字元缺漏

若需要輸出 Big5 給舊系統,部分字元可能無法轉換——例如某些罕用字、特殊符號、以及使用者造字。

text = '測試內容'
try:
    data = text.encode('cp950')
except UnicodeEncodeError as e:
    print(f'無法轉換的字元位置: {e.start}')
    # 可改用替代字元或 HTML 實體
    data = text.encode('cp950', errors='xmlcharrefreplace')

實務建議:轉換前先檢查哪些字元會有問題,決定是替換、改用實體標記,還是回報給來源修正。

用 DictReader 與 DictWriter

處理有標題列的 CSV 時,用字典的方式比索引可靠得多:

import csv

# 讀取
with open('input.csv', newline='', encoding='utf-8-sig') as f:
    rows = list(csv.DictReader(f))

for row in rows:
    print(row['title'], row['slug'])

# 寫出
fields = ['title', 'slug', 'content']
with open('output.csv', 'w', newline='', encoding='utf-8-sig') as f:
    w = csv.DictWriter(f, fieldnames=fields)
    w.writeheader()
    w.writerows(rows)

好處是欄位順序改變時程式不會壞掉,也比 row[3] 這種寫法容易理解。

欄位中包含換行或逗號

CSV 模組會自動處理引號跳脫,不要自己用字串拼接產生 CSV。

# 不要這樣做
line = f'{title},{content}\n'   # content 有逗號就爆了

# 應該用 csv 模組
writer.writerow([title, content])

內容含 HTML 或多行文字時,這一點特別重要。

Excel 檔案的處理

from openpyxl import load_workbook, Workbook

# 讀取
wb = load_workbook('data.xlsx', data_only=True)
ws = wb.active
for row in ws.iter_rows(min_row=2, values_only=True):
    print(row)

# 寫出
wb = Workbook()
ws = wb.active
ws.append(['標題', '數量'])
ws.append(['測試', 100])
wb.save('output.xlsx')

data_only=True 會讀取公式的計算結果而非公式本身——多數情況這才是你要的。

注意事項

  • 舊的 .xls 格式需要不同的套件
  • 大型檔案讀取時考慮使用唯讀模式以節省記憶體
  • 儲存格格式與樣式需要另外處理

JSON 處理

import json

# 讀取
with open('data.json', encoding='utf-8') as f:
    data = json.load(f)

# 寫出(中文不要被轉成跳脫序列)
with open('output.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

ensure_ascii=False 很重要——不加的話中文會變成一堆跳脫字元,雖然功能正常但完全無法閱讀。

批次重新命名檔案

from pathlib import Path
import re

folder = Path('/path/to/images')

for f in folder.glob('*.jpg'):
    # 轉小寫、空格改連字號、移除特殊字元
    new_name = re.sub(r'[^a-z0-9\-.]', '',
                      f.name.lower().replace(' ', '-'))
    if new_name != f.name:
        target = f.with_name(new_name)
        if target.exists():
            print(f'略過(已存在): {new_name}')
            continue
        f.rename(target)
        print(f'{f.name} -> {new_name}')

三個安全習慣

  1. 先跑一次「只印出不執行」的版本,確認結果符合預期
  2. 檢查目標檔名是否已存在,避免覆蓋
  3. 處理前備份

檔名對搜尋的影響見圖片的替代文字與檔名該怎麼寫

資料清理的常見需求

import re

def clean_text(s):
    if s is None:
        return ''
    s = str(s)
    s = s.replace('\u00a0', ' ')      # 不斷行空白
    s = re.sub(r'[ \t]+', ' ', s)      # 連續空白
    s = re.sub(r'\n{3,}', '\n\n', s)  # 過多空行
    return s.strip()

不斷行空白是最常見的隱形問題——它看起來像空格但不是,會造成比對失敗或版面異常。

處理大檔案

不要一次把整個檔案載入記憶體:

with open('large.csv', newline='', encoding='utf-8-sig') as f:
    for row in csv.DictReader(f):
        process(row)      # 逐列處理

需要輸出時同樣逐列寫入,記憶體用量就與檔案大小無關。

本文的範例以 Python 3 為例,實際的套件版本與 API 可能隨版本更新而異。在正式環境執行任何批次處理前,請先以少量資料測試並確實備份。

發表於2026-08-08   更新於2026-08-24
免費諮詢 · 1 個工作天內回覆

準備好讓網站 開始幫你帶生意了嗎?

不論是要做新網站、救舊網站,還是只想先聊聊方向——先諮詢,不用先付錢,我們照實給你建議。

1 個工作天回覆免費諮詢與報價費用白紙黑字26 年找得到人
免費諮詢
免費諮詢 LINE諮詢 03-4020420 臉書傳訊
免費諮詢 LINE諮詢 03-4020420 臉書傳訊
免費諮詢
免費諮詢 LINE諮詢 03-4020420 臉書傳訊
免費諮詢 LINE諮詢 03-4020420 臉書傳訊