首頁 > 運動

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

由 CSDN 發表于 運動2021-05-11

簡介(偷偷告訴你們,大家喜聞樂見的Emoji表情都是在1號平面,範圍是U+1F600-U+1F64F)(2)UCS-2和UCS-4ISO10646標準為“通用字符集”(UCS)定義了一種16位的編碼形式(即UCS-2),UCS-2全稱Unive

65536為什麼是0

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

作者 | Aledsan

責編 | 王曉曼

出品 | CSDN部落格

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

基礎概念

1、字元

字元指類字形單位或符號,包括字母、數字、運算子號、標點符號和其他符號,以及一些功能性符號。一般來說我們稱某個字符集裡面的字元,叫xx字元,如ASCII字符集裡面的ASCII字元,GB2312字符集裡面的GB2312字元。

2、字符集

字符集(Character Set、Charset),字面上的理解就是字元的集合,是一個自然語言文字系統支援的所有字元的集合。字元是各種文字和符號的總稱,包括文字、數字、字母、音節、標點符號、圖形符號等。例如ASCII字符集,定義了128個字元;GB2312字符集定義了7445個字元。而

字符集準確地來說,指的是已編號的字元的有序集合

(但不一定是連續的,後文有詳細介紹)。

常見字符集名稱:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。

3、碼位

在字元編碼術語中,碼位(code point)或稱編碼位置、碼點,是組成碼空間(或內碼表)的數值。例如,ASCII碼包含128個碼位,範圍是016進位制到7F16進位制,擴充套件ASCII碼包含256個碼位,範圍是016進位制到FF16進位制,而Unicode包含1,114,112個碼位,範圍是016進位制到10FFFF16進位制。Unicode碼空間劃分為17個Unicode字元平面(基本多文種平面,16個輔助平面),每個平面有65,536(= 216)個碼位。因此Unicode碼空間總計是17 × 65,536 = 1,114,112。 —解釋來源於維基百科。

4、字元編碼

字元編碼(Character Encoding),是把字符集中的字元按一定方式編碼為某指定集合中的某一物件的過程(比如將字元編碼為由0和1兩個數字所組成的位串模式、由0~9十個數字所組成的自然數序列或電脈衝等),亦即在字符集與指定集合兩者之間建立一個對應關係(即對映關係)的過程。這是資訊處理的一項基礎技術。常見的例子包括將拉丁字母表編碼成摩斯電碼和ASCII碼。

PS:這裡我們計算機這裡字元編碼肯定是用二進位制來編碼的。

看完這四個概念,你應該要明白,它們之間的關係,以ASCII為例,下圖解釋它們之間關係。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

這裡細說一下,碼位就是這個字符集裡面字元的一個表示位置,通俗來說,碼位就是一般跟字符集綁在一起,字元編碼是把字符集中的字元編碼為特定的二進位制數,以便在計算機中儲存。這個二進位制數就叫xx碼。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

字符集編碼分類總結

在說字符集編碼之前,先明確一個觀點,字符集編碼與字符集是兩個不同層面的概念:

(1)charset 是character set 的簡寫,即字符集。

(2)encoding 是 charsetencoding 的簡寫,即字符集編碼,簡稱編碼。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

1、ASCII編碼

ASCII(美國資訊交換標準程式碼)是基於拉丁字母(就是我們現在的英文字母)的一套電腦編碼系統。它主要用於顯示現代英語,而其擴充套件版本延伸美國標準資訊交換碼則可以部分支援其他西歐語言,並

等同於國際標準ISO/IEC 646

ASCII由電報碼發展而來。第一版標準釋出於1963年,1967年經歷了一次主要修訂,最後一次更新則是在1986年,至今為止共定義了128個字元;

其中33個字元無法顯示

(一些終端提供了擴充套件,使得這些字元可顯示為諸如笑臉、撲克牌花式等8-bit符號),且這33個字元多數都已是陳廢的控制字元。控制字元的用途主要是用來操控已經處理過的文字。在33個字元之外的是95個可顯示的字元。用鍵盤敲下空白鍵所產生的空白字元也算1個可顯示字元(顯示為空白)。

每個ASCII字元佔用1個位元組(8bits)

,共有128位字元或符號,使用7位二進位制數(剩下的1位二進位制為0,即高位為0)來表示所有的大寫和小寫字母,數字0 到9、標點符號,以及在美式英語中使用的特殊控制字元。

缺點:ASCII的最大缺點是隻能顯示26個基本拉丁字母、阿拉伯數目字和英式標點符號,因此只能用於顯示現代美國英語(而且在處理英語當中的外來詞如nave、café、élite等等時,所有重音符號都不得不去掉,即使這樣做會違反拼寫規則)。而EASCII(

即擴充套件的ASCII碼,利用8位的高位設為1進行擴充套件

)雖然解決了部分西歐語言的顯示問題,但對更多其他語言依然無能為力。因此現在的作業系統內碼(稍後會講)基本已經拋棄ASCII碼而轉用Unicode碼。

ASCII碼錶 :http://www.asciitable.com

2、GB2312編碼

前面可以看到ASCII碼即使進行了擴充套件也能表示的字元也很少,尤其是當需要計算機顯示儲存中文的時候,就需要一種對中文進行編碼的字符集,GB 2312就是解決中文編碼的字符集,由國家標準委員會發布。那個時候當中國人們得到計算機時,已經沒有可以利用的位元組狀態來表示漢字,況且有6000多個常用漢字需要儲存,於是想到把那些ASCII碼中127號之後的奇異符號們直接取消掉, 規定:

一個小於127的字元的意義與原來相同,但兩個大於127的字元連在一起時,就表示一個漢字

,前面的一個位元組(稱之為高位元組)從0xA1用到0xF7,後面一個位元組(低位元組)從0xA1到0xFE,這樣我們就可以組合出大約7000多個簡體漢字了。

在這些編碼裡,我們還把數學符號、羅馬希臘的字母、日文的假名們都編進去了,連在 ASCII 裡本來就有的數字、標點、字母都統統重新編了兩個位元組長的編碼

,這就是常說的

"全形"字元

,而原來在127號以下的那些就叫“半形”字元了。這種漢字方案叫做 “GB2312”。

GB2312 是對ASCII 的中文擴充套件。相容ASCII。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

這裡的A GB2312碼是0xA3C1,0xA3和0xC1都是高於127的,所以判斷它是個全形字元,另外我們可以觀察到所有的GB2312編碼表中的GB2312碼每個位元組都是大於0xA0的,這個就是為了保證能區分是否為ASCII碼,小於127的位元組就按照ASCII碼標準,碰到連續兩個大於127位元組就組合成一個GB2312碼。

GB2312漢字編碼字符集對照表:http://tools.jb51.net/table/gb2312

3、GBK編碼

但是中國的漢字太多了,我們很快就就發現有許多人的人名沒有辦法在這裡打出來,不得不繼續把 GB2312 沒有用到的碼位找出來用上。後來還是不夠用,於是乾脆

不再要求低位元組一定是127號之後的內碼,只要第一個位元組是大於127就固定表示這是一個漢字的開始,不管後面跟的是不是擴充套件字符集裡的內容

。結果擴充套件之後的編碼方案被稱為 “GBK” 標準,

GBK 包括了GB2312 的所有內容,同時又增加了近20000個新的漢字(包括繁體字)和符號

4、GB18030編碼

後來中國的少數民族也要用電腦了,GBK的兩萬多字也已經無法滿足我們的需求了,還有更多可能你自己從來沒見過的漢字需要編碼。這時候顯然只用2bytes表示一個字已經不夠用了(2byte最多隻有65536種組合,然而為了和ASCII相容,最高位不能為0就已經直接淘汰了一半的組合,只剩下3萬多種組合無法滿足全部漢字要求)。因此GB18030多出來的漢字使用4byte編碼。

當然,為了相容GBK,這個四位元組的前兩位顯然不能與GBK衝突(實操中發現後兩位也並沒有和GBK衝突)。透過多年的發展至此,GB18030編碼的中文檔案已經有七萬多個漢字了。

GB18030包含三種長度的編碼:單位元組的ASCII、雙位元組的GBK(略帶擴充套件)、以及用於填補所有Unicode碼位的四位元組UTF區塊。所以

