從水井三寶到多語導覽——ASCII、Unicode與中文字串
讓白馬、烏龜、姻緣花與台語地方故事,跨過裝置、檔案和網頁仍能正確顯示。
完成本篇後,你能分辨字元、碼位、字串與位元組;理解 ASCII、Unicode 與 UTF-8 的關係;使用
ord()、chr()、encode()、decode();處理中文字串、正規化與常見亂碼。一、電腦怎麼認識「姻緣花」?
人看到的是文字,電腦儲存的是數字與位元組。為了讓不同裝置對同一個字有共同理解,需要三個層次:
| 層次 | 意義 | 例子 |
|---|---|---|
| 字元 character | 人理解的文字單位 | 「花」 |
| Unicode 碼位 | 字元的通用編號 | U+82B1 |
| 編碼 encoding | 把碼位轉成實際位元組的規則 | UTF-8 |
二、ASCII:早期的基本字元表
ASCII 主要涵蓋英文字母、數字、標點與控制字元,無法直接表示「水井村」等中文字。
print(ord("A"))
print(chr(65))
print(ord("1"))
text = "USR"
for character in text:
print(character, ord(character))ord() 取得單一字元的 Unicode 碼位整數;chr() 則把整數轉回字元。ASCII 字元同時也是 Unicode 的一部分。
三、Unicode 不等於 UTF-8
Unicode 是字元及碼位的共同標準;UTF-8、UTF-16 則是把碼位轉成位元組的編碼方式。Python 3 的 str 表示 Unicode 文字,bytes 表示已編碼的位元組。
name = "姻緣花"
print(type(name))
for character in name:
print(character, f"U+{ord(character):04X}")四、encode 與 decode:文字和位元組之間的橋
story = "水井三寶:白馬、烏龜、姻緣花"
utf8_data = story.encode("utf-8")
restored_story = utf8_data.decode("utf-8")
print(utf8_data)
print(restored_story)
print(story == restored_story)記住方向:文字 encode 成 bytes;bytes decode 回文字。兩端必須對同一批位元組使用相符編碼。
五、字數與位元組數為何不同?
texts = ["ABC", "水井村", "水井村🐢"]
for text in texts:
encoded = text.encode("utf-8")
print(
text,
"字串長度:", len(text),
"UTF-8 位元組數:", len(encoded),
)len(str) 計算 Python 字串中的碼位數量;len(bytes) 計算位元組。資料庫欄位、網路封包或設備緩衝區若限制位元組,不能只用中文字數估算。
六、中文字串也能切片與搜尋
guide = "白馬、烏龜、姻緣花"
items = guide.split("、")
print(items)
print("烏龜" in guide)
print(guide.replace("、", "|"))
for number, item in enumerate(items, start=1):
print(f"{number}. {item}")Python 的字串操作對中文同樣適用。但使用者看到的一個圖形,可能由多個 Unicode 碼位組成,因此「畫面上的字數」不一定永遠等於 len()。
七、看起來一樣,電腦卻判斷不同
帶重音字母可以是一個預組字元,也可以由基本字母加組合符號構成。兩者畫面相似,但直接比較可能不同。
import unicodedata
text_a = "Tâi-gí"
text_b = "Ta\u0302i-gi\u0301"
print(text_a, text_b)
print(text_a == text_b)
normalized_a = unicodedata.normalize("NFC", text_a)
normalized_b = unicodedata.normalize("NFC", text_b)
print(normalized_a == normalized_b)搜尋、比對與去除重複資料前,可統一使用 NFC。正規化改變的是字元表示方式,不會自動修正拼字或翻譯。
八、清理訪談轉文字資料
語音轉文字結果常混入全形空白、換行與前後空白。先做最小幅度清理,並保留原始訪談版本。
import unicodedata
def clean_transcript(text):
text = unicodedata.normalize("NFC", text)
text = text.replace("\u3000", " ")
lines = [line.strip() for line in text.splitlines()]
return "\n".join(line for line in lines if line)
raw_text = " 阿嬤講:姻緣花開矣。\n\n 這是庄頭的記持。 "
print(clean_transcript(raw_text))九、讀寫 UTF-8 文字檔
開啟文字檔時明確指定 encoding="utf-8",可降低不同作業系統間的差異。
from pathlib import Path
path = Path("shuijing_guide.txt")
story = "水井三寶:白馬、烏龜、姻緣花\n"
path.write_text(story, encoding="utf-8")
loaded_story = path.read_text(encoding="utf-8")
print(loaded_story)若來源是 Windows 軟體匯出的 UTF-8 BOM 檔案,可視情況以 utf-8-sig 讀取;但應先確認來源格式,不要輪流猜編碼直到「看似正常」。
十、亂碼是怎麼發生的?
亂碼通常不是文字「壞掉」,而是寫入與讀取使用不同編碼。以下故意用錯誤方式示範:
original = "水井村"
utf8_bytes = original.encode("utf-8")
wrong_text = utf8_bytes.decode("latin-1")
print(wrong_text)
recovered = wrong_text.encode("latin-1").decode("utf-8")
print(recovered)這個還原方法只適用於特定「UTF-8 位元組被誤當 Latin-1」的情況。真實資料若已被多次轉碼或遺失位元組,可能無法完全恢復,應優先找回原始檔。
十一、不要隨便忽略解碼錯誤
data = b"guide:\xff\xfe"
try:
text = data.decode("utf-8")
except UnicodeDecodeError as error:
print("無法以 UTF-8 解碼:", error)
text = None
print(text)errors="ignore" 會直接丟掉無法解碼的內容,可能讓地方名稱或訪談語句悄悄缺字。資料保存工作應記錄錯誤並回查來源;只有明確了解後果時才使用替代策略。
十二、完成多語導覽資料清理工具
import unicodedata
def normalize_text(value):
if not isinstance(value, str):
raise TypeError("導覽文字必須是字串")
value = unicodedata.normalize("NFC", value)
return " ".join(value.split())
def clean_guide_record(record):
required = ("主題", "語言", "內容")
missing = [key for key in required if key not in record]
if missing:
raise KeyError("、".join(missing))
return {
"主題": normalize_text(record["主題"]),
"語言": normalize_text(record["語言"]),
"內容": normalize_text(record["內容"]),
}
records = [
{"主題": "白馬", "語言": "華語", "內容": " 水井村地方故事 "},
{"主題": "姻緣花", "語言": "台語", "內容": "庄頭的記持"},
]
for record in records:
print(clean_guide_record(record))十三、常見問題檢查表
| 現象 | 可能原因 | 建議 |
|---|---|---|
| 中文字變成問號或亂碼 | 編碼不一致 | 確認原始編碼及讀寫設定 |
| 相同臺羅詞搜尋不到 | 組合字元表示不同 | 比對前統一 NFC |
| 上傳設備後文字被截斷 | 把字數誤當位元組數 | 以指定編碼後的 bytes 計算 |
| 檔案開頭多出奇怪符號 | UTF-8 BOM | 確認後使用 utf-8-sig |
| 忽略錯誤後地方名稱缺字 | 使用 errors="ignore" | 保留錯誤並回查原始資料 |
十四、USR 實作挑戰
列出「白馬、烏龜、姻緣花」每個字元的 Unicode 碼位與 UTF-8 位元組數,觀察標點與中文字的差異。
對已獲同意的教學逐字稿進行 NFC 與空白清理,逐行比較原始版和清理版,請講述者確認沒有改變原意。
將 UTF-8 導覽檔分別在 Thonny、Colab、手機與網頁開啟,記錄顯示差異與解決方式。
十五、與生成式 AI 協作
請擔任 Python Unicode 與地方語言資料助教。
請檢查我的多語導覽文字處理程式:
1. 分辨 str 與 bytes 的使用是否正確;
2. 檔案讀寫必須明確指定 UTF-8;
3. 搜尋比對前使用 NFC 正規化;
4. 不可用 errors="ignore" 默默刪字;
5. 保留原始逐字稿,不可擅自改寫地方語意;
6. 說明如何測試中文、臺羅與表情符號。
請先指出風險,再提供修正版。Unicode 為字元建立共同編號,UTF-8 則把這些編號轉成位元組。只要清楚區分
str 與 bytes、統一編碼並保留原始資料,多語地方故事就能更可靠地跨平台流動。
沒有留言:
張貼留言