go網(wǎng)絡(luò)編程學(xué)習(xí)筆記5(字符集和編碼)

引言

以前用 EBCDIC 和 ASCII 編碼蛙卤,(別看只有兩種編碼)粘姜,但事情從來沒有簡(jiǎn)單過盗温,恰恰相反變得越來越復(fù)雜了。但據(jù)推測(cè),編碼簡(jiǎn)化就像(黎明前)地平線上閃過了一道光缝左,但要等到天亮還得 50 年亿遂。
早期計(jì)算機(jī)是從美國(guó)浓若、英國(guó)、澳大利亞這些英語(yǔ)國(guó)家發(fā)展起來的蛇数,結(jié)果計(jì)算機(jī)字符集就以這些國(guó)家使用的語(yǔ)言和字符進(jìn)行設(shè)計(jì)挪钓,大體上,也就是拉丁字母苞慢,加上數(shù)字诵原、標(biāo)點(diǎn)別的字符。他們使用 ASCII 或 EBCDIC 進(jìn)行編碼挽放。
字符處理的機(jī)制是基于此的:文本文件和基于字節(jié)序列的基本輸入輸出绍赛,每個(gè)字節(jié)代表一個(gè)單獨(dú)的字符。字符串比較可以通過對(duì)比相對(duì)應(yīng)的字節(jié)實(shí)現(xiàn)辑畦,字符串的大小寫轉(zhuǎn)換可以通過單個(gè)字節(jié)的操作完成吗蚌,等等。

用理工科的眼光看纯出,世界上只有 ASCII 一種編碼就清靜了蚯妇。但實(shí)際正是相反的趨勢(shì),越來越多的人需要計(jì)算機(jī)軟件中使用自己熟悉的語(yǔ)言暂筝。如果你的軟件可以在不同的國(guó)家運(yùn)行箩言,那你的用戶就需要軟件使用他們自己的語(yǔ)言。在分布式的系統(tǒng)中焕襟,使用不同的系統(tǒng)模塊的人可能希望不同的語(yǔ)言和字符陨收。
國(guó)際化(i18n)是指你的應(yīng)用怎么處理不同的語(yǔ)言和文化。本地化(l10n)是說你怎么把國(guó)際化的應(yīng)用適配成小群體使用鸵赖。
國(guó)際化和本地化各自都是一個(gè)很大的課題务漩。舉個(gè)例子,關(guān)于顏色的話題:白色在西方表示純潔它褪,在中國(guó)表示死亡饵骨,在埃及表示喜悅。在這章中我們只關(guān)注字符的處理茫打。

定義

我們所關(guān)心的是系統(tǒng)處理你所表述的內(nèi)容居触,十分重要。下面是有人做的一套行之有效的定義方法老赤。

字符

字符"自然語(yǔ)言中用符號(hào)表示信息的單位轮洋,比如字母、數(shù)字诗越、標(biāo)點(diǎn)"(維基百科)砖瞧,字符是有價(jià)值的最小書寫單位(Unicode)這就包括了 a 和 A,或其他語(yǔ)言字符嚷狞,也包括數(shù)字 2和標(biāo)點(diǎn)','块促,還有像 '£'這樣的字符荣堰。
字符實(shí)際上是符號(hào)的抽象組合,也就是說 a 代表了所有手寫的 a竭翠,有點(diǎn)像柏拉圖圓也是圓的關(guān)系振坚。原則上字符也包括控制字符,也就是實(shí)際中不存在只是為了處理語(yǔ)言的格式用的斋扰。
字符本身并不沒有特定形狀渡八,只是我們通過形狀來識(shí)別它。即使如此传货,我們也要聯(lián)系上下文才能理解:數(shù)學(xué)中屎鳍,如果你看到 π (pi)這個(gè)字符,它表示圓周率问裕,但是如果你讀希臘文逮壁,它只是 16 個(gè)字母;"προσ"是希臘詞語(yǔ)“with”粮宛,這個(gè)和 3.14159 沒有半點(diǎn)關(guān)系窥淆。

字符體系和字符集

