前言
在 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!)