<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
剛接觸 Go 語言時,就聽說有一個叫rune
的資料型別,即使查閱過一些資料,對它的理解依舊比較模糊,加之對陌生事物的天然排斥,在之後很長一段時間的程式設計工作中,我都沒有讓它出現在我的程式碼裡。
逃避雖然有用,但是似乎有些可恥,想要成為一名成熟、優秀的 Go 語言開發工程師,必須要有直面陌生事物並且成功運用的勇氣和能力,帶著這樣的覺悟,讓我們一起走近rune
,直視它!
瞭解一下,rune
型別究竟是什麼?
rune
型別是 Go 語言的一種特殊數位型別。在builtin/builtin.go
檔案中,它的定義:type rune = int32
;官方對它的解釋是:rune
是型別int32
的別名,在所有方面都等價於它,用來區分字元值跟整數值。使用單引號定義 ,返回採用 UTF-8 編碼的 Unicode 碼點。Go 語言通過rune
處理中文,支援國際化多語言。
眾所周知,Go 語言有兩種型別宣告方式:一種叫型別定義宣告,另一種叫型別別名宣告。其中,別名的使用在大型專案重構中作用最為明顯,它能解決程式碼升級或遷移過程中可能存在的型別相容性問題。而rune
跟byte
是 Go 語言中僅有的兩個型別別名,專門用來處理字元。當然,我們也可以通過type
關鍵字加等號的方式宣告更多的型別別名。
學習一下,rune
型別怎麼用?
我們知道,字串由字元組成,字元的底層由位元組組成,而一個字串在底層的表示是一個位元組序列。在 Go 語言中,字元可以被分成兩種型別處理:對佔 1 個位元組的英文類字元,可以使用byte
(或者unit8
);對佔 1 ~ 4 個位元組的其他字元,可以使用rune
(或者int32
),如中文、特殊符號等。
下面,我們通過範例應用來具體感受一下。
統計帶中文字串長度
// 使用內建函數 len() 統計字串長度 fmt.Println(len("Go語言程式設計")) // 輸出:14
前面說到,字串在底層的表示是一個位元組序列。其中,英文字元佔用 1 位元組,中文字元佔用 3 位元組,所以得到的長度 14 顯然是底層佔用位元組長度,而不是字串長度,這時,便需要用到rune
型別。
// 轉換成 rune 陣列後統計字串長度 fmt.Println(len([]rune("Go語言程式設計"))) // 輸出:6
這回對了。很容易,我們解鎖了rune
型別的第一個功能,即統計字串長度。
如果想要擷取字串中 ”Go語言“ 這一段,考慮到底層是一個位元組序列,或者說是一個陣列,通常情況下,我們會這樣:
s := "Go語言程式設計" // 8=2*1+2*3 fmt.Println(s[0:8]) // 輸出:Go語言
結果符合預期。但是,按照位元組的方式進行擷取,必須預先計算出需要擷取字串的位元組數,如果位元組數計算錯誤,就會顯示亂碼,比如這樣:
s := "Go語言程式設計" fmt.Println(s[0:7]) // 輸出:Go語�
此外,如果擷取的字串較長,那通過位元組的方式進行擷取顯然不是一個高效準確的辦法。那有沒有不用計算位元組數,簡單又不會出現亂碼的方法呢?不妨試試這樣:
s := "Go語言程式設計" // 轉成 rune 陣列,需要幾個字元,取幾個字元 fmt.Println(string([]rune(s)[:4])) // 輸出:Go語言
到這裡,我們解鎖了rune
型別的第二個功能,即擷取字串。
思考一下,為什麼rune
型別可以做到?
通過上面的範例,我們發現似乎在處理帶中文的字串時,都需要用到rune
型別,這究竟是為什麼呢?除了使用rune
型別,還有其他方法嗎?
在深入思考之前,我們需要首先弄清楚string
、byte
、rune
三者間的關係。
字串在底層的表示是由單個位元組組成的一個不可修改的位元組序列,位元組使用UTF-8[1]編碼標識Unicode[2]文字。Unicode 文字意味著.go
檔案內可以包含世界上的任意語言或字元,該檔案在任意系統上開啟都不會亂碼。UTF-8 是 Unicode 的一種實現方式,是一種針對 Unicode 可變長度的字元編碼,它定義了字串具體以何種方式儲存在記憶體中。UFT-8 使用 1 ~ 4 為每個字元編碼。
Go 語言把字元分byte
和rune
兩種型別處理。byte
是型別unit8
的別名,用於存放佔 1 位元組的 ASCII 字元,如英文字元,返回的是字元原始位元組。rune
是型別int32
的別名,用於存放多位元組字元,如佔 3 位元組的中文字元,返回的是字元 Unicode 碼點值。如下圖所示:
s := "Go語言程式設計" // byte fmt.Println([]byte(s)) // 輸出:[71 111 232 175 173 232 168 128 231 188 150 231 168 139] // rune fmt.Println([]rune(s)) // 輸出:[71 111 35821 35328 32534 31243]
它們的對應關係如下圖:
瞭解了這些,我們再回過來看看,剛才的問題是不是清楚明白很多?接下來,讓我們再來看看原始碼中是如何處理的,以utf8.RuneCountInString()[3]函數為例。
範例:
// 統計字串長度 fmt.Println(utf8.RuneCountInString("Go語言程式設計")) // 輸出:6
原始碼:
// RuneCountInString is like RuneCount but its input is a string. func RuneCountInString(s string) (n int) { // 呼叫 len() 函數得到位元組數 ns := len(s) for i := 0; i < ns; n++ { c := s[i] // 如碼點值小於 128,則為佔 1 位元組的 ASCII 字元(或者說英文字元),長度 + 1 if c < RuneSelf { // RuneSelf = 128 // ASCII fast path i++ continue } // 查詢首位元組資訊表,得到中文佔 3 位元組,所以這裡的 x = 3 x := first[c] // 判斷 x = 3,xx = 241(0xF1) if x == xx { i++ // invalid. continue } // 提取有效的 UTF-8 位元組長度編碼資訊,size = 3 size := int(x & 7) if i+size > ns { i++ // Short or invalid. continue } // 提取有效位元組範圍 accept := acceptRanges[x>>4] // accept.lo,accept.hi,表示 UTF-8 中第二位元組的有效範圍 // locb = 0b10000000,表示 UTF-8 編碼非首位元組的數值下限 // hicb = 0b10111111,表示 UTF-8 編碼非首位元組的數值上限 if c := s[i+1]; c < accept.lo || accept.hi < c { size = 1 } else if size == 2 { } else if c := s[i+2]; c < locb || hicb < c { size = 1 } else if size == 3 { } else if c := s[i+3]; c < locb || hicb < c { size = 1 } i += size } return n }
呼叫該函數時,傳入一個原始的字串,程式碼會根據每個字元的碼點大小判斷是否為 ASCII 字元,如果是,則算做 1 位;如果不是,則查詢首位元組表,明確字元佔用的位元組數,驗證有效性後再進行計數。
在我看來,rune
型別只是一種名稱叫法,表示用來處理長度大於 1 位元組( 8 位)、不超過 4 位元組( 32 位)的字元型別。但萬變不離其宗,我們使用函數時,無論傳入引數的是原始字串還是rune
,最終都是對位元組進行處理。看似陌生的事物,沉下心瞭解到其本質以後,才發現原來並不陌生,缺少的只是正視它的勇氣!
[1]
UTF-8:https://zh.wikipedia.org/wiki/UTF-8
[2]
Unicode:https://zh.wikipedia.org/wiki/Unicode
[3]
utf8.RuneCountInString()
:https://golang.org/src/unicode/utf8/utf8.go
到此這篇關於Go 中的 rune 型別的文章就介紹到這了,更多相關Go rune 型別內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45