字符集就是一個(gè)不同的且唯一的字符的集合,像拉丁字母巍杈,不需要指定順序忧饭。在英語(yǔ)中,盡管我們說 a 是在 z 的前面筷畦,但我們不說 a 比 z 要小词裤。電話聯(lián)系人的排序方式里,McPhee 在MacRea 的前面說明了字母排序不是嚴(yán)格的按字符的順序汁咏。
字符體系就是字名和字形的結(jié)合亚斋,比如作媚,a 可能寫成 'a', 'a' or 'a'攘滩,但這不是強(qiáng)制的,他們只是樣本纸泡。字符體系可能區(qū)分大小寫漂问,所以 a 和 A 是不同的。但他們的意思可能是一樣的女揭,就算是長(zhǎng)的不一樣蚤假。(有點(diǎn)像編程語(yǔ)言對(duì)待大小寫,有的大小寫敏感吧兔,比如 Go 語(yǔ)言磷仰,有的就是一樣的,比如 Basic境蔼。)灶平。另一方面伺通,字符系統(tǒng)可能包括長(zhǎng)的一樣但意義不同的:希臘字母的數(shù)學(xué)符號(hào)就有兩個(gè)意思,比如 π逢享。他們也被叫成無法編碼的字符集罐监。

字符編碼

字符編碼字符到整數(shù)的映射。一個(gè)字符集的映射也被稱為一個(gè)編碼字符集字符集瞒爬。這個(gè)映射中的每個(gè)字符的值通常被稱為一個(gè)編碼(code point)弓柱。 ASCII 也是一個(gè)字符集,'a'的編碼是 97侧但,'A'是 65(十進(jìn)制)矢空。
字符編碼仍然是一個(gè)抽象的概念。它不是我們可以看到的文件或者 TCP 的包禀横。不過妇多,確和這兩個(gè)概念很像,它就是一種把人抽象出來的概念轉(zhuǎn)化為數(shù)字的映射關(guān)系燕侠。

字符編碼

字符的交互(傳輸)和存儲(chǔ)都要以某種方式編碼者祖。要發(fā)送一個(gè)字符串,你需要將字符串中的所有字符進(jìn)行編碼绢彤。每種字符集都有很多的編碼方案七问。
例如,7 位字節(jié) ASCII 編碼可以轉(zhuǎn)換成 8 位字節(jié)(8 進(jìn)制)茫舶。所以械巡,ASCII 的'A'(編碼值 65)可以被編碼為 8 進(jìn)制的 01000001。不過饶氏,另一種不同的編碼方式對(duì)最高位別有用途讥耗,如奇偶校驗(yàn),帶有奇校驗(yàn)的 ASCII 編碼“A”將是這個(gè) 8 進(jìn)制數(shù)11000001疹启。還有一些協(xié)議古程,如 Sun的 XDR,使用 32 位字長(zhǎng)編碼 ASCII 編碼喊崖。所以挣磨,'A'將被編碼為0000000000000000000000001000001。
字符編碼是在程序應(yīng)用層面使用的荤懂。應(yīng)用程序處理編碼的字符時(shí)茁裙,是否帶包含奇偶校驗(yàn)處理8 位字符或 32 位字符,顯然有很大的差別节仿。
把字符編碼擴(kuò)展到字符串晤锥。一個(gè)字節(jié)寬、帶有奇偶校驗(yàn)的“ABC”編碼為 10000000(高位奇偶校驗(yàn))0100000011(C)01000010(B)01000001(A 在低位)廊宪。對(duì)于編碼在字符串上的討論也很重要矾瘾,雖然編碼規(guī)則可能不同眉踱。

編碼傳輸

某個(gè)應(yīng)用程序的字符編碼只要內(nèi)部能處理字符串就足夠了。然而霜威,一旦你需要在不同應(yīng)用程序之間交互谈喳,那怎么編碼可就成了需要進(jìn)一步討論問題了:字節(jié)、字符戈泼、字是怎么傳輸?shù)男銮荨W址幋a可能有很多空白字符(待商議),從而可以使用如 zip 算法對(duì)文本進(jìn)行壓縮大猛,從而節(jié)省帶寬陨享”菜或者炬称,它可以減少到 7 位字節(jié)堰塌,奇偶校驗(yàn)位,使用 base64 編碼來代替唉堪。
如果我們知道的字符編碼和傳輸編碼模聋,那么問題就成了如何通過編程處理字符和字符串;如果我們不知道字符編碼和傳輸編碼唠亚,那么如何猜到某個(gè)特定字符串的編碼方式就是大問題链方。因?yàn)闆]有約定發(fā)送文件的字符編碼
不過,在互聯(lián)網(wǎng)上傳輸文本的編碼是有約定的灶搜。很簡(jiǎn)單:文本消息頭包含的編碼信息祟蚀。例如,HTTP 報(bào)頭可以包含這么幾行割卖,如