我們說GB18030採用多位元組編碼,每個字元可以由 1 個、2 個或 4 個位元組組成

其實我們用到的99%以上的漢字,都在GB2312那一塊區域內。在實際使用中,GBK編碼已經可以滿足大部分場景了,GB18030編碼中所有漢字都是我們這輩子都不一定能見到的文字,所以平時經常會使用的就是GBK編碼。

這裡額外總結一下這四個相容性關係是GB18030相容GBK,GBK相容GB2312,GB2312相容ASCII。所謂相容,你可以簡單理解為子集、不衝突的關係。

例如GB2312編碼的檔案中可以出現ASCII字元,GBK編碼的檔案中可以出現GB2312和ASCII字元,GB18030編碼的檔案可以出現GBK、GB2312、ASCII字元。

5、Unicode

友情建議:看Unicode一些概念解釋和歷史時,建議看維基百科,而且是英文版的,別用中文版,有些地方講的模稜兩可,容易陷入盲區。

Unicode

(中文:萬國碼、國際碼、統一碼、單一碼)(全稱Universal Multiple-Octet Coded Character Set)它伴隨著通用字符集(英語:Universal Character Set, UCS)的標準而發展。所以可以看出他是字符集。

(1)Unicode與 ISO 10646

全世界很多個國家都在為自己的文字編碼,並且互不相通,不同的語言字元編碼值相同卻代表不同的符號(例如:韓文編碼EUC-KR中“”的編碼值正好是漢字編碼GBK中的“茄憊絹”)。

因此,同一份文件,複製至不同語言的機器,就可能成了亂碼,於是人們就想:

我們能不能定義一個超大的字符集,它可以容納全世界所有的文字字元,再對它們統一進行編碼,讓每一個字元都對應一個不同的編碼值,從而就不會再有亂碼了

如果說“各個國家都在為自己文字獨立編碼”是百家爭鳴,那麼“建立世界統一的字元編碼”則是一統江湖,誰都想來做這個武林盟主。早前就有兩個機構做了這個事:

國際標準化組織(ISO),他們於1984年建立ISO/IEC JTC1/SC2/WG2工作組,試圖制定一份“通用字符集”(Universal Character Set,簡稱UCS),並最終制定了ISO 10646標準。(簡單來說ISO 10646標準就是UCS)

統一碼聯盟,他們由Xerox、Apple等軟體製造商於1988年組成,並且開發了Unicode標準(The Unicode Standard,這個字首Uni很牛逼哦—Unique, Universal, and Uniform)。

Unicode與ISO 10646標準的風風雨雨:

在1984年,喜歡以繁多的編號糊弄群眾的國際標準化組織ISO也開始著手製定解決不同語言字元數量太大問題的解決方案,

這一方案被稱為Universal Character Set(UCS)

,正式的編號是ISO-10646(記得麼,ASCII是ISO-646,不知這種安排是否是故意的)。

還是ISO高瞻遠矚,一開始就確定了UCS是一個31位的編碼字符集(即用一個大小不超過2的31次方的整數數字為每個字元編號),這回真的足以容納古往今來所有國家,所有語言所包含的字元了(是的,任何國家,任何小語種都包括)。雖然後來他們意識到,2的31次方個碼位又實在太多了……

天下大勢,分久必合。無論Unicode還是UCS,最初的目的都是杜絕各種各樣名目繁多形式各異互不相容老死不相往來的私用擴充套件編碼(好囉嗦的一句話),結果兩方確立標準的同時(最初時這兩個標準是不相容的),因為都是個幹個的,肯定不可能一模一樣,出現標準不同。

1991年,Unicode聯盟與ISO的工作組終於開始討論Unicode與UCS的合併問題,雖然其後的合併進行了很多年,Unicode初版規範中的很多編碼都需要被改寫,UCS也需要對碼空間的使用進行必要限制,但成果是喜人的。

