字符,字節(jié)和編碼(一)
2006-12-25 02:43

級(jí)別:中級(jí)

摘要:本文介紹了字符與編碼的發(fā)展過(guò)程,相關(guān)概念的正確理解。舉例說(shuō)明了一些實(shí)際應(yīng)用中,編碼的實(shí)現(xiàn)方法。然后,本文講述了通常對(duì)字符與編碼的幾種誤解,由于這些誤解而導(dǎo)致亂碼產(chǎn)生的原因,以及消除亂碼的辦法。本文的內(nèi)容涵蓋了“中文問(wèn)題”,“亂碼問(wèn)題”。

掌握編碼問(wèn)題的關(guān)鍵是正確地理解相關(guān)概念,編碼所涉及的技術(shù)其實(shí)是很簡(jiǎn)單的。因此,閱讀本文時(shí)需要慢讀多想,多思考。

引言

“字符與編碼”是一個(gè)被經(jīng)常討論的話題。即使這樣,時(shí)常出現(xiàn)的亂碼仍然困擾著大家。雖然我們有很多的辦法可以用來(lái)消除亂碼,但我們并不一定理解這些辦法的內(nèi)在原理。而有的亂碼產(chǎn)生的原因,實(shí)際上由于底層代碼本身有問(wèn)題所導(dǎo)致的。因此,不僅是初學(xué)者會(huì)對(duì)字符編碼感到模糊,有的底層開(kāi)發(fā)人員同樣對(duì)字符編碼缺乏準(zhǔn)確的理解。

回頁(yè)首

1.?編碼問(wèn)題的由來(lái),相關(guān)概念的理解

1.1?字符與編碼的發(fā)展

從計(jì)算機(jī)對(duì)多國(guó)語(yǔ)言的支持角度看,大致可以分為三個(gè)階段:

  系統(tǒng)內(nèi)碼 說(shuō)明 系統(tǒng)
階段一 ASCII 計(jì)算機(jī)剛開(kāi)始只支持英語(yǔ),其它語(yǔ)言不能夠在計(jì)算機(jī)上存儲(chǔ)和顯示。 英文?DOS
階段二 ANSI編碼
(本地化)
為使計(jì)算機(jī)支持更多語(yǔ)言,通常使用?0x80~0xFF?范圍的?2?個(gè)字節(jié)來(lái)表示?1?個(gè)字符。比如:漢字?'中'?在中文操作系統(tǒng)中,使用?[0xD6,0xD0]?這兩個(gè)字節(jié)存儲(chǔ)。

不同的國(guó)家和地區(qū)制定了不同的標(biāo)準(zhǔn),由此產(chǎn)生了?GB2312,?BIG5,?JIS?等各自的編碼標(biāo)準(zhǔn)。這些使用?2?個(gè)字節(jié)來(lái)代表一個(gè)字符的各種漢字延伸編碼方式,稱為?ANSI?編碼。在簡(jiǎn)體中文系統(tǒng)下,ANSI?編碼代表?GB2312?編碼,在日文操作系統(tǒng)下,ANSI?編碼代表?JIS?編碼。

不同?ANSI?編碼之間互不兼容,當(dāng)信息在國(guó)際間交流時(shí),無(wú)法將屬于兩種語(yǔ)言的文字,存儲(chǔ)在同一段?ANSI?編碼的文本中。
中文?DOS,中文?Windows?95/98,日文?Windows?95/98
階段三 UNICODE
(國(guó)際化)
為了使國(guó)際間信息交流更加方便,國(guó)際組織制定了?UNICODE?字符集,為各種語(yǔ)言中的每一個(gè)字符設(shè)定了統(tǒng)一并且唯一的數(shù)字編號(hào),以滿足跨語(yǔ)言、跨平臺(tái)進(jìn)行文本轉(zhuǎn)換、處理的要求。 Windows?NT/2000/XP,Linux,Java

字符串在內(nèi)存中的存放方法:

在?ASCII?階段,單字節(jié)字符串使用一個(gè)字節(jié)存放一個(gè)字符(SBCS)。比如,"Bob123"?在內(nèi)存中為:

42 6F 62 31 32 33 00
B o b 1 2 3 \0

在使用?ANSI?編碼支持多種語(yǔ)言階段,每個(gè)字符使用一個(gè)字節(jié)或多個(gè)字節(jié)來(lái)表示(MBCS),因此,這種方式存放的字符也被稱作多字節(jié)字符。比如,"中文123"?在中文?Windows?95?內(nèi)存中為7個(gè)字節(jié),每個(gè)漢字占2個(gè)字節(jié),每個(gè)英文和數(shù)字字符占1個(gè)字節(jié):

D6 D0 CE C4 31 32 33 00
1 2 3 \0

在?UNICODE?被采用之后,計(jì)算機(jī)存放字符串時(shí),改為存放每個(gè)字符在?UNICODE?字符集中的序號(hào)。目前計(jì)算機(jī)一般使用?2?個(gè)字節(jié)(16?位)來(lái)存放一個(gè)序號(hào)(DBCS),因此,這種方式存放的字符也被稱作寬字節(jié)字符。比如,字符串?"中文123"?在?Windows?2000?下,內(nèi)存中實(shí)際存放的是?5?個(gè)序號(hào):

2D 4E 87 65 31 00 32 00 33 00 00 00 ?????←?在?x86?CPU?中,低字節(jié)在前
1 2 3 \0  

一共占?10?個(gè)字節(jié)。

回頁(yè)首

1.2?字符,字節(jié),字符串

理解編碼的關(guān)鍵,是要把字符的概念和字節(jié)的概念理解準(zhǔn)確。這兩個(gè)概念容易混淆,我們?cè)诖俗鲆幌聟^(qū)分:

  概念描述 舉例
字符 人們使用的記號(hào),抽象意義上的一個(gè)符號(hào)。 '1',?'中',?'a',?'$',?'¥',?……
字節(jié) 計(jì)算機(jī)中存儲(chǔ)數(shù)據(jù)的單元,一個(gè)8位的二進(jìn)制數(shù),是一個(gè)很具體的存儲(chǔ)空間。 0x01,?0x45,?0xFA,?……
ANSI
字符串
在內(nèi)存中,如果“字符”是以?ANSI?編碼形式存在的,一個(gè)字符可能使用一個(gè)字節(jié)或多個(gè)字節(jié)來(lái)表示,那么我們稱這種字符串為?ANSI?字符串或者多字節(jié)字符串 "中文123"
(占7字節(jié))
UNICODE
字符串
在內(nèi)存中,如果“字符”是以在?UNICODE?中的序號(hào)存在的,那么我們稱這種字符串為?UNICODE?字符串或者寬字節(jié)字符串 L"中文123"
(占10字節(jié))

由于不同?ANSI?編碼所規(guī)定的標(biāo)準(zhǔn)是不相同的,因此,對(duì)于一個(gè)給定的多字節(jié)字符串,我們必須知道它采用的是哪一種編碼規(guī)則,才能夠知道它包含了哪些“字符”。而對(duì)于?UNICODE?字符串來(lái)說(shuō),不管在什么環(huán)境下,它所代表的“字符”內(nèi)容總是不變的。

回頁(yè)首

1.3?字符集與編碼

各個(gè)國(guó)家和地區(qū)所制定的不同?ANSI?編碼標(biāo)準(zhǔn)中,都只規(guī)定了各自語(yǔ)言所需的“字符”。比如:漢字標(biāo)準(zhǔn)(GB2312)中沒(méi)有規(guī)定韓國(guó)語(yǔ)字符怎樣存儲(chǔ)。這些?ANSI?編碼標(biāo)準(zhǔn)所規(guī)定的內(nèi)容包含兩層含義:

  1. 使用哪些字符。也就是說(shuō)哪些漢字,字母和符號(hào)會(huì)被收入標(biāo)準(zhǔn)中。所包含“字符”的集合就叫做“字符集”。??
  2. 規(guī)定每個(gè)“字符”分別用一個(gè)字節(jié)還是多個(gè)字節(jié)存儲(chǔ),用哪些字節(jié)來(lái)存儲(chǔ),這個(gè)規(guī)定就叫做“編碼”。??