Content-Type: text/html; charset=ISO-8859-4
Content-Encoding: gzip

上面是說前酿,將字符集是 ISO 8859-4(對(duì)應(yīng)到歐洲的某些國(guó)家)作為默認(rèn)編碼,然后用 gzip壓縮鹏溯。內(nèi)容類型的第二部分就是我們指的是“傳輸編碼”(IETF RFC2130)罢维。
但是,怎么讀懂這個(gè)信息呢剿涮?它沒有編碼言津?這不就是先有雞還是先有蛋的問題么攻人?嗯取试,不是的。按照慣例怀吻,這樣的信息使用 ASCII 編碼(準(zhǔn)確地說瞬浓,美國(guó) ASCII),所以程序可以讀取headers蓬坡,然后適配其文檔的其余部分的編碼猿棉。

ASCII 編碼

ASCII 字符集包含的英文字符磅叛、數(shù)字標(biāo)點(diǎn)符號(hào)和一些控制字符萨赁。
最常見的** ASCII 編碼使用 7 位字節(jié)**弊琴,所以 A 的碼是 65。
這個(gè)字符集是實(shí)際的美國(guó) ASCII杖爽。鑒于歐洲需要處理重音字符敲董,于是省略一些標(biāo)點(diǎn)字符,形成一個(gè)最小的字符集慰安,ISO 646腋寨,同時(shí)有合適的歐洲本國(guó)字符的“國(guó)家變種字符集”。有興趣的可以看看 Jukka Korpel 的這個(gè)網(wǎng)頁(yè) http://www.cs.tut.fi/?jkorpela/ chars.html化焕。

ISO 8859 字符集