最終,兩者統一了抽象字符集(即任何一個在Unicode中存在的字元,在UCS中也存在),且最靠前的65535個字元也統一了字元的編碼。對於碼空間,兩者同意以一百一十萬為限(即兩者都認為雖然65536不夠,但2的31次方又太大,一百一十萬是個雙方都可接受的碼空間大小,也夠用,當然,這裡說的一百一十萬只是個約數),Unicode將碼空間擴充套件到了一百一十萬,而UCS將永久性的不使用一百一十萬以後的碼位。

也就是說,現在再講Unicode只包含65536個字元是不對的(雖然大家現在都知道Unicode至少都可以囊括幾億個字元)。除了對已經定義的字元進行統一外,Unicode聯盟與ISO工作組也同意今後任何的擴充套件工作兩者均保持同步,因此雖然從歷史的意義上講Unicode與UCS不是一回事(甚至細節上說也不是一回事),但現在提起Unicode,指代兩者均無不妥,畢竟因為已經統一了。

(現在網上基本上把Unicode字符集叫做UCS,Unicoide的全稱是 Universal Multiple-Octet Coded Character Set簡寫也是UCS,一看也對上了,害,只能說天註定)。

現在Unicode編碼點分為17個平面(plane),每個平面包含216(即65536)個碼位(codepoint)。17個平面的碼位可表示為從U+xx0000到U+xxFFFF,其中xx表示十六進位制值從0016到1016,共計17個平面。

這第一個位置(當xx是00的時候)被稱為BMP(基本多文種平面,BasicMultilingualPlane)。它包含了最常用的碼位從U+0000到U+FFFF(常見的65536個字元)。

其餘16個平面(從下面的1號平面到16號平面),你可以叫做非BMP,由此這樣分的話裡面的字元就有兩個概念:BMP字元和非BMP字元,後者也被稱為補充字元。(偷偷告訴你們,大家喜聞樂見的Emoji表情都是在1號平面,範圍是U+1F600-U+1F64F)

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

(2)UCS-2和UCS-4

ISO10646標準為“通用字符集”(UCS)定義了

一種16位的編碼形式(即UCS-2)

UCS-2全稱Universal Character Set coded in 2octets

,從英文上就可以看出含義,以2位元組編碼的通用字符集編碼,固定佔用2個位元組,它包含65536個編碼空間(可以為全世界最常用的63K字元編碼,為了相容Unicode,0xD800-0xDFFF之間的碼位未使用)。

例:“漢”的UCS-2編碼為6C49。除此之外ISO10646標準為“通用字符集”(UCS)還定義了

一種31位的編碼形式(即UCS-4),UCS-4全稱UniversalCharacter Set coded in 4 octets

,其編碼固定佔用4個位元組,編碼空間為0x00000000 ~0x7FFFFFFF(可以編碼20多億個字元)。

隨著Unicode與ISO 10646合併統一,Unicode就用UCS通用字符集標準,早期的 Unicode 編碼實現也就採用了UCS-2和UCS-4。(準確來說是UCS-2,UCS-4基本上是理論,沒付諸實際,畢竟早期65536個字元已經夠用了,我兩個位元組編碼能實現的事,腦子笨的人才會用四個位元組實現,你以為儲存和頻寬有多的)。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

這裡編碼最多也就存在UCS-2(big Endian和LittleEndian先不管,後面會講)。

Unicode字符集只規定了碼點和文字之間的對應關係,並沒有規定碼點在計算機中如何儲存。

UCS-2和UCS-4就規定了具體的實現,後來改進演變為了UTF-16, UTF-32。然後又創造了一種全新的簡單粗暴好用的變長編碼UTF-8,於是乎這三哥們就形成了現代Unicode字符集編碼實現的三劍客。

(3)UTF-16與UTF-32

Unicode與ISO 10646合併統一後,Unicode與 ISO 10646 的通用字符集概念(UCS)相對應。

早期實現Unicode用的編碼是UCS-2

,後來隨著發展發現 216(即65536)個字元不能滿足了,Unicode標準本身發生了變化:65536個字元顯得不足,引入了更大的31位空間和一個編碼(UCS-4),每個字元需要4個位元組。