各個(gè)國(guó)家和地區(qū)在制定編碼標(biāo)準(zhǔn)的時(shí)候,“字符的集合”和“編碼”一般都是同時(shí)制定的。因此,平常我們所說(shuō)的“字符集”,比如:GB2312,?GBK,?JIS?等,除了有“字符的集合”這層含義外,同時(shí)也包含了“編碼”的含義。

UNICODE?字符集”包含了各種語(yǔ)言中使用到的所有“字符”。用來(lái)給?UNICODE?字符集編碼的標(biāo)準(zhǔn)有很多種,比如:UTF-8,?UTF-7,?UTF-16,?UnicodeLittle,?UnicodeBig?等。

回頁(yè)首

1.4?常用的編碼簡(jiǎn)介

簡(jiǎn)單介紹一下常用的編碼規(guī)則,為后邊的章節(jié)做一個(gè)準(zhǔn)備。在這里,我們根據(jù)編碼規(guī)則的特點(diǎn),把所有的編碼分成三類:

分類 編碼標(biāo)準(zhǔn) 說(shuō)明
單字節(jié)字符編碼 ISO-8859-1 最簡(jiǎn)單的編碼規(guī)則,每一個(gè)字節(jié)直接作為一個(gè)?UNICODE?字符。比如,[0xD6,?0xD0]?這兩個(gè)字節(jié),通過(guò)?iso-8859-1?轉(zhuǎn)化為字符串時(shí),將直接得到?[0x00D6,?0x00D0]?兩個(gè)?UNICODE?字符,即?"?D"。

反之,將?UNICODE?字符串通過(guò)?iso-8859-1?轉(zhuǎn)化為字節(jié)串時(shí),只能正常轉(zhuǎn)化?0~255?范圍的字符。
ANSI?編碼 GB2312,
BIG5,
Shift_JIS,
ISO-8859-2?……
把?UNICODE?字符串通過(guò)?ANSI?編碼轉(zhuǎn)化為“字節(jié)串”時(shí),根據(jù)各自編碼的規(guī)定,一個(gè)?UNICODE?字符可能轉(zhuǎn)化成一個(gè)字節(jié)或多個(gè)字節(jié)。

反之,將字節(jié)串轉(zhuǎn)化成字符串時(shí),也可能多個(gè)字節(jié)轉(zhuǎn)化成一個(gè)字符。比如,[0xD6,?0xD0]?這兩個(gè)字節(jié),通過(guò)?GB2312?轉(zhuǎn)化為字符串時(shí),將得到?[0x4E2D]?一個(gè)字符,即?'中'?字。

“ANSI?編碼”的特點(diǎn):
1.?這些“ANSI?編碼標(biāo)準(zhǔn)”都只能處理各自語(yǔ)言范圍之內(nèi)的?UNICODE?字符。
2.?“UNICODE?字符”與“轉(zhuǎn)換出來(lái)的字節(jié)”之間的關(guān)系是人為規(guī)定的。
UNICODE?編碼 UTF-8,
UTF-16,?UnicodeBig?……
與“ANSI?編碼”類似的,把字符串通過(guò)?UNICODE?編碼轉(zhuǎn)化成“字節(jié)串”時(shí),一個(gè)?UNICODE?字符可能轉(zhuǎn)化成一個(gè)字節(jié)或多個(gè)字節(jié)。

與“ANSI?編碼”不同的是:
1.?這些“UNICODE?編碼”能夠處理所有的?UNICODE?字符。
2.?“UNICODE?字符”與“轉(zhuǎn)換出來(lái)的字節(jié)”之間是可以通過(guò)計(jì)算得到的。

我們實(shí)際上沒(méi)有必要去深究每一種編碼具體把某一個(gè)字符編碼成了哪幾個(gè)字節(jié),我們只需要知道“編碼”的概念就是把“字符”轉(zhuǎn)化成“字節(jié)”就可以了。對(duì)于“UNICODE?編碼”,由于它們是可以通過(guò)計(jì)算得到的,因此,在特殊的場(chǎng)合,我們可以去了解某一種“UNICODE?編碼”是怎樣的規(guī)則。