8 進(jìn)制是字節(jié)的標(biāo)準(zhǔn)長(zhǎng)度萄窜。這使得 ASCII 可以有 128 個(gè)額外的編碼。 ISO 8859 系列的字符集可以包含眾多的歐洲語(yǔ)言字符集撒桨。查刻。 ISO 8859-1 也被稱為 Latin-1,覆蓋了許多在西歐國(guó)家的語(yǔ)言凤类,同時(shí)這一系列的其他字符集包括歐洲其他國(guó)家赖阻,甚至希伯來語(yǔ),阿拉伯語(yǔ)和泰語(yǔ)踱蠢。例如火欧,ISO 8859-5 包括使用斯拉夫語(yǔ)字符的俄羅斯等,而 ISO 8859-8 則包含希伯來文字母茎截。
這些字符集使用 8進(jìn)制作為標(biāo)準(zhǔn)的編碼格式苇侵。例如,在 ISO 8859-1 字符' 'á'的字符編碼為 193企锌,同時(shí)被編碼為 193榆浓。所有的 ISO 8859 系列前 128 個(gè)保持和 ASCII 相同的值,所以撕攒,ASCII 字符在所有這些集合都是相同的陡鹃。
HTML 語(yǔ)言規(guī)范曾經(jīng)推薦 ISO 8859-1 字符集,不過 HTML3.2 之后的規(guī)范就不再推薦抖坪,4.0 開始推薦 Unicode 編碼萍鲸。2010 年 Google 通過它抓取的網(wǎng)頁(yè)做出了一個(gè)估算,20%的網(wǎng)頁(yè)使用ISO 8859 編碼擦俐,20%使用 ASCII(unicode 接近 50%脊阴,

Unicode 編碼

ASCII 和 ISO 8859 都不能覆蓋象形文字。中文大約有 20000 個(gè)獨(dú)立的字符,其中 5000 個(gè)常用字符嘿期。這些字符需要不止一個(gè)字節(jié)品擎,基本上雙字節(jié)都會(huì)被用上。也有一些多字節(jié)的編碼:中文的 Big5, EUC-TW, GB2312 和 GBK/GBX备徐,日文的 JIS X 0208萄传,等等。這些編碼通常是不兼容的
Unincode 是一個(gè)受到擁護(hù)的字符集編碼標(biāo)準(zhǔn)蜜猾,旨在統(tǒng)一主要使用的編碼盲再。它包含了歐洲文字、亞洲文字和印度文字等“晗常現(xiàn)在 Unicode 已經(jīng)到了 5.2 的版本答朋,包含 107,0000 個(gè)字符。編碼字符超過 65536棠笑,也就是 2^16梦碗。這已經(jīng)覆蓋了整個(gè)編碼。
(Unicode 編碼)前 256 個(gè)編碼對(duì)應(yīng) ISO 8859-1蓖救,同時(shí)前 128 個(gè)也是美式 ASCII 編碼洪规。所以主流的編碼都是相互兼容的,ISO 8859-1循捺、ASCII 和 Unicode 是一樣的斩例。對(duì)其他字符集則不一定正確:例如,雖然 Big5 編碼也在 Unicode 中从橘,但他們的編碼值并不相同念赶。http://moztw.org/docs/big5/table/unicode1.1-obsolete.txt 這個(gè)頁(yè)面就是證明:一張 Big5 到Unicode 的大的映射表。
為了在計(jì)算機(jī)系統(tǒng)中表示 Unicode 字符恰力,必須使用一個(gè)編碼方案叉谜。UCS 編碼使用兩個(gè)字節(jié)來編碼一個(gè)字符值。然而踩萎,Unicode 現(xiàn)在有太多的字符需要對(duì)應(yīng)到雙字節(jié)的編碼停局。以下方案是替代原來陳舊的編碼方案的:

  • UTF-32 使用 4 個(gè)字節(jié)編碼,但是已經(jīng)不再推薦香府,HTML5 甚至嚴(yán)重警告反對(duì)使用
  • UTF-16 是最常見的董栽,它通過溢出兩個(gè)字節(jié)來處理 ASCII 和 ISO 8859-1 外的字符
  • UTF-8 每個(gè)字符使用 1 到 4 個(gè)字節(jié),所以 ASCII 值不變企孩,但 ISO 8859-1 的值會(huì)變化
  • UTF-7 有時(shí)會(huì)用到锭碳,但不常見

UTF-8, Go 語(yǔ)言和 runes

UTF - 8 是最常用的編碼。谷歌估計(jì)它抓取的網(wǎng)頁(yè)有 50%使用 UTF-8 編碼柠硕。ASCII 字符集具有相同的在 UTF-8 中編碼值相同工禾,所以 UTF-8 的讀取方法可以用 Unicode 字符集讀取一個(gè)ASCII 字符組成的網(wǎng)頁(yè)运提。
Go 語(yǔ)言使用 UTF-8 編碼字符串蝗柔。每個(gè)字符類型都是 rune闻葵。rune 是 int32 的一個(gè)別名,因?yàn)閁nicode 編碼可以是 1,2 或 4 個(gè)字節(jié)癣丧。字符和字符串其實(shí)都是一個(gè) runes 的數(shù)組
Unicode 中一個(gè)字符串其實(shí)是一個(gè)字節(jié)數(shù)組槽畔,但是你要注意:只有 ASCII 這個(gè)字符集是一個(gè)字節(jié)等于一個(gè)字符。所有其他字符占用 2 個(gè)胁编,三個(gè)或四個(gè)字節(jié)厢钧。這意味著,一個(gè)字符串的長(zhǎng)度(runes)通常是不一樣的長(zhǎng)度的字節(jié)數(shù)組嬉橙。他們只有在全是 ASCII 字符是才相同早直。
下面的程序片段可以說明這些。如果我們使用 utf-8 來檢驗(yàn)它的長(zhǎng)度市框,你只會(huì)得到它字符層面的長(zhǎng)度霞扬。但如果你把字符串轉(zhuǎn)換成 rues 數(shù)組[]rune,你就等到一個(gè) Unicode 編碼的數(shù)組:

str := "百度一下枫振,你就知道"
println("String length", len([]rune(str)))
println("Byte length", len(str)) 

輸出為

String length 9
Byte length 27 

UTF-8 編碼的客戶端和服務(wù)端

可能令人驚訝的是喻圃,無論是客戶端或服務(wù)器你不需要對(duì) utf-8 的文本做任何特殊的處理。UTF-8 字符串的數(shù)據(jù)類型是一個(gè)字節(jié)數(shù)組粪滤,如上所示斧拍。Go 語(yǔ)言自動(dòng)處理編碼后的字符串是1,2杖小,3 或 4 個(gè)字節(jié)肆汹。所以 utf-8 的字符串你可以隨便寫。
類似于讀取字符串予权,只要讀入一個(gè)字節(jié)數(shù)組县踢,然后使用 string([]byte)將數(shù)組轉(zhuǎn)換成一個(gè)字符串。如果 Go 語(yǔ)言不能正確解碼伟件,將字節(jié)轉(zhuǎn)換為 Unicode 字符硼啤,那么它給使用 Unicode 替換字符\uFFFD。生成的字節(jié)數(shù)組的長(zhǎng)度是有效字符串的長(zhǎng)度斧账。
所以前面章節(jié)中提到的客戶端和服務(wù)端使用 uft-8 編碼表現(xiàn)的很好

ASCII 編碼的客戶端和服務(wù)器

ASCII 字符的 ASCII 編碼和 UTF-8 編碼的值相同谴返,所以普通的 UTF-8 字符能正常處理 ASCII字符,不需要做任何特殊的處理咧织。

Go 語(yǔ)言和 utf-16

utf-16 編碼可以用 16 位字節(jié)無符號(hào)整形數(shù)組處理嗓袱。 utf16 包就是用來處理這樣的字串的。將一個(gè) Go 語(yǔ)言的 utf-8 正常編碼的字串轉(zhuǎn)換 utf-16 的編碼习绢,你應(yīng)先將字串轉(zhuǎn)換成[]rune數(shù)組渠抹,然后使用 utf16.Encode 生成一個(gè) uint16 類型的數(shù)組蝙昙。
同樣,解碼一個(gè)無符號(hào)短整型的 utf-16 數(shù)組成一個(gè) Go 字符串梧却,你需要 utf16.Decode 將編碼轉(zhuǎn)換成[]rune 奇颠,然后才能改成一個(gè)字符串。如下面的代碼所示:

str := "百度一下放航,你就知道"
runes := utf16.Encode([]rune(str))
ints := utf16.Decode(runes)
str = string(ints) 

類型轉(zhuǎn)換需要客戶端和服務(wù)器在合適的時(shí)機(jī)讀取和寫入 16 位的整數(shù)

Little-endian 和 big-endian

然而烈拒,UTF-16 編碼潛藏著一個(gè)小的惡魔。它基本上是一個(gè) 16 字節(jié)字符編碼广鳍。最大的問題是:每一個(gè)短字荆几,是如何拼寫的?高位在前還是高位在后赊时?無論哪種方式吨铸,只要是發(fā)生器和接收器約定好就可以
Unicode 通過一個(gè)特殊字節(jié)標(biāo)記了尋址方式,這個(gè)字節(jié)就被稱為 BOM(字節(jié)順序標(biāo)記)祖秒。這是一個(gè)零寬度非打印字符诞吱,所以你永遠(yuǎn)不會(huì)在文本中看到它。但是它通過 0xFFFE 的值狈涮,可以告訴你編碼的順序

  • 在 big-endian 系統(tǒng)中狐胎,它是 FF FE
  • 在 little-endian 系統(tǒng)中,它是 FE FF

有時(shí) BOM 會(huì)位于文本的第一個(gè)字符歌馍。文本被讀入時(shí)可以檢查握巢,以確定使用的是那種系統(tǒng)。

UTF-16 編碼的客戶端和服務(wù)器

根據(jù) BOM 的約定松却,服務(wù)器可以預(yù)先設(shè)置 BOM 來表示 utf-16,如下

/* UTF16 Server
*/

