前言
當在 Go 字串中索引位置n時,為什麼沒有得到第n個字元?
func main() { foo := "ABC" for _, v := range foo { fmt.Println(v) // 65, 66, 67 }}相較於其他程式語言一串文字在遍歷時會預期拿到單一個字符,在 Go 會拿到「Rune」;如果直接透過索引取得 string 內容會拿到 byte:
func main() { s := "Hello世界" fmt.Println(len(s)) // 11 byte fmt.Println(s[0]) // 72 (H 的 byte 值) fmt.Println(s[5]) // 228 (世 的第一個 byte)}啥是 Rune?
要創建一個 Rune 可以透過 '' 單引號定義:
r := 'A'Rune 是內建的類型,實際上是 int32 的別名(所有方面都等價),設計用來表示一個 Unicode 碼點(code point),使得 Go 能夠正確處理各種語言的字符。
String vs Rune vs Byte
- Byte(位元組)
- uint8 的別名,代表一個 8 位元的值(0-255)
- 構成字串的基本單位
- String(字串)
- 由一系列 byte 組成的不可變序列
- 可以包含任何有效的 UTF-8 字符
- Rune(符文)
- int32 的別名
- 設計用於存放 Unicode codepoint
實際案例
轉換為 Rune Slice
func main() { s := "Hello世界" runes := []rune(s)
fmt.Println(runes[5]) // 19990 (世) fmt.Printf("%c\n", runes[5]) // 世 fmt.Println("字符數量:", len(runes)) // 7}使用 Range 遍歷
func main() { s := "Hello世界"
for i, r := range s { fmt.Printf("索引: %d, 字符: %c, Unicode: U+%04X\n", i, r, r) } // 索引: 0, 字符: H, Unicode: U+0048 // 索引: 1, 字符: e, Unicode: U+0065 // 索引: 2, 字符: l, Unicode: U+006C // 索引: 3, 字符: l, Unicode: U+006C // 索引: 4, 字符: o, Unicode: U+006F // 索引: 5, 字符: 世, Unicode: U+4E16 // 索引: 8, 字符: 界, Unicode: U+754C}Indexed capitalization
給定一個由「小寫字母」組成的字串和「一個整數索引」陣列,將指定索引處的所有字母大寫。如果索引超出字串範圍,則忽略該索引。
"abcdef", [1,2,5] ==> "aBCdeF""abcdef", [1,2,5,100] ==> "aBCdeF" // There is no index 100.初步我的想法是創建空的 []rune 並遍歷字串 st 判斷當前字元是否存在 arr 當中,如果是則推入大寫反之小寫。所以會是 O(n × m)。
import "unicode"
func Capitalize(st string, arr []int) string { result := []rune{}
for i, c := range st { if contains(arr, i) { result = append(result, unicode.ToUpper(c)) } else { result = append(result, c) } }
return string(result)}
func contains(arr []int, val int) bool { for _, v := range arr { if v == val { return true } } return false}額外留意:Byte Index 不是 Char Index
假設題目沒有輸入為英文小寫的限制。
綜合以上對 string 處理的知識,可以發現 i 實際上是 string 背後的 byte,而一個字符可能由多個 byte 構成導致出錯,應該以字元 index 為基準而非 byte index:
func CapitalizeByCharIndex(st string, arr []int) string { result := []rune{} charIndex := 0 // 手動維護字符索引
for _, c := range st { // 不用 byte 索引 i if contains(arr, charIndex) { result = append(result, unicode.ToUpper(c)) } else { result = append(result, c) } charIndex++ // 每個字符遞增 } return string(result)}換個思路:從要轉大寫的陣列下手
將 st 轉換為 []rune 背後實際上是一次遍歷轉換,再透過遍歷 arr 覆寫上大寫的字元 ,整個流程更直白是 O(n + m)。
func CapitalizeByChar(st string, arr []int) string { runes := []rune(st)
for _, idx := range arr { if idx < len(runes) { runes[idx] = unicode.ToUpper(runes[idx]) } }
return string(runes)}延伸閱讀
- Characters do not exist in Go: Everything about runes!
- The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)