但是統一碼聯盟對此予以抵制(這就是為什麼我之前說UCS-4是一種理論編碼,根本就沒付諸實際),這是因為每個字元4個位元組浪費了很多磁碟空間和記憶體,並且因為一些製造商已經在每個字元2個位元組的技術上投入了大量資金。所以最後透過一系列巴拉巴拉討論規定形成了一種折衷方案,建立了UTF-16編碼方案(此時Unicode標準2。0),它替代了原有的UCS-2,並做了改進。

它與UCS-2一樣,它使用兩個位元組為全世界最常用的63K字元編碼,不同的是,它使用4個位元組對不常用的字元進行編碼。目的就是為了支援從17個平面編碼1,112,064個程式碼點。

UTF-16屬於變長編碼。我們可以將UTF-16編碼看成是UCS-2編碼父集。在沒有輔助平面字元(surrogate code points)前,UTF-16與UCS-2所指的是同一意思。但當引入輔助平面字元後,就稱為UTF-16了。

現在應該認為UCS-2已作廢,如果有人還用這種,也不必糾結,它就是表達用定長2位元組編碼,自己心裡清楚就行(基本上你查維基百科上UCS-2都是重定向到UTF-16)。

另外當時ISO 10646的UCS-4編碼併入了Unicode標準,而UCS-4有20多億個編碼空間,但實際使用範圍並不超過0x10FFFF,並且為了相容Unicode標準,ISO也承諾將不會為超出0x10FFFF的UCS-4編碼賦值。

由此提出了實實在在的UTF-32編碼(現在也應該認為UCS-4像UCS-2一樣作廢,維基百科上UCS-4也重定向到UTF-32頁面),它的編碼值與UCS-4相同,只不過其編碼空間被限定在了0~0x10FFFF之間。因此也可以說:

UTF-32是UCS-4的一個子集。

(現在若有軟體聲稱自己支援UCS-2,那其實是暗指它不能支援在UTF-16中超過2位元組的字集。)

UTF-16

(16 位 Unicode轉換格式)是一種字元編碼,能夠對Unicode的所有1,112,064個有效碼點進行編碼(實際上,此程式碼點數由UTF-16的設計決定,這個你細品你就知道什麼意思,就好像某個班有55個人,根據55個座位確定55個人,而55個座位這個多少是由55個人決定的,兩者是相互的,這是一個哲學道理,hh扯遠了,所以其中意味自行明白)。

前面提到過:Unicode編碼點分為17個平面(plane),每個平面包含216(即65536)個碼位(codepoint),而第一個平面稱為“基本多語言平面”(Basic Multilingual Plane,簡稱BMP),其餘平面稱為“輔助平面”(Supplementary Planes)。其中“基本多語言平面”(00xFFFF)中0xD8000xDFFF之間的碼位作為保留,未使用。

UCS-2只能編碼“基本多語言平面”中的字元,此時UTF-16與UCS-2的編碼一樣(都直接使用Unicode的碼位作為編碼值),例:“漢”在Unicode中的碼位為6C49,而在UTF-16編碼也為6C49。

另外,UTF-16還可以利用保留下來的0xD800-0xDFFF區段的碼位來對“輔助平面”的字元的碼位進行編碼,因此UTF-16可以為Unicode中所有的字元編碼。

UTF-16和UTF-32也就是如今Unicode編碼的標準之二,他們的區別就是UTF-16是變長編碼,大部分是2位元組和少部分4位元組,UTF-32是定長編碼,表示任何字元都用 4 位元組

(4)UTF-8

從前述內容可以看出:無論是UCS-2/4還是UTF-16/32,一個字元都需要多個位元組來編碼,這對那些英語國家來說多浪費頻寬啊!(尤其在網速本來就不快的那個年代……),而且我們注意到UTF-16最少2位元組和UTF-32不變4位元組,這肯定是不相容ASCII碼的,由此,UTF-8產生了。

在UTF-8編碼中,ASCII碼中的字元還是ASCII碼的值,只需要一個位元組表示,其餘的字元需要2位元組、3位元組或4位元組來表示。

UTF-8的編碼規則:

對於ASCII碼中的符號,使用單位元組編碼,其編碼值與ASCII值相同。其中ASCII值的範圍為0~0x7F,所有編碼的二進位制值中第一位為0(這個正好可以用來區分單位元組編碼和多位元組編碼)。