package main

import (
    "fmt"
    "net" 
    "os"
    "unicode/utf16"
 )
 
 const BOM= '\ufffe'
 
func main() {
    service := "0.0.0.0:1210"
    tcpAddr, err := net.ResolveTCPAddr("tcp", service)
    checkError(err) 
 
    listener, err := net.ListenTCP("tcp", tcpAddr)
    checkError(err)
 
    for{
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
 
        str := "j'ai arrêté"
        shorts := utf16.Encode([]rune(str))
        writeShorts(conn, shorts)
 
        conn.Close() // we're finished
    }
 }
 
func writeShorts(conn net.Conn, shorts []uint16) {
    var bytes [2]byte
    // send the BOM as first two bytes
    bytes[0] = BOM>> 8
    bytes[1] = BOM&255
 
    _, err := conn.Write(bytes[0:])
 
    if err != nil {
        return 
    }
 
    for _, v := range shorts {
        bytes[0] = byte(v >> 8)
        bytes[1] = byte(v & 255)
        _, err = conn.Write(bytes[0:])
 
        if err != nil {
            return
        }
    }
}
 
func checkError(err error) {
    if err != nil {
        fmt.Println("Fatal error ", err.Error())
        os.Exit(1)
    }
} 

但客戶端讀取一個(gè)字節(jié)流暴浦,提取并檢查 BOM 時(shí)解碼該流的其余部分的。

/* UTF16 Client
*/

package main

import (
    "fmt"
    "net"
    "os"
    "unicode/utf16"
)
 
const BOM= '\ufffe' 
 
func main() {
    if len(os.Args) != 2 {
        fmt.Println("Usage: ", os.Args[0], "host:port")
        os.Exit(1)
    }
 
    service := os.Args[1]
    conn, err := net.Dial("tcp", service)
    checkError(err)
 
    shorts := readShorts(conn)
    ints := utf16.Decode(shorts)
 
    str := string(ints)
    fmt.Println(str)
 
    os.Exit(0)
 }
  
func readShorts(conn net.Conn) []uint16{
    var buf [512]byte
    // read everything into the buffer
    n, err := conn.Read(buf[0:2])
    for true {
        err := conn.Read(buf[n:])
        if m == 0 || err != nil {
            break
        }
        n += m
    }
    checkError(err)
 
    var shorts []uint16
    shorts = make([]uint16, n/2)
 
    if buf[0] == 0xff && buf[1] == 0xfe {
        // big endian
        for i := 2; i <n; i += 2 {
            shorts[i/2] = uint16(buf[i])<<8 + uint16(buf[i+1]) )<<8 + uint16(buf[i+1])
        }
    } else if buf[1] == 0xff && buf[0] == 0xfe {
        // little endian
        for i := 2; i < n; i += 2 {
            shorts[i/2] = uint16(buf[i+1])<<8 + uint16(buf[i]) 1])<<8 + uint16(buf[i])
        }
    } else{
        // unknown byte order
        fmt.Println("Unknown order")
    }
    return shorts
}
 
func checkError(err error) {
    if err != nil {
        fmt.Println("Fatal error ", err.Error())
        os.Exit(1)
    }
} 

Unicode 的疑難雜癥

這本書不是有關(guān)國(guó)際化問題晓锻。特別是歌焦,我們不想鉆研的神秘的 Unicode。但是你應(yīng)該知道砚哆,Unicode 不是一個(gè)簡(jiǎn)單的編碼独撇,也有很多的復(fù)雜的地方。例如躁锁,一些早期的字符集用非空格字符纷铣,尤其是重音字符。這些重音字符要轉(zhuǎn)換成 Unicode 可以用兩種辦法:作為一個(gè) Unicode字符战转,或作為一個(gè)非空格字符和非重音字符的組合搜立。例如, U+04D6 CYRILLIC CAPITAL LETTER IE WITH BREVE 是一個(gè)字符槐秧。這是相當(dāng)于 U+0415 CYRILLIC CAPITAL LETTER IE 和 U+0306 加上 BREVE.啄踊。這使得字符串比較有時(shí)變得困難了忧设。 GO 規(guī)范確目前沒有對(duì)這個(gè)問題過深研究。

