跳轉到

1.1 字串的表示與編碼#

概念說明#

電腦只能處理二進位數字,因此「文字」必須轉成數字才能儲存與傳輸。這個轉換靠兩個機制:字元集(character set)定義每個字元的編號(碼點),編碼(encoding)定義編號如何轉成位元組序列。例如 ASCII 規定字母 A 的編號是 65,並用一個位元組 0x41 儲存。

ASCII 只能表示 128 個字元,對英文夠用,卻無法涵蓋中文。為了統一全球文字,Unicode 聯盟為每個字元指定一個唯一的碼點(code point),格式常寫作 U+XXXX,例如 中 是 U+4E2D。Unicode 只是編號系統,實際存檔還需編碼方式,最常見的是 UTF-8:它以 1 到 4 個位元組表示一個碼點,英文用 1 位元組(相容 ASCII),中文常用 3 位元組,因此「字元數」與「位元組數」不同。

字串則是字元的序列。在 Python 3 中,str 型別儲存的是碼點序列,可以視為不可變(immutable)的陣列;一旦建立就無法修改任何字元,只能產生新字串。這個不可變特性有重要含義:重複以 += 拼接字串可能導致 O(n²) 的時間成本,正確做法是先把片段放進串列,最後用 join 一次串接,時間為 O(n)。

演算法步驟:高效字串拼接#

  1. 建立空串列 parts。
  2. 逐次將每個片段 append 到 parts(均攤 O(1))。
  3. 使用 ''.join(parts) 產生最終字串(O(n))。
  4. 總時間複雜度 O(n),遠優於逐次 += 的最壞 O(n²)。

Python 程式碼範例#

# --- 字元與碼點 ---
ch = '中'
print(ord(ch))              # 20013,即 U+4E2D 的十進位
print(chr(20013))           # '中'
print(ch.encode('utf-8'))   # b'\xe4\xb8\xad',3 個位元組

# --- 字元數 vs 位元組數 ---
s = "Hello中文"
print(len(s))               # 7(字元數)
print(len(s.encode('utf-8')))  # 13(位元組數)

# --- 拼接:壞做法 vs 好做法 ---
def bad_concat(items):
    s = ""
    for it in items:
        s += str(it)        # 每次都可能複製整個字串
    return s

def good_concat(items):
    parts = [str(it) for it in items]
    return ''.join(parts)   # O(n)

data = list(range(100000))
import time
t0 = time.time(); bad_concat(data); t1 = time.time()
good_concat(data); t2 = time.time()
print(f"+= : {t1-t0:.4f}s   join: {t2-t1:.4f}s")

# --- 常用字串操作 ---
text = "演算法演算法"
print(text.count("演算法"))   # 2
print(text.replace("演算法", "algorithm"))
print(text[::-1])             # 反轉:法算演法算演
print("abc".upper(), "ABC".lower())
print("  hi  ".strip())

複雜度分析#

假設字串長度為 n。以索引存取單一字元為 O(1);len 為 O(1);count、find、反轉等需要掃描整個字串,為 O(n);join 為 O(n);而用 += 迴圈拼接 n 個片段,每次拼接平均複製一半舊內容,最壞總成本為 O(n²)。辨識這些差異,是撰寫高效文字程式的第一步。

練習#

  1. 寫一個函式 byte_len(s),回傳字串 s 以 UTF-8 編碼後的位元組數,並用「Hello 世界」測試。
  2. 寫一個函式,不使用 [::-1] 或 reversed,自行用迴圈反轉字串,並說明其複雜度。
  3. 產生一個長度 10 萬的字元清單,分別用 += 與 join 拼接並計時,驗證兩者時間差是否達到數量級的差距。