其它字元用多個位元組來編碼(假設用N個位元組),多位元組編碼需滿足:第一個位元組的前N位都為1,第N+1位為0,後面N-1 個位元組的前兩位都為10,這N個位元組中其餘位全部用來儲存Unicode中的碼位值。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

現如今

UTF-8 是網際網路上使用最廣的一種 Unicode 的實現方式

,是其他兩種無可比擬的。

(5)UTF的位元組序和BOM

位元組序就要先補充一點知識:

碼元(code unit):是能用於處理或交換編碼文字的最小位元組合。它代表某種編碼中最小的可用來識別一個合法字元的最小位元組數序列。

UTF-8使用變長的位元組序列來表示字元;某個字元(對應一個碼點)可能使用1-4個位元組才能表示;在UTF-8中一個字元最小可能一個位元組,所以我們規定1個位元組就是一個碼元;

UTF-16使用也變長位元組序列來表示字元;某個字元(對應一個碼點)可能使用2個或者4個字元來表示;因為2個位元組序列是最小的能夠識別一個碼點的單位,同理我們規定2個位元組就是一個碼元;

UTF-32使用定長的4個位元組表示一個字元;一個字元(對應一個碼點)使用4個字元來表示,這樣4個位元組就是一個碼元。

簡單來說,就是“碼點”經過對映後得到的二進位制串的轉換格式單位稱之為“碼元”。“碼點”就是一串二進位制數,“碼元”就是切分這個二進位制數的方法。這些編碼每次處理一個碼元,你可以把它理解為UTF-8每次讀碼點的8位,UTF-16每次讀碼點的16位,UTF-32每次讀碼點的32位,。當然這也是為什麼叫這些叫Unicode轉換格式的原因。處理的是同一個字符集,但是處理方式不同。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

位元組序

UTF-8一次一個UTF-8碼元,即處理一個位元組,沒有位元組序的問題。UTF-16一次處理一個UTF-16碼元,對應兩個位元組,UTF-32一次一個UTF-32碼元,對應處理四個位元組,所以這就要考慮到一個位元組序問題。

以UTF-16w為例,在解釋一個UTF-16編碼文字前,首先要弄清楚每個編碼單元的位元組序。例如收到一個“奎”的Unicode編碼是594E,“乙”的Unicode編碼是4E59。

如果我們收到UTF-16位元組流“594E”,那麼這是“奎”還是“乙”?這就考慮大小端問題,所以UTF-16編碼包括三種:UTF-16BE(Big Endian),UTF-16LE(Little Endian)、UTF-16(類似的名稱UCS-2BE和UCS-2LE用於顯示UCS-2的版本。)

UTF-16BE和UTF-16LE好理解,直接指定了位元組序(大小端),但是UTF-16怎麼處理呢?

Unicode規範中推薦的標記位元組順序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一個有點小聰明的想法:

在UCS編碼中有一個叫做“ZERO WIDTH NO-BREAKSPACE”的字元,它的編碼是FEFF。而FFFE在UCS中是不存在的字元,所以不應該出現在實際傳輸中。UCS規範建議我們在傳輸位元組流前,先傳輸字元“ZERO WIDTH NO-BREAK SPACE”。

這樣如果接收者收到FEFF,就表明這個位元組流是Big-Endian的;如果收到FFFE,就表明這個位元組流是Little-Endian的

同樣的類比,UTF-32也是這樣的。有UTF-32BE、UTF-32LE、UTF-32。前面UTF-32BE和UTF-32LE直接指定了位元組序(大小端),後面的UTF-32也是靠BOM。

UTF-8不需要BOM來表明位元組順序,但可以用BOM來表明編碼方式。字元“ZERO WIDTH NO-BREAKSPACE”的UTF-8編碼是EF BB BF(讀者可以用我們前面介紹的編碼方法驗證一下)。所以如果接收者收到以EF BB BF開頭的位元組流,就知道這是UTF-8編碼了。

