Go 裡的rune型別是int32的別名。
由於底層是int32型別,rune型別儲存的是帶正負號的 32 位元整數值。
不過,rune型別和int32型別不同的地方在於,它儲存的整數值代表單一的 Unicode 字元。
Unicode 是 ASCII 的超集合,它為每個字元指定一個獨一無二的數字來表示該字元。 這個獨一無二的數字稱為 Unicode 碼點。 Unicode 的目標,是把世界上所有的字元,包括各種字母、數字、符號,甚至 emoji,都用 Unicode 碼點來表示。
在 Go 裡,rune型別代表單一的 Unicode 碼點。
下表列出幾個 Unicode 字元範例,以及它們的 Unicode 碼點和十進位值:
| Unicode 字元 | Unicode 碼點 | 十進位值 |
|---|---|---|
| 0 | U+0030 |
48 |
| A | U+0041 |
65 |
| a | U+0061 |
97 |
| ¿ | U+00BF |
191 |
| π | U+03C0 |
960 |
| 🧠 | U+1F9E0 |
129504 |
UTF-8 是一種可變寬度的字元編碼,用來把每個 Unicode 碼點編碼成 1、2、3 或 4 個位元組。
由於一個 Unicode 碼點最多可以編碼成 4 個位元組,rune型別必須能容納最多 4 個位元組的資料。
這就是rune型別是int32別名的原因,因為int32型別正好能容納最多 4 個位元組的資料。
Go 的原始碼檔案是以 UTF-8 編碼。
宣告rune型別的變數時,把一個字元放在單引號裡就行了:
myRune := '¿'
由於rune只是int32的別名,印出 rune 的型別會得到int32:
myRune := '¿'
fmt.Printf("myRune type: %T\n", myRune)
// Output: myRune type: int32
同樣地,印出 rune 的值會得到它的整數(十進位)值:
myRune := '¿'
fmt.Printf("myRune value: %v\n", myRune)
// Output: myRune value: 191
若要印出 rune 所代表的 Unicode 字元,請使用%c格式化動詞:
myRune := '¿'
fmt.Printf("myRune Unicode character: %c\n", myRune)
// Output: myRune Unicode character: ¿
若要印出 rune 所代表的 Unicode 碼點,請使用%U格式化動詞:
myRune := '¿'
fmt.Printf("myRune Unicode code point: %U\n", myRune)
// Output: myRune Unicode code point: U+00BF
Go 的字串是以 UTF-8 編碼,這表示字串裡包含的是 Unicode 字元。 字串中的字元會依它所代表的 Unicode 字元,儲存並編碼成 1、2、3 或 4 個位元組。
在 Go 裡,序列是用切片來表示,而這些切片可以用 range 疊代。
當我們對字串做疊代時,Go 會把字串轉換成一連串的 rune,每個 rune 佔 4 個位元組(別忘了,rune 型別是int32的別名!)
雖然字串只是一段位元組切片,但range關鍵字疊代的是字串的 rune,而不是它的位元組。
在這個範例中,index變數代表目前 rune 位元組序列的起始索引,char變數則代表目前的 rune:
myString := "❗hello"
for index, char := range myString {
fmt.Printf("Index: %d\tCharacter: %c\t\tCode Point: %U\n", index, char, char)
}
// Output:
// Index: 0 Character: ❗ Code Point: U+2757
// Index: 3 Character: h Code Point: U+0068
// Index: 4 Character: e Code Point: U+0065
// Index: 5 Character: l Code Point: U+006C
// Index: 6 Character: l Code Point: U+006C
// Index: 7 Character: o Code Point: U+006F
由於 rune 可以佔 1、2、3 或 4 個位元組,字串的長度不一定等於字串中的字元數。
用內建的len函式取得字串以位元組計的長度,並用utf8.RuneCountInString函式取得字串中的 rune 數量:
import "unicode/utf8"
myString := "❗hello"
stringLength := len(myString)
numberOfRunes := utf8.RuneCountInString(myString)
fmt.Printf("myString - Length: %d - Runes: %d\n", stringLength, numberOfRunes)
// Output: myString - Length: 8 - Runes: 6
你的任務是建立一個日誌函式庫,用來協助管理組織的日誌。 這個函式庫讓使用者能辨識某則日誌是由哪個應用程式發出、修復損毀的日誌,以及判斷某一行日誌是否在特定的字元數限制之內。
日誌來自多個應用程式,每個應用程式都使用自己專屬的日誌格式。 日誌在存入日誌聚合系統之前,必須先辨識出發出它的應用程式。
實作Application函式,它接受一行日誌,並回傳發出這行日誌的應用程式。
若要辨識某一行日誌是由哪個應用程式發出,請依照下表搜尋這行日誌中的特定字元:
| 應用程式 | 字元 | Unicode 碼點 |
|---|---|---|
recommendation |
❗ | U+2757 |
search |
🔍 | U+1F50D |
weather |
☀ | U+2600 |
如果某一行日誌不包含上表中的任何字元,就對呼叫方回傳default。
如果某一行日誌包含上表中多個字元,就回傳從左到右在這行日誌中第一個找到的字元所對應的應用程式。
Application("❗ recommended search product 🔍")
// => recommendation
由於日誌基礎架構中存在一個罕見但持續發生的 bug,日誌中的某些字元可能會損毀。 花了一些時間找出損毀的字元及其原始值之後,你決定更新日誌函式庫,來協助修復損毀的日誌。
實作Replace函式,它接受一行日誌、一個損毀的字元,以及原始值,並回傳一行修改後的日誌,其中所有出現的損毀字元都已取代為原始值。
log := "please replace '👎' with '👍'"
Replace(log, '👎', '👍')
// => please replace '👍' with '👍'"
負責顯示日誌的系統對於每行日誌可顯示的字元數設有限制。 因此,使用者希望這個函式庫包含一個輔助函式,用來判斷某一行日誌是否在特定的字元數限制之內。
實作WithinLimit函式,它接受一行日誌與字元數限制,並回傳這行日誌是否在字元數限制之內。
WithinLimit("hello❗", 6)
// => true