標籤:style http color 使用 strong ar 檔案 資料 art
正式內容開始之前,我們先來瞭解一個基本概念,編碼字元集。
編碼字元集:編碼字元集是一個字元集,它為每個字元分配一個唯一數字。Unicode 標準的核心是一個編碼字元集,字母“A”的編碼為 004116 和字元“”的編碼為 20AC16。Unicode 標準始終使用十六進位數字,並且在書寫時在前面加上首碼“U+”,所以“A”的編碼書寫為“U+0041”。
1 ASCII碼
我們知道,在電腦內部,全部的資訊終雩都表示為一個二進位的字串。每個二進位位(bit)有0和1兩種狀態,因此八個二進位位就能夠組合出256種狀態,這被稱為一個位元組(byte)。也就是說,一個位元組一共能夠用來表示256種不同的狀態,每個狀態相應一個符號,就是256個符號,從0000000到11111111。
上個世紀60年代,美國制定了一套字元編碼,對英語字元與二進位位之間的關係,做了統一規定。這被稱為ASCII碼,一直沿用至今。
ASCII碼一共規定了128個字元的編碼(準確地說ASCII碼是一個編碼字元集),比方空格“SPACE”是32(二進位00100000),大寫的字母A是65(二進位01000001)。這128個符號(包含32個不能列印出來的控制符號),僅僅佔用了一個位元組的後面7位,最前面的1位統一規定為0。後128個稱為擴充ASCII碼,眼下很多基於x86的系統都支援使用擴充ASCII碼。
256個ASCII碼中的後128個擴充碼可定製用來表示特殊字元和非英語字元,GB2312就是利用這後面的128個擴充字元來表示漢字,[161,254]共94個字元來組成雙位元組來表示簡體漢字字元表。
2 Unicode編碼字元集
光是英語字元ASCII編碼字元集是夠了,可是假設算上世界上其它的語言的字元,ASCII碼顯然不夠了,於是Unicode編碼字元集應運而生。
Unicode用數字0-0x10FFFF來映射這些字元,最多能夠容納1114112個字元,或者說有1114112個碼位。碼位就是能夠分配給字元的數字。UTF-8、UTF-16、UTF-32都是將數字轉 換到程式資料的編碼方案。
3 UTF-8
http://zh.wikipedia.org/wiki/UTF-8
Unicode編碼字元集僅僅是統一定義了全部字元和它相應Unicode編碼值,而我們的程式中怎麼去儲存和讀取這個Unicode編碼值呢?顯然,你能夠直接統一規定全部Unicode編碼值用四個位元組來儲存。可是這種話,對於Unicode編碼字元集中的與ASCII碼錶相應的那部分字元(僅僅須要一個位元組來表示的Unicode編碼值)就有點浪費了。這樣,utf-8也就粉墨登場了。
UTF-8最大的一個特點,就是它是一種變長的編碼方式。它能夠使用1~4個位元組表示一個符號,依據不同的符號而變化位元組長度。
Unicode符號範圍 | UTF-8編碼方式
(十六進位) | (二進位)
--------------------+---------------------------------------------
0000 0000 ~ 0000 007F | 0xxxxxxx --------7bit
0000 0080 ~ 0000 07FF | 110xxxxx 10xxxxxx -------11bit
0000 0800 ~ 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx -----16bit
0001 0000 ~ 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx ----21bit
UTF-8編碼規則:
1> 對於Unicode編碼值的二進位位元小於等於7的情況,用一個位元組來表示這個Unicode編碼值,位元組的第一位設為0,後面7位為這個符號的unicode碼。因此對於英語字母,UTF-8編碼和ASCII碼是同樣的。
2> 對於Unicode編碼值的二進位位元大於等於8而且小於等於11的情況,用兩個位元組來表示,第一個位元組的前兩位都設為1,第兩+1位設為0,第二個位元組的前兩位設為10。剩下的沒有提及的二進位位,所有填充這個符號相應的unicode碼。
3> 以此類推,對於須要n個utf-8 位元組來表示的的符號(n>1),第一個位元組的前n位都設為1,第n+1位設為0,後面位元組的前兩位一律設為10。剩下的沒有提及的二進位位,所有填充這個符號相應的unicode碼。
utf-8編碼長度最大為四個位元組,所以最多僅僅能表示Unicode編碼值的位元為21位的Unicode字元。
4. UTF-16
http://zh.wikipedia.org/wiki/UTF-16
| 16進位編碼範圍 |
UTF-16表示方法(二進位) |
10進位碼範圍 |
位元組數量 |
| U+0000---U+FFFF |
xxxxxxxx xxxxxxxx |
0-65535 |
2 |
| U+10000---U+10FFFF |
110110yyyyyyyyyy 110111xxxxxxxxxx |
65536-1114111 |
4 |
UTF-16比起UTF-8,優點在於大部分字元都以固定長度的位元組(2位元組)儲存----0號平面(包括全部主要的字元)都在此表示範圍,但UTF-16卻無法相容於ASCII編碼.
UTF-16編碼以16位不帶正負號的整數為單位。我們把Unicode 編碼記作U。編碼規則例如以下:
假設U<0x10000,U的UTF-16編碼就是U相應的16位不帶正負號的整數(為書寫簡便,下文將16位不帶正負號的整數記作WORD)。
假設U≥0x10000,我們先計算U‘=U-0x10000,然後將U‘寫成二進位形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16編碼(二進位)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。
為什麼U‘能夠被寫成20個二進位位?Unicode的最大碼位是0x10ffff,減去0x10000後,U‘的最大值是0xfffff,所以肯定能夠用20個二進位位表示。比如:Unicode編碼0x20C30,減去0x10000後,得到0x10C30,寫成二進位是:0001 0000 1100 0011 0000。用前10位依次替代模板中的y,用後10位依次替代模板中的x,就得到:1101100001000011 1101110000110000,即0xD843 0xDC30。
依照上述規則,Unicode編碼0x10000-0x10FFFF的UTF-16編碼有兩個WORD,第一個WORD的高6位是110110,第二個WORD的高6位是110111。可見,第一個WORD的取值範圍(二進位)是11011000 00000000到11011011 11111111,即0xD800-0xDBFF。第二個WORD的取值範圍(二進位)是11011100 00000000到11011111 11111111,即0xDC00-0xDFFF。
為了將一個WORD的UTF-16編碼與兩個WORD的UTF-16編碼區分開來,Unicode編碼的設計者將0xD800-0xDFFF保留下來,並稱為代理區(Surrogate):
D800-DB7F ║ High Surrogates ║ 高位替代
DB80-DBFF ║ High Private Use Surrogates ║ 高位專用替代
DC00-DFFF ║ Low Surrogates ║ 低位替代
高位替代就是指這個範圍的碼位是兩個WORD的UTF-16編碼的第一個WORD。低位替代就是指這個範圍的碼位是兩個WORD的UTF-16編碼的第二個WORD。那麼,高位專用替代是什麼意思?我們來解答這個問題,順便看看怎麼由UTF-16編碼推導Unicode編碼。
假設一個字元的UTF-16編碼的第一個WORD在0xDB80到0xDBFF之間,那麼它的Unicode編碼在什麼範圍內?我們知道第二個WORD的取值範圍是0xDC00-0xDFFF,所以這個字元的UTF-16編碼範圍應該是0xDB80 0xDC00到0xDBFF 0xDFFF。我們將這個範圍寫成二進位:
1101101110000000 11011100 00000000 - 1101101111111111 1101111111111111
依照編碼的相反步驟,取出高低WORD的後10位,並拼在一起,得到
1110 0000 0000 0000 0000 - 1111 1111 1111 1111 1111
即0xe0000-0xfffff,依照編碼的相反步驟再加上0x10000,得到0xf0000-0x10ffff。這就是UTF-16編碼的第一個WORD在0xdb80到0xdbff之間的Unicode編碼範圍,即平面15和平面16。由於Unicode標準將平面15和平面16都作為專用區,所以0xDB80到0xDBFF之間的保留碼位被稱作高位專用替代。
5. 標準Unicode編碼錶分析
Unicode字元平面映射:
http://zh.wikipedia.org/wiki/Unicode%E5%AD%97%E7%AC%A6%E5%B9%B3%E9%9D%A2%E6%98%A0%E5%B0%84
完整的Unicode編碼錶可見連結:http://zh.wikibooks.org/wiki/Unicode
眼下的 Unicode 字元分為 17 組編排, 每組稱為平面(Plane),而每平面擁有65536(即 216)個代碼點。然而眼下僅僅有少數平面被使用。
上述的Unicode編碼錶連結中僅僅列出了少數幾個已經被使用的平面。
| U+ |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
A |
B |
C |
D |
E |
F |
| 0000 |
NUL |
SOH |
STX |
ETX |
EOT |
ENQ |
ACK |
BEL |
BS |
HT |
LF |
VT |
FF |
CR |
SO |
SI |
| 0010 |
DLE |
DC1 |
DC2 |
DC3 |
DC4 |
NAK |
SYN |
ETB |
CAN |
EM |
SUB |
ESC |
FS |
GS |
RS |
US |
| 0020 |
SP |
! |
" |
# |
$ |
% |
& |
‘ |
( |
) |
* |
+ |
, |
- |
. |
/ |
| 0030 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
: |
; |
< |
= |
> |
? |
| 0040 |
@ |
A |
B |
C |
D |
E |
F |
G |
H |
I |
J |
K |
L |
M |
N |
O |
| 0050 |
P |
Q |
R |
S |
T |
U |
V |
W |
X |
Y |
Z |
[ |
\ |
] |
^ |
_ |
表分為橫豎兩列,相當於x和y確定唯一的Unicode的值(十六進位)。如:
ESC字元由x座標0010和y座標B確定,那麼它的Unicode編碼值就是0010 + B = 001B
表就是這麼讀的啦。
6. UTF-8和UTF-16位元組序的問題
http://zh.wikipedia.org/wiki/UTF-8
http://zh.wikipedia.org/wiki/UTF-16
網上查了下,對於這二者的位元組序的原因非常表面。位元組序的介紹例如以下:
UTF-8以位元組為編碼單元,沒有位元組序的問題。UTF-16以兩個位元組為編碼單元,所以有位元組序的問題。
那為什麼以位元組為編碼單元的utf-8就沒有位元組序的問題呢?它也能夠有三個或四個位元組組成呀。我想非常多人都會這麼問的,也包含我自己。讓我們再回頭來看看位元組序是什麼東西,對於單個位元組長度的變數類型來說,它是沒有位元組問題的。對於多位元組長度的變數類型才有位元組問題。
假設牽涉到在多個CPU之間傳輸數字問題.如通過網路,檔案和匯流排傳輸數位話.必需要考慮到數字位元組序問題。網路傳輸時,不同位元組序的平台進行資料轉送時,在發送資料之前都必須轉換成為網路位元組序後再進行傳輸,即統一用的網路位元組序(即大頭)來傳輸。接收方依據自己cpu的位元組序將網路位元組序轉換為本地位元組序,這樣一來,接收方按再依照自己的位元組序去讀取這個資料時和發送方讀取的是一樣的。因此,對於網路傳輸,網路傳輸這一層面的位元組序問題已經解決,Unicode編碼層面的位元組序問題與以下的檔案儲存體中的一樣。匯流排,這個是同一平台多cpu(各cpu的位元組序不一樣)之間的資料交換的情況,我想我們基本上是接觸不到這樣的情況了,不做研究之列。剩下就是個通過檔案資料轉送的情況,即一個平台上的檔案在另外一個不同位元組序的平台上開啟。這樣的情況怎麼考慮位元組序的問題呢?
再回過頭來細緻分析"UTF-8以位元組為編碼單元,沒有位元組序的問題。UTF-16以兩個位元組為編碼單元,所以有位元組序的問題。"這句話的含義了。什麼是編碼單元?Unicode僅僅是一個符號集,依據UTF-8或是UTF-16能夠計算出某個符號相應的的唯一的二進位代碼,可是這個二進位的代碼詳細怎麼儲存的呢,這個編碼規則中都沒有指定。可是UTF-8指定了,儲存這個二進位代碼時,必須按位元組為單元來讀和儲存;UTF-16指定了,儲存這個二進位代碼時,必須按兩個位元組為單元來讀和儲存。這個時候我們應該知道事實上編碼單元,就是某種編碼方式中,最小讀取和儲存其位元值的單元。呵呵,明確這個非常重要哦。
不管是UTF-8或UTF-16,其終於的二進位編碼長度都有可能是大於等於兩個位元組的。可是UTF-8是依照位元組為編碼單元,所以儲存時是依照其編碼順序(位元值從左至右)的順序來儲存的。所以讀取檔案時,依照檔案裡儲存順序一個位元組一個位元組讀取就能得到和儲存時一樣的二進位編碼了,也就不須要考慮位元組序了。而UTF-16是依照兩個位元組為編碼單元的,它的二進位編碼僅僅有兩個位元組或四個位元組這兩種長度。所以儲存時,依照編碼順序(位元值從左至右),一次儲存兩個位元組到檔案,直到所有存完。這樣兩個位元組與其下一個兩個位元組之間的儲存順序也是與二進位編碼順序一致的。可是作為編碼單元的這兩個位元組本身的順序怎麼放呢?UTF-16是沒有指定的,你能夠依據自己的喜好人為地按大頭來存放這兩個位元組,也能夠依照小頭來存放。所以你依照UTF-16編碼後儲存的檔案,別人去讀你這個檔案裡的內容時,是依照大頭還是小頭去讀這個編碼單元呢?
假設還有那麼一點暈的話,我再舉個範例了:
如果要將 U+64321 (16進位) 轉成 UTF-16 編碼. 由於它超過 U+FFFF, 所以他必須編譯成32位(4個byte)的格式,例如以下所看到的:11
V = 0x64321
Vx = V - 0x10000
= 0x54321
= 0101 0100 0011 0010 0001
Vh = 01 0101 0000 // Vx 的高位部份的 10 bits
Vl = 11 0010 0001 // Vx 的低位部份的 10 bits
w1 = 0xD800 //結果的前16位元初始值
w2 = 0xDC00 //結果的後16位元初始值
w1 = w1 | Vh
= 1101 1000 0000 0000
| 01 0101 0000
= 1101 1001 0101 0000
= 0xD950
w2 = w2 | Vl
= 1101 1100 0000 0000
| 11 0010 0001
= 1101 1111 0010 0001
= 0xDF21
所以這個字 U+64321 最後正確的 UTF-16 二進位編碼應該是:
1101 1001 0101 00001101 1111 0010 0001
綠色部分為高16位,紅色為低16位。
由於UTF-16 的編碼單元為兩個位元組,所以儲存時,依照二進位編碼順序,先儲存高位的兩個位元組1101 10010101 0000 ,然後再儲存地位的兩個位元組1101 1111 0010 0001。
可是兩個位元組之間的順序是什麼呢?順序就是先儲存兩個位元組中的"低地址"的那個位元組,然後再儲存高地址的那個位元組。
假設是小頭順序(最低位元組在最低位,最高位元組在最高位),則"低地址"中儲存的是低位元組。所以,在高位的兩個位元組中,先儲存最低位的位元組50,再儲存最高位的位元組D9,即十六進位格式儲存為50D9 ;同理,低位的兩個位元組的儲存順序就是21DF。四個位元組連起來,儲存內容就是50D9 21DF。
假設是大頭順序(最高位元組在地址最低位,最低位元組在地址最高位),則"低地址"中儲存的是高位元組。所以,高位連個位元組先儲存最低位的位元組D9,再儲存最高為的位元組50 ----D950
同理,低位的兩個位元組的儲存順序就是DF21。四個位元組連起來,儲存內容就是D950 DF21。
7. UTF-8和UTF-16位元組序的問題的相應解決方式
經過上述的分析,相信大家應該清楚了位元組序的問題了。那麼怎樣讓別的程式讀取你寫的檔案時知道你儲存時的位元組序呢?
Unicode規範中定義,每個檔案內容的最前面添?一個表示編碼順序的字元,這個字元的名字叫做”零寬度非換行空格“(ZERO WIDTH NO-BREAK SPACE),這個字元相應的Unicode字元的編碼值為FEFF。所以對於UTF-16,假設你寫的時候使用小頭順序,則這個字元在檔案裡的儲存順序為FFFE;假設是大頭順序,則這個字元的儲存順序為FEFF。
UTF -8本來是與位元組序無關的,不須要指定編碼位元組序,可是能夠用BOM(Byte Order Mark)來表明編碼方式。字元"ZERO WIDTH NO-BREAK SPACE"的UTF-8編碼是EF BB BF,由於是以位元組為編碼單中繼存放區的,所以這個字元檔案裡相應的儲存格式與編碼格式一樣。依據這一點,我們知道假設檔案內容以EF BB BF開頭,就知道這是UTF-8編碼了。
8. 不同編碼方式的轉換
windows下,記事本中,支援將檔案裡的內容以不同的編碼方式儲存。
ANSI是記事本中預設的編碼方式-----對於英文檔案是ASCII編碼,對於中文簡體檔案是GB2312編碼(僅僅針對Windows中文簡體版,假設是繁體中文版會採用Big5碼)。
記事本也支援utf-8格式的,通過依次採用ANSI 和 UTF-8編碼方式儲存,我們能夠看到這兩種編碼方式之間的轉換。用文本編輯軟體UltraEdit中的十六進位格式,觀察該檔案的不同編碼方式相應的值。
9 讀寫文字檔時,程式時怎樣推斷檔案的編碼格式
1. 首先,程式通過通過推斷檔案頭幾個位元組來推斷檔案的編碼格式(BOM位元組)
ANSI : 無格式定義;
Unicode : 前兩個位元組為 FFFE ;
Unicode big endian : 前兩位元組為 FEFF ;
UTF-8 : 前兩位元組為 EFBB ;
UTF-16 big endian的BOM: FF FE;
UTF-16 little endian的BOM: FE FF;
2. BOM不存在的情況判定。
UTF-8的判定,依據內容判定
UTF-8的編碼規則:
字元位元組長度 標誌位元組 值
一位元組長 0XXXXXXX
兩位元組長 110XXXXX 10XXXXXX
三位元組長 1110XXXX 10XXXXXX 10XXXXXX
四位元組長 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
**標誌位元組判定用到的資料**
定義數組btHead 長度為4儲存用來判定標誌位元組的十進位數值: 0,192, 224, 240
定義數組btBitAndValue 長度為4儲存用來得到標誌位元組長度的十進位數值:128: 224, 240, 248
**值判定用到的資料**
定義變數btValueHead用來儲存值得標誌所相應的十進位數值:128
定義變數btFixValueAnd 儲存用來取得值的標誌的十進位數值:192
a.以位元組方式讀取檔案裡內容儲存到位元組數組中
b.對a中讀取的檔案內容做loop操作。
首先對當前的位元組分別與btBitAndValue中的四個值進行位與操作,每次得到的值與btHead中的值比較,找到相等的值時能夠依據當前的 值來判定字元的位元組長度L。並運行下一個迴圈,在跳過b的操作次數(L - 1)次時在運行b操作
c.取得值的標誌。 將此致的值與btFixValueAnd進行位與操作,將取得的值與btValueHead進行比較,假設相等則對下一個位元組繼續運行c操作,直到啟動並執行次數是L-1次。假設不相等則說明不是UTF-8編碼格式。
UTF-16的判定與UTF-8的判定類似僅僅要知道編碼規則就能夠。
Unicode 字元集與它的編碼方式