ISO 8859 編碼和 Go 語(yǔ)言

ISO 8859 系列字符集都是 8 位字符集颠通,他們?yōu)闅W洲不同地區(qū)和其他一些地方設(shè)計(jì)址晕。他們有相同的 ASCII 并且都在地位,但高位不同蒜哀。據(jù)谷歌估計(jì)斩箫,ISO 8859 編碼了盡 20%的網(wǎng)頁(yè)吏砂。
第一個(gè)編碼字符集撵儿,ISO 8859-1 或叫做 Latin-1,前 256 個(gè)字符和 Unicode 相同狐血。 Latin-1 字符的 utf-16 和 ISO 8859-1 有相同的編碼淀歇。但是,這并不真的有用匈织,因?yàn)?UTF-16 是一個(gè) 16位的編碼字符集而 ISO 8859-1 是 8 位編碼浪默。 UTF-8 是一種 8 位編碼,但是高位用來表示更多的字符缀匕,所以只有 ASCII 的一部分是 utf-8 和 ISO 8859-1 相同纳决,所以UTF-8 并沒有多大實(shí)際用途(都是 8 位的)
但 ISO8859 系列沒有任何復(fù)雜的問題乡小。每一組中的每個(gè)字符對(duì)應(yīng)一個(gè)唯一的 Unicode 字符阔加。例如,在 ISO 8859-2 中的字符“l(fā)atin capital letter I with ogonek”在 ISO 8859-2 是 0xc7(十六進(jìn)制)满钟,對(duì)應(yīng)的 Unicode 的 U+012E胜榔。 ISO 8859 字符集和 Unicode 字符集之間轉(zhuǎn)換其實(shí)只是一個(gè)表查找。
這個(gè)從 ISO 8859 到 Unicode 的查找表湃番,可以用一個(gè) 256 的數(shù)組完成夭织。因?yàn)椋S多字符索引相同吠撮。因此尊惰,我們只需要一個(gè)標(biāo)注不同索引的映射就可以。
ISO 8859-2 的映射為

var unicodeToISOMap = map[int] uint8 {
    0x12e: 0xc7,
    0x10c: 0xc8,
    0x118: 0xca,
    // plus more
}

從 utf-8 轉(zhuǎn)換成 ISO 8859-2 的函數(shù)

/*Turn a UTF-8 string into an ISO 8859 encoded byte array 8 string into an ISO 8859
*/

func unicodeStrToISO(str string) []byte {
    // get the unicode code points
    codePoints := []int(str)
    // create a byte array of the same length
    bytes := make([]byte, len(codePoints))
    for n, v := range(codePoints) {
        // see if the point is in the exception map tion map
        iso, ok := unicodeToISOMap[v]
        if !ok {
            // just use the value
            iso = uint8(v)
        }
        bytes[n] = iso
    }
   
    return bytes
} 

同樣你可以將 ISO 8859-2 轉(zhuǎn)換為 utf-8

var isoToUnicodeMap = map[uint8] int {
    0xc7: 0x12e,
    0xc8: 0x10c,
    0xca: 0x118,
    // and more
}
   
func isoBytesToUnicode(bytes []byte) string {
    codePoints := make([]int, len(bytes))
    for n, v := range(bytes) {
        unicode, ok :=isoToUnicodeMap[v]
        if !ok {
            unicode = int(v) unicode = int(v) unicode = int(v)
        }
        codePoints[n] = unicode
    }
   
    return string(codePoints)
} 

這些函數(shù)可以用來將 ISO 8859-2 當(dāng)作 UTF-8 來讀寫泥兰。通過改變映射表弄屡,可以覆蓋其他的 ISO8859 字符集合。Latin-1 字符集(ISO 8859-1)是一個(gè)特殊的情況:地圖映射為空逾条,因?yàn)樽址?Latin-1 和 Unicode 中編碼相同琢岩。同樣的方法,你也可以使用其他字符集構(gòu)建映射表师脂,如Windows1252担孔。

其他字符集和 Go 語(yǔ)言

