Python: 關於Unicode 的BOM - 傑克! 真是太神奇了! - 痞客邦
文章推薦指數: 80 %
註一: 主要是因為可使用的編碼數只有256 個, 而不同code page 之間會對應不同的符號, 進而無法得知資訊的原始樣貌. 關於Unicode 的BOM (Byte Order Mark) ...
傑克!真是太神奇了!
跳到主文
記性不好,寫程式,架主機...都需要看小抄!
歡迎光臨MagicJack在痞客邦的'小抄'天地
部落格全站分類:數位生活
相簿
部落格
留言
名片
公告版位
從小害怕寫作文,文筆不佳到現在,還請各位讀者大大:
1.發現有錯誤,請留言告知.(或者你'覺得'不對也行)
2.用字措辭不當,請留言告知.
3.有看沒有懂?幫到忙也好,幫倒忙也罷,總之留個言吧.
Dec16Thu202111:32
Python:關於Unicode的BOM
Unicode的誕生背景
最早電腦使用的字元編碼都是ASCII.ASCII作為美國的計算機編碼標準,理所當然的只包含了英語的26字母大小寫,再加上一些常用符號.後來因應一些非英語系國家/地區需要,IBM將ASCII編碼擴充加入他們各自所需要的一些特殊字元,是為codepage.例如:codepage437是原始的英文頁碼;codepage858是帶有歐元符號的多語言頁碼(我們用的正體中文是codepage950).所以DOS,Windows作業系統上有chcp指令可以查訽/切換不同的codepage.
但是這只解決了區域性的交流問題.國際交流上的問題依然存在(註一).例如:亞洲語系的問題,CJK字元編碼統一的問題...於是有了Unicode聯盟,致力於制定標準,並努力的將全球各種文字符號的編碼統一,以利於資訊的交流.
註一:主要是因為可使用的編碼數只有256個,而不同codepage之間會對應不同的符號,進而無法得知資訊的原始樣貌.
關於Unicode的BOM(ByteOrderMark)
BOM(ByteOrderMark)字面意思是位元組順序記號(註二,註三).那為什麼Unicode會有BOM的定義?又為什麼需要BOM呢?
這是因為外部存儲體(RAM,ROM,flash...)的一直是以8位元為單位來定址,但現在的CPU早就已經不再只是8位元的,而是16位元,32位元甚至是64位元CPU了.這些大於8位元的CPU對於大於8位元的資料如何擺放在外部儲存體中卻有二種不同的主要流派:
小端序(Little-Endian):將資料依其位元組的數量級由小至大擺放在外部儲存體地址也是由小至大.
大端序(Big-Endian):將資料依其位元組的數量級由小至大擺放在外部儲存體地址卻是反過來由大至小.
這二大Endian各自都有支持的理由.不過幾十年下來,目前除了TCP/IP是Big-Endian之外,許多(軟體/硬體)協定都徧向採用Little-Endian(註四).Unicode不像TCP/IP或者其他協定只能用規定的endian進行資料交換,而是設計了BOM,讓各個系統可以個自以最方便的方法使用Unicode,但又可以解決不同endian系統之間的資訊交換.
Unicode基本編碼為16bits大小,後來擴充為32bits(目前尚未流行)Unicode的編碼空間從U+0000到U+10FFFF,共有1,112,064個碼位(codepoint).為了讓資料可以順利在不同Endian取向的CPU之間交換,Unicode編碼的檔案最前頭先寫入一個數值為65,279(16bits格式:0xFEFF或32bits格式:0x0000FEFF)的BOM(ByteOrderMark).
要注意的是:端序問題只是資料進出(I/O)時的問題.在CPU內部不論是你是大端序機器或是小端序機器,UnicodeBOM的數值固定為65,279:16bits格式0xFEFF;32bits格式0x0000FEFF.
所以,以16bits格式為例:0xFEFF的MSB(最高數量級位元組)為0xFE,LSB(最低數量級位元組)為0xFF.想當然的小端序的機器將UTF-16BOM寫到檔案時,第一個位元組是LSB0xFF,此即為UTF-16-LE(小端序)編碼.反之大端序的機器將UTF-16BOM寫到檔案時,第一個位元組是MSB0xFE,所以就變成UTF-16-BE(大端序)編碼.這樣子資料檔案要在各種電腦之間交換時,就可以判定資料是不是和本機的Endian相同.如果不同就需要將資料的高低位元組交換一下,才能正確解碼.
不同Unicode編碼的BOM
編碼十六進位表示
UTF-8EFBBBF
UTF-16-LEFFFE
UTF-16-BEFEFF
UTF-32-LEFFFE0000
UTF-32-BE0000FEFF
UTF-8的編碼規則和BOM
至於UTF-8編碼:是將Unicode編碼的字串資料轉成8位元序列(轉換規則如下表:UTF-8編碼規則),所有機器都必需一個位元組接著一個位元組的讀(地址由小到大),所以不存在大小端的問題.還有為何UTF-8的BOM會變成EFBBBF.其實它依然是數值65,279(0xFEFF)編碼成UTF-8的結果.並不是什麼特別的設定.
UTF-16BOM與UTF-8BOM互轉
UTF-16十六進位值FEFF
二進位值1111111011111111
UTF-8二進位值111011111011101110111111
十六進位值EFBBBF
UTF-8編碼規則
CodePointRangebitsByte1Byte2Byte3Byte4Byte5Byte6
U+0000~ U+007F7
0xxxxxxx00~7F
U+0080~ U+07FF11
110xxxxxC0~DF
10xxxxxx80~BF
U+0800~ U+FFFF16
1110xxxxE0~EF
10xxxxxx80~BF
10xxxxxx80~BF
U+10000~ U+1FFFFF21
11110xxxF0~F7
10xxxxxx80~BF
10xxxxxx80~BF
10xxxxxx80~BF
U+200000~ U+3FFFFFF26
111110xxF8~FB
10xxxxxx80~BF
10xxxxxx80~BF
10xxxxxx80~BF
10xxxxxx80~BF
U+4000000~U+7FFFFFFF31
1111110xFC~FD
10xxxxxx80~BF
10xxxxxx80~BF
10xxxxxx80~BF
10xxxxxx80~BF
10xxxxxx80~BF
Unicode基本頁面(BasicMultilingualPlane,0x0~0xFFFF)的字符轉UTF-8可以3bytes以內完成.
輔助頁面(SupplementaryPlanes,0x10000~0x10FFFF)的字符才會動用到4bytes編碼.
5~6byte編碼基本上目前用不上.
註二:位元組順序記號這個字面翻譯有點小拗口,個人認為改稱之為端序記號似乎好些.你認為呢?
註三:你可能會看到有些簡体中文的網站把它翻譯料表,物料清單或材料清單,這應該是機器翻譯把它誤認為另一個BOM(BillOfMaterial).不要說我黑白講,這幾個例子就是:https://tech-wiki.online/cn/javascript-unicode.html(誤值為物料清单字符),https://cloud.tencent.com/developer/section/1125377(誤值為物料清单和材料清单)
註四:參看維基百科位元組順序.
UTF-16的編碼規則
由於Unicode的編碼空間從U+0000到U+10FFFF,共有1,112,064個碼位(codepoint),超出16位元可以表示的範圍.因此UTF-16將輔助平面字符(SupplementaryPlanes),即超出0xFFFF的部份0x10000~0x10FFFF,以一對(二組)16位元資料來表示:第一組16位元編碼範圍0xD800~0xDBFF,加上第二組16位元編碼範圍0xDC00~0xDFFF此即所謂代理對(SurrogatePair).
為此Unicode也將區段0xD800~0xDFFF空下來不編碼.
代理對(SurrogatePair)的編碼方法如下:
將Unicode輔助平面字符的碼位(codepoint)減去0x10000,得到的數值範圍為20位元,並將之分為2組10位元.
(0x10000~0x10FFFF)-0x10000=0x0~0xFFFFF
高位的10位元的值(範圍為0x0~0x3FF)加上0xD800得到第一個碼元,稱作前導代理(leadsurrogate).
(0x0~0x3FF)+0xD800=0xD800~0xDBFF
低位的10位元的值(範圍為0x0~0x3FF)加上0xDC00得到第二個碼元,稱作後尾代理(trailsurrogate)
(0x0~0x3FF)+0xDC00=0xDC00~0xDFFF
UTF-16編碼規則
lead\trail
0xDC00
0xDC01
...
0xDFFF
0xD8000x100000x10001…0x103FF
0xD8010x104000x10401…0x107FF
0xD8020x108000x10801…0x10BFF
⋮⋮⋮⋱⋮
0xDBFF0x10FC000x10FC01…0x10FFFF
Python如何處理BOM
Python3在開啟檔案時(open())指定encoding參數可以讓我們選擇保留BOM(自己處理BOM的讀寫);或者也可以輕鬆點,讓系統自動處理BOM.
下列是我目前試過的Unicode相關的合法encoding參數值:(註五)
'utf-8':系統不自動處理BOM.(Python3預設值(註六))
讀檔時BOM被當作資料讀入.
寫檔時,要依據需求自己先寫入一個BOM(write('\ufeff')).如果寫檔時沒有寫BOM,檔案的開頭自然也就不會有BOM(即所謂的UTF-8noBOM編碼).
'utf-8-sig':系統自動處理BOM.
讀檔時,自動跳過BOM(有沒有BOM都OK)
寫檔時,自動加BOM(一定有)
'utf-16':系統自動處理BOM.
讀檔時,自動跳過BOM(有沒有BOM都OK)
寫檔時,自動加BOM(一定有,大小端序應該是依系統而定)
'utf-16-le':系統不自動處理BOM.
讀檔時BOM被當作資料讀入.
寫檔時,依據需求自己先寫入一個BOM(write('\ufeff')).
讀/寫時,Python字串以Little-Endian解/編碼.
'utf-16-be':系統不自動處理BOM.
讀檔時BOM被當作資料讀入.
寫檔時,依據需求自己先寫入一個BOM(write('\ufeff')).
讀/寫時,Python字串以Big-Endian解/編碼.
注意:有系統自動處理BOM功能的是:'utf-8-sig'和'utf-16',不要弄混了.
註五:當然,還有utf-32相關的三個'utf-32','utf-32-le','utf-32-be'合法設定值,但是我自己沒試過,可能要大家自行腦補一下.
註六:這個預設值在Windows平台,有點不同.它預設是使用的頁碼950(Windows繁體中文版系統預設值).我們需要自行在環境變數(系統或者使用者均可)新增一個環境變數PYTHONUTF8=1;或者使用python-xutf8來啟動python(即加上參數-xutf8).如此Python3才會將encoding參數預設改為'UTF-8'(即encoding='UTF-8').
另外,使用chcp65001指令並無法更動Python3預設的encoding設定.
不過有一個設定可以將你的命令提示字元,預設為使用65001頁碼;同時也會將Python3的encoding參數預設改為'UTF-8'.操作如下:打開控制台(如果沒有它的捷徑,在左下方的'搜尋欄'中輸入'控制台'也可以找到它),打開其中的'地區'設定.在'系統管理'頁籤中,點選下方的'變更系統地區設定(C)...'按鈕,開啟'地區設定'子視窗,並勾選'Beta:使用UnicodeUTF-8提供全語言支援(U)'.按下'確定'按鈕,然後重新開機即可.
控制台(請選擇'地區')
地區-系統管理設定畫面(點選'變更系統地區設定(C)...'按鈕)
地區設定子視窗(請勾選'Beta:使用UnicodeUTF-8提供全語言支援(U)')
Python3實例
廢話不多說,來看幾個實例吧.下面這個很直觀吧!?
xf3=open('./test-utf8.txt','w',encoding='utf-8')
xf3.write(u'\uFEFF')
xf3.write('UTF-8寫檔測試,自行加入utf-8BOM.\n')
xf3.close()
輸出檔案的內容如下:
python寫檔附加utf-8BOM
再來一個讀csv檔的例子.csv檔一般並沒有要求使用UnicodeBOM,但是MSExcel輸出的csv檔有BOM,讀檔時也要有BOM.下面這個例子用'utf-8-sig'來省略utf-8BOM的檢查(第12~13行).程式如下:
importcsv
importnumpyasnp
defdict_csvloader(path):
'''
Loadlinesintextfileasadict().
'''
keyDict=dict()
withopen(path,newline='',encoding='utf-8-sig')ascsvfile:
reader=csv.DictReader(csvfile)
headers=list(next(reader,None).keys())
#ifheaders[0][0]==u'\ufeff':
#headers[0]=headers[0][1:]
forrowinreader:
data=tuple(row.values())
ifdata[2]=='':
break
vstr=np.array(data[3:])
vstr[vstr=='']='0'
keyDict[data[2]]=vstr.astype(np.float32)
returnkeyDict,headers
nutriTbl,headers=dict_csvloader('./nutrition.csv')
資料檔nutrition.csv範例如下:
樣品編號,食品分類,樣品名稱,修正熱量(kcal),水分(g),粗蛋白(g),粗脂肪(g),總碳水化合物(g),膳食纖維(g),鈉(mg),鉀(mg),鈣(mg),鎂(mg),鐵(mg),鋅(mg),維生素A總量(IU),維生素E總量(mg),維生素B1(mg),維生素B2(mg),菸鹼素(mg),維生素B6(mg),維生素C(mg)
A0100101,穀物類,大麥仁,343,12.3,8.9,1.6,76.1,8.9,14,229,26,49,1.6,1.2,0,1.11,0.16,0.04,3.31,0.22,0.2
A0110101,穀物類,大麥片,352,12.1,8.6,1.8,76.7,6.0,7,246,13,55,2.2,0.8,0,0.26,0.15,0.04,3.23,0.18,9.8
A0120101,穀物類,大麥仁粉,374,5.7,7.1,1.3,85.0,7.2,10,307,25,50,1.1,1.3,0,0.12,0.09,0.03,2.85,0.23,0.0
追加註記
要是你不嫌麻煩,或者有點OO的潔癖:覺得直接用'\uFEFF'代表UnicodeBOM不對或者不夠道地.那可以考慮一下使用importcodecs引入codecs模組來使用下列常數:
codecs.BOM
codecs.BOM_BE
codecs.BOM_LE
codecs.BOM_UTF8
codecs.BOM_UTF16
codecs.BOM_UTF16_BE
codecs.BOM_UTF16_LE
codecs.BOM_UTF32
codecs.BOM_UTF32_BE
codecs.BOM_UTF32_LE
參考連結
我的貼文:Python:.py檔的編碼問題
文章標籤
unicode
BOM
unicodeBOM
utf-8BOM
endian
bigendian
littleendian
端序
大端序
小端序
encoding='UTF-8'
pythonencoding預設值
PYTHON環境變數
全站熱搜
創作者介紹
MagicJackTing
傑克!真是太神奇了!
MagicJackTing發表在痞客邦留言(0)人氣()
E-mail轉寄
全站分類:數位生活個人分類:python此分類上一篇:Python:.py檔的編碼問題
上一篇:Python:.py檔的編碼問題
下一篇:CSS:關於pseudo-class:not()
▲top
留言列表
發表留言
月曆
«
十月2022
»
日
一
二
三
四
五
六
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
熱門文章
最新文章
文章搜尋
文章分類
程式(4)
python(7)git(3)Java(0)CLanguage(15)
嵌入式系統(6)
KeilARMC(2)Arduino(3)KeilC51(2)GCC(3)OS(EmbeddedSystem)(3)硬體(9)
網頁(3)
JavaScript(4)CSS(14)HTML(3)
Windows(10)其他(6)部落格設定(6)
最新留言
新聞交換(RSS)
誰來我家
我的連結
QRCode
POWEREDBY
(登入)
回到頁首
回到主文
免費註冊
客服中心
痞客邦首頁
©2003-2022PIXNET
關閉視窗
PIXNET
Facebook
Yahoo!
Google
MSN
{{guestName}}
(登出)
您尚未登入,將以訪客身份留言。
亦可以上方服務帳號登入留言
請輸入暱稱(最多顯示6個中文字元)
請輸入標題(最多顯示9個中文字元)
請輸入內容(最多140個中文字元)
請輸入左方認證碼:
看不懂,換張圖
請輸入驗證碼
送出留言
延伸文章資訊
- 1「帶BOM 的UTF-8」和「無BOM 的UTF-8」有什麼區別
utf-16才需要加bom。因為它是按unicode順序編碼,在BMP範圍內是二位元組,需要識別是大或小位元組序。
- 2What's the difference between UTF-8 and UTF-8 with BOM?
The Unicode Standard permits the BOM in UTF-8, but does not require or recommend its use. Byte or...
- 3「带BOM 的UTF-8」和「无BOM 的UTF-8」有什么区别?网页 ...
UTF-8 不需要BOM,尽管Unicode 标准允许在UTF-8 中使用BOM。 所以不含BOM 的UTF-8 才是标准形式,在UTF-8 文件中放置BOM 主要是微软的习惯(顺便提一下:把带...
- 4位元組順序記號 - 维基百科
位元組順序記號(英語:byte-order mark,BOM)是位於碼點 U+FEFF 的統一碼字符的名称。 ... 每個Unicode編碼(包括Unicode標準以外的編碼,如UTF-7,見下...
- 5unicode-bom - ESLint - Pluggable JavaScript Linter
The Unicode Byte Order Mark (BOM) is used to specify whether code units are big endian or little ...