哈嘍,你們好,我是asong
。今天咱們一塊兒來看看Go
語言中的rune
數據類型,首先從一道面試題入手,你能很快說出下面這道題的答案嗎?
func main() { str := "Golang夢工廠" fmt.Println(len(str)) fmt.Println(len([]rune(str))) }
運行結果是15
和15
仍是15
和9
呢?先思考一下,一會揭曉答案。前端
其實這並非一道面試題,是我在平常開發中遇到的一個問題,當時場景是這樣的:後端要對前端傳來的字符串作字符校驗,產品的需求是限制爲200字符,而後我在後端作校驗時直接使用len(str) > 200
來作判斷,結果出現了bug
,前端字符校驗沒有超過200
字符,調用後端接口確一直是參數錯誤,改爲使用len([]rune(str)) > 200
成功解決了這個問題。具體緣由咱們在文中揭曉。面試
Unicode
和字符編碼在介紹rune
類型以前,咱們仍是要從一些基礎知識開始。 ------ Unicode
和字符編碼。後端
Unicode
?咱們都知道計算機只能處理數字,若是想要處理文本須要轉換爲數字才能處理,早些時候,計算機在設計上採用8bit
做爲一個byte
,一個byte
表示的最大整數就是255
,想表示更大的整數,就須要更多的byte
。顯然,一個字節表示中文,是不夠的,至少須要兩個字節,並且還不能和ASCII編碼衝突,因此,我國制定了GB2312
編碼,用來把中文編進去。可是世界上有不少語言,不一樣語言制定一個編碼,就會不可避免地出現衝突,因此unicode
字符就是來解決這個痛點的。Unicode
把全部語言都統一到一套編碼裏。總結來講:"unicode其實就是對字符的一種編碼方式,能夠理解爲一個字符---數字的映射機制,利用一個數字便可表示一個字符。"數組
雖然unicode
把全部語言統一到一套編碼裏了,可是他卻沒有規定字符對應的二進制碼是如何存儲。以漢字「漢」爲例,它的 Unicode
碼點是 0x6c49
,對應的二進制數是 110110001001001
,二進制數有 15
位,這也就說明了它至少須要 2
個字節來表示。能夠想象,在 Unicode
字典中日後的字符可能就須要 3
個字節或者 4
個字節,甚至更多字節來表示了。分佈式
這就致使了一些問題,計算機怎麼知道你這個 2
個字節表示的是一個字符,而不是分別表示兩個字符呢?這裏咱們可能會想到,那就取個最大的,假如 Unicode
中最大的字符用 4
字節就能夠表示了,那麼咱們就將全部的字符都用 4
個字節來表示,不夠的就往前面補 0
。這樣確實能夠解決編碼問題,可是卻形成了空間的極大浪費,若是是一個英文文檔,那文件大小就大出了 3
倍,這顯然是沒法接受的。函數
因而,爲了較好的解決 Unicode
的編碼問題, UTF-8
和 UTF-16
兩種當前比較流行的編碼方式誕生了。UTF-8
是目前互聯網上使用最普遍的一種 Unicode
編碼方式,它的最大特色就是可變長。它可使用 1 - 4
個字節表示一個字符,根據字符的不一樣變換長度。在UTF-8
編碼中,一個英文爲一個字節,一箇中文爲三個字節。學習
Go
語言中的字符串先來看一下官方對string
的定義:ui
// string is the set of all strings of 8-bit bytes, conventionally but not // necessarily representing UTF-8-encoded text. A string may be empty, but // not nil. Values of string type are immutable. type string string
人工翻譯:編碼
string
是8
位字節的集合,一般但不必定表明UTF-8
編碼的文本。string
能夠爲空,但不能爲nil
。string
的值是不能改變的
說得通俗一點,其實字符串其實是隻讀的字節切片,對於字符串底層而言就是一個byte
數組,不過這個數組是隻讀的,不容許修改。spa
寫個例子驗證一下:
func main() { byte1 := []byte("Hl Asong!") byte1[1] = 'i' str1 := "Hl Asong!" str1[1] = 'i' }
對於byte
的操做是可行,而string
操做會直接報錯:
cannot assign to str1[1]
因此說string
修改操做是不容許的,僅僅支持替換操做。
根據前面的分析,咱們也能夠得出咱們將字符存儲在字符串中時,也就是按字節進行存儲的,因此最後存儲的實際上是一個數值。
Go
語言的字符串編碼上面咱們介紹了字符串的基本概念,接下來咱們看一下Go
語言中的字符串編碼是怎樣的。
Go
源代碼爲 UTF-8
編碼格式的,源代碼中的字符串直接量是 UTF-8
文本。因此Go
語言中字符串是UTF-8
編碼格式的。
Go
語言字符串循環Go
語言中字符串可使用range
循環和下標循環。咱們寫一個例子,看一下兩種方式循環有什麼區別:
func main() { str := "Golang夢工廠" for k,v := range str{ fmt.Printf("v type: %T index,val: %v,%v \n",v,k,v) } for i:=0 ; i< len(str) ; i++{ fmt.Printf("v type: %T index,val:%v,%v \n",str[i],i,str[i]) } }
運行結果:
v type: int32 index,val: 0,71 v type: int32 index,val: 1,111 v type: int32 index,val: 2,108 v type: int32 index,val: 3,97 v type: int32 index,val: 4,110 v type: int32 index,val: 5,103 v type: int32 index,val: 6,26790 v type: int32 index,val: 9,24037 v type: int32 index,val: 12,21378 v type: uint8 index,val:0,71 v type: uint8 index,val:1,111 v type: uint8 index,val:2,108 v type: uint8 index,val:3,97 v type: uint8 index,val:4,110 v type: uint8 index,val:5,103 v type: uint8 index,val:6,230 v type: uint8 index,val:7,162 v type: uint8 index,val:8,166 v type: uint8 index,val:9,229 v type: uint8 index,val:10,183 v type: uint8 index,val:11,165 v type: uint8 index,val:12,229 v type: uint8 index,val:13,142 v type: uint8 index,val:14,130
根據運行結果咱們能夠得出以下結論:
使用下標遍歷獲取的是ASCII
字符,而使用Range
遍歷獲取的是Unicode
字符。
rune
數據類型官方對rune
的定義以下:
// rune is an alias for int32 and is equivalent to int32 in all ways. It is // used, by convention, to distinguish character values from integer values. type rune = int32
人工翻譯:
rune
是int32
的別名,在全部方面都等同於int32
,按照約定,它用於區分字符值和整數值。
說的通俗一點就是rune
一個值表明的就是一個Unicode
字符,由於一個Go
語言中字符串編碼爲UTF-8
,使用1-4
字節就能夠表示一個字符,因此使用int32
類型範圍就能夠完美適配。
前面說了這麼多知識點,確實有點亂了,咱們如今就根據開始的那道題來作一個總結。爲了方便查看,在貼一下這道題:
func main() { str := "Golang夢工廠" fmt.Println(len(str)) fmt.Println(len([]rune(str))) }
這道題的正確答案是15
和9
。
具體緣由:
len()
函數是用來獲取字符串的字節長度,rune
一個值表明的就是一個Unicode
字符,因此求rune
切片的長度就是字符個數。由於在utf-8
編碼中,英文佔1
個字節,中文佔3
個字節,因此最終結果就是15
和9
。
貼個圖,方便理解:
unicode/utf8
庫若是你們對rune
的使用不是很明確,能夠學習使用一下Go
標準庫unicode/utf8
,其中提供了多種關於rune
的使用方法。好比上面這道題,咱們就可使用utf8.RuneCountInString
方法獲取字符個數。更多庫函數使用方法請自行解鎖,本篇就不作過多介紹了。
針對全文,咱們作一個總結:
UTF-8
Go
語言的字符串能夠包含任意字節,字符底層是一個只讀的byte
數組。Go
語言中字符串能夠進行循環,使用下表循環獲取的acsii
字符,使用range
循環獲取的unicode
字符。Go
語言中提供了rune
類型用來區分字符值和整數值,一個值表明的就是一個Unicode
字符。Go
語言中獲取字符串的字節長度使用len()
函數,獲取字符串的字符個數使用utf8.RuneCountInString
函數或者轉換爲rune
切片求其長度,這兩種方法均可以達到預期結果。好啦,這篇文章就到這裏啦,素質三連(分享、點贊、在看)都是筆者持續創做更多優質內容的動力!
建立了一個Golang學習交流羣,歡迎各位大佬們踊躍入羣,咱們一塊兒學習交流。入羣方式:關注公衆號獲取。更多學習資料請到公衆號領取。
我是asong,一名普普統統的程序猿,讓咱們一塊兒慢慢變強吧。歡迎各位的關注,咱們下期見~~~
推薦往期文章: