iT邦幫忙

4

使用 Python 解析中文內碼

  • 分享至 

  • xImage
  •  

前言

本文分享一些中文內碼的心得:

  1. 取得中文的Big5及UTF-8內碼。
  2. 顯示所有中文字。
  3. 有些字不會念,如何取得注音?

中文編碼

中文編碼五花八門,以前有IBM主機編碼、倚天編碼、資策會 Big5、CNS中文標準交換碼、萬國碼(UTF)...等,另外,還有一些擴編的變形,現在主流的編碼是UTF-8,它涵蓋全世界大部分的語文,因此,作業系統編碼大部分是以UTF-8為預設值,以Windows作業系統為例,可使用 MS Word 查到UTF-8內碼,選擇『插入』>『符號』>『其他符號』,點選任何一個字,例如下圖,『一』的內碼是0x4e00,0x代表16進位,4e00是16進位的內碼值。
https://ithelp.ithome.com.tw/upload/images/20230410/20001976OmBNHcaCe1.png
圖一. Windows 內碼查詢

Python同時支援Big5、UTF-8編碼,也支援大陸的GBK編碼,我們來小玩一下:

  1. 啟動python,輸入下列程式碼。
ord('一') # output:19968
  1. 會得到19968,再利用hex,可轉為16進位的內碼。
hex(ord('一')) # output:0x4e00

果然是0x4e00,與圖一相符。可以驗算一下。

# 驗算 0x4e00 = ?
4*16**3+14*16**2 # output:19968
  1. 也可以使用格式化,將十進位轉為十六進位。
f"{ord('一'):x}" # output:4e00

讀者可以試試其他的字,例如日文ぁ,內碼為0x3041。

  1. 反之,輸入內碼,顯示字符。
chr(19968) # output:'一'

或是

chr(0x4e00) # output:'一'
  1. 顯示中文前10個單字。
for i in range(10):
    print(chr(ord('一')+i), end='')
# 一丁丂七丄丅丆万丈三
  1. 顯示日文50音含濁音。
for i in range(83):
    print(chr(ord('ぁ')+i), end='')
# ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをん    

UTF-8 碼位(Code point)

除了ord、chr函數外,encoding/decoding更可以支援各種各種編碼的轉換,但他們有點複雜,以UTF-8為例,每個字的內碼長度是不固定的,可以是1~4 bytes,ASCII code前128個字符是1個byte,而漢字大部分是3個byte,如下圖,每個byte前面都有固定的bit,以識別編碼。
https://ithelp.ithome.com.tw/upload/images/20230410/20001976igXKW0hmSs.png
圖二. UTF-8 碼位,圖片來源:維基百科 UTF-8

以下就來實驗encode/decode用法。

  1. 以encode取得UTF-8內碼。
'一'.encode('utf8') # output:b'\xe4\xb8\x80'

'一'的內碼果然是3個byte。

  1. encode與ord('一')得到的內碼為何不同呢? 因為UTF-8每個byte前面都有固定的bit,需去除掉,才能轉為整數。先轉為二進位看看:
f'{0xe4b880:b}' # output:'111001001011100010000000'

仔細比對一下,固定的bit如下圖的框,與圖二相符。
https://ithelp.ithome.com.tw/upload/images/20230410/20001976yuGNMgijtc.png
圖三. 固定的bits

  1. 去掉固定的bits,將剩餘的bits轉為十進位。
x = b'111001001011100010000000'
int(x[4:8] + x[10:16] + x[-6:], 2) # output:19968

與ord('一')結果相同,真的成功看懂UTF-8文件了。瞭解這個,我們要進行文件轉碼,就沒有問題了。

  1. 直接將以上步驟合而為一。
x = f"{int.from_bytes('一'.encode('utf8'), 'big'):b}"
int(x[4:8] + x[10:16] + x[-6:], 2) # output:19968
  1. encode與decode可以進行多個單字的轉碼。
'一二三四'.encode('utf8') # output:b'\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89\xe5\x9b\x9b'
  1. encode後再decode,內容不變。
'一二三四'.encode('utf8').decode('utf8') # output:'一二三四'

Big5 編碼

使用爬蟲,有時候抓回來的網頁出現亂碼,通常是因為該網頁為Big5編碼,可以使用下列指令轉為字串。

b'\xa4@\xa4G\xa4T\xa5|'.decode('big5') # output:'一二三四'

Big5的中文內碼一律採用2 bytes,範圍在0xa440~0xf9d5之間,而且中間有空隙。

應用

  1. 顯示所有中文字:由於UTF-8是中、日、韓混合編碼(CJK),因此,以Big5列印會比較簡單。筆者自CNS11643中文標準交換碼全字庫取得CNS2BIG5.txt,該檔含所有中文內碼。
# 讀取 CNS2BIG5.txt
import pandas as pd

df = pd.read_csv('./CNS2BIG5.txt', sep='\t', header=None, names=['cns', 'big5'])
df.head()

顯示100個中文字:

for no, i in enumerate(df['big5'].values):
    if no > 100: break # 顯示100個中文字
    print(int(i, 16).to_bytes(2, 'big').decode('big5'), end='')

輸出結果:
一乙丁七乃九了二人儿入八几刀刁力匕十卜又三下丈上丫丸凡久么也乞于亡兀刃勺千叉口土士夕大女子孑孓寸小尢尸山川工己已巳巾干廾弋弓才丑丐不中丰丹之尹予云井互五亢仁什仃仆仇仍今介仄元允內六兮公冗凶分切刈勻勾勿化

如要顯示所有中文字,可將第2行刪除。

  1. 顯示某個字的注音:假設看到難字,不知怎麼發音,例如『埠』,可以自CNS11643中文標準交換碼全字庫取得CNS_phonetic.txt,該檔含所有中文注音。需與CNS2BIG5.txt合併,找到中文內碼與注音對照表。
# 合併CNS_phonetic.txt、CNS2BIG5.txt,找到中文內碼與注音對照表
df_all = df.merge(df2,  on='cns')
df_all.head()

查詢『埠』:

x = f"{int.from_bytes('埠'.encode('big5'), 'big'):x}".upper()
df_all.query('big5 == @x')

執行結果:
https://ithelp.ithome.com.tw/upload/images/20230410/20001976Fd33JCXqTF.png

查詢『熵』:

x = f"{int.from_bytes('熵'.encode('big5'), 'big'):x}".upper()
df_all.query('big5 == @x')

執行結果:
https://ithelp.ithome.com.tw/upload/images/20230410/20001976fZRflD3YCd.png

事實上,『熵』應念為第2個,音同『商』。

延伸

還可以作更多的延伸,例如:

  1. 製作一個網頁,提供難字的注音查詢。
  2. 判斷一個檔案是Big5或UTF-8編碼?
  3. 提供難字的倉頡查詢,可提供倉頡輸入法的入門。
  4. 繁/簡轉碼、Big5/UTF-8轉碼。
  5. 另外要注意的是,如果使用open()讀取檔案(read),預設是以Big5讀取檔案內容,寫入(write)也是如此。

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言