Go語言中多位元組字元的處理
1 概述
Go語言的字串是使用 UTF-8
編碼的。 UTF-8
是 Unicode
的實現方式之一。本文內容包括: UTF-8
和 Unicode
的關係,Go語言提供的 unicode
包和 unicode/utf8
包的使用。
2 UTF-8
和 Unicode
的關係
Unicode
一種字符集,是國際標誰化組織(ISO)設計的一個包括了地球上所有文化、所有字母和符號 的編碼。他們叫它 Universal Multiple-Octet Coded Character Set
,簡稱 UCS,也就是 Unicode
。 Unicode
為每一個 字元 分配一個唯一的 碼點(Code Point),就是一個唯一的值。例如 康 的碼點就是 24247,十六進位制為 5eb7
。
Unicode
字符集僅僅定義了字元與碼點的對應關係,但是並沒有定義該如何編碼(儲存)這個碼值,這就導致了很多問題。例如由於字元的碼值不同,導致所需要的儲存空間是不一致的,計算機不能確定接下來的字元是佔用幾個位元組。還有就是如果採用固定的長度假設都是4個位元組來儲存碼點值,那麼會導致空間的額外浪費,因為 ascii
碼字元其實僅僅需要一個位元組的空間。
UTF-8
就是解決如何為 Unicode
編碼而設計的一種編碼規則。可以說 UTF-8
是 Unicode
的實現方式之一。其特點是一種變長編碼,使用1到4個位元組表示一個字元,根據不同的符號而變化長度。 UTF-8
的編碼規則有二:
- 對於單位元組的符號,位元組的第一位設為0,後面7位為這個符號的
Unicode
碼。因此對於ASCII碼字元,UTF-8
編碼和ASCII
碼是相同的。 - 對於 n 位元組的符號(n > 1,2到4),第一個位元組的前n位都設為1,第n + 1 位設為 0,後面位元組的前兩位一律設為10。剩下的沒有提及的二進位制位,全部為這個符號的
Unicode
碼。
以下是編碼規則:
Unicode| UTF-8 --------------------------------------------------------- 0000 0000-0000 007F | 0xxxxxxx 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx ---------------------------------------------------------
Go語言中,對於 Unicode 和 UTF-8 使用了 unicode
和 unicode/utf8
包來實現,下面是閱讀 API 的總結和說明。
3 Unicode
包
Go語言中,提供了 Unicode
包,處理與 Unicode
相關的操作,整理如下:
Is(rangeTab *RangeTable, r rune) bool
檢測 rune r 是否在 rangeTable
指定的字元範圍內。
rangeTable
一個 Unicode
碼值集合,通常使用 unicode
包中定義的集合。
判斷字元是否出現在漢字集合中: unicode.Is(unicode.Scripts["Han"], 'k') // 返回 false unicode.Is(unicode.Scripts["Han"], '康') // 返回 true
In(r rune, ranges ...*RangeTable) bool
檢測 rune r 是否在多個 rangeTable
指定的字元範圍內。
rangeTable
一個 Unicode
碼值集合,通常使用 unicode
包中定義的集合。
unicode.In('康', unicode.Scripts["Han"], unicode.Scripts["Latin"]) // 返回 true unicode.In('k', unicode.Scripts["Han"], unicode.Scripts["Latin"]) // 返回 true
IsOneOf(ranges []*RangeTable, r rune) bool
檢測 rune r 是否在 rangeTable
ranges 指定的字元範圍內。與 In
功能類似,推薦使用 In
。
IsSpace(r rune) bool
檢測字元 rune r 是否是空白字元。在Latin-1字元空間中,空白字元為:
'\t', '\n', '\v', '\f', '\r', ' ', U+0085 (NEL), U+00A0 (NBSP)
其它的空白字元請參見策略Z和屬性Pattern_White_Space。
IsDigit(r rune) bool
檢測字元 rune r 是否是十進位制數字字元。
unicode.IsDigit('9') // 返回 true unicode.IsDigit('k') // 返回 false
IsNumber(r rune) bool
檢測字元 rune r 是否是 Unicode
數字字元。
IsLetter(r rune) bool
檢測一個字元 rune r 是否是字母
unicode.IsLetter('9') // 返回 false unicode.IsLetter('k') // 返回 true
IsGraphic(r rune) bool
一個字元 rune r 是否是 unicode 圖形字元。圖形字元包括字母、標記、數字、符號、標點、空白。
unicode.IsGraphic('9') // 返回 true unicode.IsGraphic(',') // 返回 true
IsControl(r rune) bool
檢測一個字元 rune r 是否是 unicode 控制字元。
IsMark(r rune) bool
檢測一個字元 rune r 是否是標記字元。
IsPrint(r rune) bool
檢測一個字元 rune r 是否是的可列印字元,基本與圖形字元一致,除ASCII空白字元U+0020。
IsPunct(r rune) bool
檢測一個字元 rune r 是否是 unicode標點字元。
unicode.IsPunct('9') // 返回 false unicode.IsPunct(',') // 返回 true
IsSymbol(r rune) bool
檢測一個字元 rune r 是否是 unicode 符號字元。
IsLower(r rune) bool
檢測一個字元 rune r 是否是小寫字母。
unicode.IsLower('h') // 返回 true unicode.IsLower('H') // 返回 false
IsUpper(r rune) bool
檢測一個字元 rune r 是否是大寫字母。
unicode.IsUpper('h') // 返回 false unicode.IsUpper('H') // 返回 true
IsTitle(r rune) bool
檢測一個字元 rune r 是否是Title字元。大部分字元的 Title 格式就是其大寫格式,少數字符的 Title 格式是特殊字元,例如 ᾏᾟᾯ
。
unicode.IsTitle('ᾯ') // 返回 true unicode.IsTitle('h') // 返回 false unicode.IsTitle('H') // 返回 true
To(_case int, r rune) rune
將字元 rune r 轉換為指定的格式,格式_case支援:unicode.UpperCase、unicode.LowerCase、unicode.TitleCase
unicode.To(unicode.UpperCase, 'h') // 返回 H
ToLower(r rune) rune
將字元 rune r 轉換為小寫。
unicode.ToLower('H') // 返回 h
func (SpecialCase) ToLower
將字元 rune r 轉換為小寫。優先使用對映表 SpecialCase。
對映表 SpecialCase 是特定語言環境下大小寫的對映表。主要應用於一些歐洲字元,例如土耳其 TurkishCase。
unicode.TurkishCase.ToLower('İ') // 返回 i
ToUpper(r rune) rune
將字元 rune r 轉換為大寫。
unicode.ToUpper('h') // 返回 H
func (SpecialCase) ToUpper
將字元 rune r 轉換為大寫。優先使用對映表 SpecialCase。
對映表 SpecialCase 是特定語言環境下大小寫的對映表。主要應用於一些歐洲字元,例如土耳其 TurkishCase。
unicode.TurkishCase.ToUpper('i') // 返回 İ
ToTitle(r rune) rune
將字元 rune r 轉換為 Title 字元。
unicode.ToTitle('h') // 返回 H
func (SpecialCase) ToTitle
將字元 rune r 轉換為 Title 字元。優先使用對映表 SpecialCase。
對映表 SpecialCase 是特定語言環境下大小寫的對映表。主要應用於一些歐洲字元,例如土耳其 TurkishCase。
unicode.TurkishCase.ToTitle('i') // 返回 İ
SimpleFold(r rune) rune
在 unicode 標準字元對映中查詢與 rune r 互相對應的 unicode 碼值。向碼值大的方向迴圈查詢。互相對應指的是同一個字元可能出現的多種寫法。
unicode.SimpleFold('H') // 返回 h unicode.SimpleFold('Φ')) // 返回 φ
4 unicode/utf8
包
DecodeLastRune(p []byte) (r rune, size int)
解碼 []byte p 中最後一個 UTF-8 編碼序列,返回該碼值和長度。
utf8.DecodeLastRune([]byte("小韓說課")) // 返回 35838 3 // 35838 就是課的 unicode 碼值
DecodeLastRuneInString(s string) (r rune, size int)
解碼 string s 中最後一個 UTF-8 編碼序列,返回該碼值和長度。
utf8.DecodeLastRuneInString("小韓說課") // 返回 35838 3 // 35838 就是課的 unicode 碼值
DecodeRune(p []byte) (r rune, size int)
解碼 []byte p 中第一個 UTF-8 編碼序列,返回該碼值和長度。
utf8.DecodeRune([]byte("小韓說課")) // 返回 23567 3 // 23567 就是 小 的 unicode 碼值
DecodeRuneInString(s string) (r rune, size int)
解碼 string s 中第一個 UTF-8 編碼序列,返回該碼值和長度。
utf8.DecodeRuneInString("小韓說課") // 返回 23567 3 // 23567 就是 小 的 unicode 碼值
EncodeRune(p []byte, r rune) int
將 rune r 的 UTF-8 編碼序列寫入 []byte p,並返回寫入的位元組數。p 滿足足夠的長度。
buf := make([]byte, 3) n := utf8.EncodeRune(buf, '康') fmt.Println(buf, n) // 輸出 [229 186 183] 3
FullRune(p []byte) bool
檢測 []byte p 是否包含一個完整 UTF-8 編碼。
buf := []byte{229, 186, 183} // 康 utf8.FullRune(buf) // 返回 true utf8.FullRune(buf[:2]) // 返回 false
FullRuneInString(s string) bool
檢測 string s 是否包含一個完整 UTF-8 編碼。
buf := "康" // 康 utf8.FullRuneInString(buf) // 返回 true utf8.FullRuneInString(buf[:2]) // 返回 false
RuneCount(p []byte) int
返回 []byte p 中的 UTF-8 編碼的碼值的個數。
buf := []byte("小韓說課") len(buf) // 返回 12 utf8.RuneCount(buf) // 返回 4
RuneCountInString(s string) (n int)
返回 string s 中的 UTF-8 編碼的碼值的個數。
buf := "小韓說課" len(buf) // 返回 12 utf8.RuneCountInString(buf) // 返回 4
RuneLen(r rune) int
返回 rune r 編碼後的位元組數。
utf8.RuneLen('康') // 返回 3 utf8.RuneLen('H') // 返回 1
RuneStart(b byte) bool
檢測位元組 byte b 是否可以作為某個 rune 編碼的第一個位元組。
buf := "小韓說課" utf8.RuneStart(buf[0]) // 返回 true utf8.RuneStart(buf[1]) // 返回 false utf8.RuneStart(buf[3]) // 返回 true
Valid(p []byte) bool
檢測切片 []byte p 是否包含完整且合法的 UTF-8 編碼序列。
valid := []byte("小韓說課") invalid := []byte{0xff, 0xfe, 0xfd} utf8.Valid(valid) // 返回 true utf8.Valid(invalid) // 返回 false
ValidRune(r rune) bool
檢測字元 rune r 是否包含完整且合法的 UTF-8 編碼序列。
valid := 'a' invalid := rune(0xfffffff) fmt.Println(utf8.ValidRune(valid)) // 返回 true fmt.Println(utf8.ValidRune(invalid)) // 返回 false
ValidString(s string) bool
檢測字串 string s 是否包含完整且合法的 UTF-8 編碼序列。
valid := "小韓說課" invalid := string([]byte{0xff, 0xfe, 0xfd}) fmt.Println(utf8.ValidString(valid)) // 返回 true fmt.Println(utf8.ValidString(invalid)) // 返回 false