Windows就是使用BOM來標記文字檔案的編碼方式的。它就建議所有的 Unicode 檔案應該以 ZERO WIDTH NOBREAK SPACE(U+FEFF)字元開頭。這作為一個“特徵符”或“位元組順序標記(byte-ordermark,BOM)”來識別檔案中使用的編碼和位元組順序。所以用Windows自帶的記事本將檔案儲存為UTF-8編碼的時候,記事本會自動在檔案開頭插入BOM(雖然BOM對UTF-8來說並不是必須的)。

但也有一些系統或程式不支援BOM,因此帶有BOM的Unicode檔案有時會帶來一些問題。比如JDK1。5以及之前的Reader都不能處理帶有BOM的UTF-8編碼的檔案,解析這種格式的xml檔案時,會丟擲異常:Content is not allowed inprolog。

Linux/UNIX 並沒有使用 BOM,因為它會破壞現有的 ASCII 檔案的語法約定。所以一般我們不建議用Windows自帶的記事本編輯UTF-8檔案就是這樣。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

總結

1、簡單地說:Unicode和UCS是字符集,不屬於編碼UTF-8、UTF-16、UTF-32等是針對Unicode字符集的編碼,UCS-2和UCS-4是針對UCS字符集的編碼(只是我們習慣把Unicode字符集編碼簡稱為Unicode編碼,把UCS字符集編碼稱為UCS編碼)。

Unicode沿用UCS字符集,在UCS-2和UCS-4基礎上提出的UTF-16、UTF-32。並發展了UTF-8,發展到現在,就密不可分了,大家基於UCS就把Uniocde維護好就行,釋出標準大家統一。以往的UCS-2和UCS-4概念就預設作廢了這樣一個關係,整個他們的發展長話短說就是這樣,懂了嗎。

2、UTF-8、UTF-16、UTF-32、UCS-2、UCS-4對比:

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

由於歷史方面的原因,你還會在不少地方看到把Unicode稱為一種編碼的情況,那是因為

早期的2位元組編碼最初稱為“ Unicode”

但現在稱為“ UCS-2”

,這種情況下的 Unicode 通常就是 UTF-16 或者是更早的 UCS-2 編碼,只是被一直搞混了,在某些老軟體上尤為常見。比如下面editplus裡面檔案編碼設定。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

以前的Windows電腦上的記事本(左邊)顯示的是Unicode,不過現在好像改了變成了UTF-16。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

不過由於各種原因,必須承認,在不同的語境下,“Unicode”這個詞有著不同的含義。

它可能指:

(1)Unicode 標準

(2)Unicode 字符集

(3)Unicode 的抽象編碼(編號),也即碼點、碼位(code point)

(4)Unicode 的一個具體編碼實現,通常即為變長的 UTF-16(16 或 32 位),又或者是更早期的定長 16 位的 UCS-2

所以像我一般有時候非要區分的話都是直接說全,Unicode 標準,Unicode 字符集,Unicode編碼等等。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

ANSI編碼

為使計算機支援更多語言,通常使用0x800~xFF範圍的2個位元組來表示1個字元。比如:漢字‘中’ 在中文作業系統中,使用 [0xD6,0xD0]這兩個位元組儲存。

不同的國家和地區制定了不同的標準,由此產生了 GB2312、GBK、GB18030、Big5、Shift_JIS 等各自的編碼標準。這些使用多個位元組來代表一個字元的各種語言延伸編碼方式,稱為 ANSI 編碼。

在簡體中文Windows作業系統中,ANSI 編碼代表 GBK 編碼;在繁體中文Windows作業系統中,ANSI編碼代表Big5;在日文Windows作業系統中,ANSI 編碼代表 Shift_JIS 編碼。

不同 ANSI 編碼之間互不相容,當資訊在國際間交流時,無法將屬於兩種語言的文字,儲存在同一段 ANSI 編碼的文字中。

在使用ANSI編碼支援多語言階段,每個字元使用一個位元組或多個位元組來表示(MBCS,Multi-Byte Character System),因此,這種方式存放的字元也被稱作多位元組字元。比如,“中文123” 在中文 Windows 95 記憶體中為7個位元組,每個漢字佔2個位元組,每個英文和數字字元佔1個位元組。