還有非常非常多的字符集編碼江锨。據(jù)谷歌稱,這些字符集通常只有很少地方使用糕篇,所以可能用的會(huì)更少啄育。但是,如果你的軟件要占據(jù)所有市場(chǎng)拌消,那么你可能需要對(duì)這些字符集進(jìn)行處理挑豌。
在最簡(jiǎn)單的情況下,查找表就夠了墩崩。但是氓英,這樣也不是總是奏效。ISO 2022 字符編碼方案通過……鹦筹。這是從日本某寫編碼中借用來個(gè)铝阐,相當(dāng)復(fù)雜。
Go 語(yǔ)言目前在語(yǔ)言本身和包文件上支持其他字符集铐拐。所以徘键,你要么避免使用其他字符集,雖然沒法和用這些字符集的程序共存遍蟋,要么自己動(dòng)手寫很多代碼吹害。

總結(jié)

這一章沒有什么代碼,卻有幾個(gè)非常復(fù)雜的概念虚青。當(dāng)然它呀,也取決于你:你要只滿足說美式英語(yǔ)的人,那問題就簡(jiǎn)單了挟憔;要是你的應(yīng)用也要讓其他人可用钟些,那你就要在這個(gè)復(fù)雜的問題上花點(diǎn)精力了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绊谭,一起剝皮案震驚了整個(gè)濱河市政恍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌达传,老刑警劉巖篙耗,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異宪赶,居然都是意外死亡宗弯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門搂妻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蒙保,“玉大人,你說我怎么就攤上這事欲主〉瞬蓿” “怎么了逝嚎?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)详恼。 經(jīng)常有香客問我补君,道長(zhǎng),這世上最難降的妖魔是什么昧互? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任挽铁,我火速辦了婚禮,結(jié)果婚禮上敞掘,老公的妹妹穿的比我還像新娘叽掘。我一直安慰自己,他們只是感情好渐逃,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布够掠。 她就那樣靜靜地躺著民褂,像睡著了一般茄菊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赊堪,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天面殖,我揣著相機(jī)與錄音,去河邊找鬼哭廉。 笑死脊僚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的遵绰。 我是一名探鬼主播辽幌,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼椿访!你這毒婦竟也來了乌企?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤成玫,失蹤者是張志新(化名)和其女友劉穎加酵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哭当,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡猪腕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了钦勘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陋葡。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖彻采,靈堂內(nèi)的尸體忽然破棺而出腐缤,到底是詐尸還是另有隱情朵栖,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布柴梆,位于F島的核電站陨溅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏绍在。R本人自食惡果不足惜门扇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望偿渡。 院中可真熱鬧臼寄,春花似錦、人聲如沸溜宽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)适揉。三九已至留攒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嫉嘀,已是汗流浹背炼邀。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留剪侮,地道東北人拭宁。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瓣俯,于是被迫代替她去往敵國(guó)和親杰标。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 字符是用戶可以讀寫的最小單位。計(jì)算機(jī)所能支持的字符組成的集合推掸,就叫做字符集桶蝎。字符集通常以二維表的形式存在。二維表的...
    劉惜有閱讀 8,097評(píng)論 2 14
  • UTF-8 編碼提供了一種簡(jiǎn)便而向后兼容的方法, 使得那種完全圍繞 ASCII 設(shè)計(jì)的操作系統(tǒng), 比如 Unix,...
    謝大見閱讀 4,680評(píng)論 0 3
  • 騰訊大講堂——字符編碼的前世今生字符串谅畅,那些你不知道的事編碼字符集標(biāo)準(zhǔn)及分類研究通信用語(yǔ)の基礎(chǔ)知識(shí) —— ISO/...
    AItsuki閱讀 1,405評(píng)論 0 4
  • 爸爸媽媽登渣,我心情不知道該怎么形容,但是我覺得我需要去真實(shí)面對(duì)你們 首先我很愧疚毡泻,我并不了解你們活了30歲胜茧。我不了解...
    心我聽你說閱讀 284評(píng)論 0 0
  • 書接上回。 這個(gè)人就是劉裕。據(jù)說也是漢室宗親呻顽。祖籍彭城(今徐州)雹顺,世居京口(今鎮(zhèn)江)。 他父親名...
    畢徽閱讀 682評(píng)論 0 2