在非 Unicode 環境下,由於不同國家和地區採用的字符集不一致,很可能出現無法正常顯示所有字元的情況。微軟公司使用了內碼表(Codepage)轉換表的技術來過渡性的部分解決這一問題,即透過指定的轉換表將非Unicode 的字元編碼轉換為同一字元對應的系統內部使用的Unicode 編碼。

可以在“語言與區域設定”中選擇一個內碼表作為非 Unicode 編碼所採用的預設編碼方式,如936為簡體中文GBK,950為正體中文Big5(皆指PC上使用的)。在這種情況下,一些非英語的歐洲語言編寫的軟體和文件很可能出現亂碼。而將內碼表設定為相應語言中文處理又會出現問題,這一情況無法避免。

從根本上說,完全採用統一編碼才是解決之道,雖然現在Unicode有了,但由於歷史遺留,老軟體等等原因,所以系統統一用某種編碼格式的Unicode目前尚無法做到這一點。

內碼表技術現在廣泛為各種平臺所採用。UTF-7 的內碼表是65000,UTF-8的內碼表是65001。簡體中文上使用的內碼表為936,GBK編碼。

以前中文DOS、中文/日文Windows95/98時代系統內碼使用的是ANSI編碼(本地化,根據不同地區設定不同的系統內碼Windows版本),現在win7,win10等等系統的內碼都是用的Unicode。

不過微軟為了以前的程式相容性,比如在某些情況下,比如你的程式需要和不支援Unicode的程式互動時,可能還是會需要用到code page,提供內碼表服務(就好比微軟不能說:“老子支援unicode了,以後不支援Unicode的程式都給我滾粗。”只能撅著屁股讓這些老掉牙的程式仍然可以執行,於是只好給他們提供一個“非Unicode預設字符集”) 。可以在cmd下輸入chcp檢視code page。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

WindowsAPI 的Wide Char 表達是 UTF-16: Unicode (Windows), L“” 表示是轉換為 wide char。

Cocoa的NSString 和 Core Foundation 的CFString 內部表達都是 UTF-16,所以其實 OSX 和 iOS 內部處理都用的是 UTF-16。

JavaString 的內部表達是 UTF-16,所以大量跨平臺程式和 Android 程式其實內部也在用 UTF-16。

大部分的作業系統和 UI framework 的內部字串表達(內碼)都是UTF-16,不過Linux系統內使用的內碼是UTF-8。

乾貨| 為什麼是0?一文搞懂字元字元與字元的區別!

Tip:內碼和外碼

在計算機科學及相關領域當中,內碼指的是“將資訊編碼後,透過某種方式儲存在特定記憶裝置時,裝置內部的編碼形式”。在不同的系統中,會有不同的內碼。

在以往的英文系統中,內碼為ASCII。在繁體中文系統中,當前常用的內碼為大五碼。在簡體中文系統中,內碼則為國標碼。

為了軟體開發方便,如國際化與本地化,現在許多系統會使用Unicode做為內碼,常見的作業系統Windows、Mac OS X、Linux皆如此。許多程式語言也採用Unicode為內碼,如Java、Python3。

外碼:除了內碼,皆是外碼。要注意的是,原始碼編譯產生的目的碼檔案(如果Java可執行檔案或class檔案)中的編碼方式屬於外碼。

引用文章:

1、必讀,真說清的話得結合上面的維基百科上的說法理解:

https://en。wikipedia。org/wiki/Universal_Coded_Character_Set

https://en。wikipedia。org/wiki/Unicode

https://en。wikipedia。org/wiki/UTF-16

https://en。wikipedia。org/wiki/UTF-32

2、除此之外參考引用了下面三篇文章的一些觀點:

http://www。blogjava。net/zhenandaci/archive/2008/12/24/248014。html

https://www。cnblogs。com/malecrab/p/5300503。html

https://blog。51cto。com/polaris/377468

版權宣告:本文為CSDN博主「Aledsan」的原創文章,遵循CC 4。0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。

原文連結:https://blog。csdn。net/weixin_43465312/article/details/105918985

Tags:編碼UTFUnicodeUCS位元組