### 原生字符串 string类型 - 数据不可变,提高了字符串的并发安全性和存储利用率 - 没有"\0"结尾,获取长度的时间复杂度是常数时间 - 原生字符串所见即所得,构造多行字符串更加简单,多行字符串用反引号包裹 - 采用unicode字符集,对非ASCII字符提供原生日支持,消除不同环境下乱码问题 ### Go字符串组成 - 字节角度 - Go 语言中的字符串值也是一个可空的字节序列,字节序列中的字节个数称为该字符串的长度 - len方法返回字符串字节长度 - 字符视角 - 字符串是由一个可空的字符序列构成 - range 循环字符 - utf8.RuneCountInString(s)统计字符个数 ### rune类型与字面值 - Go 使用 rune 这个类型来表示一个 Unicode 码点。rune 本质上是 int32 类型的别名类型,它与 int32 类型是完全等价的 > Unicode 码点,就是指将 Unicode 字符集中的所有字符“排成一队”,字符在这个“队伍”中的位次,就是它在 Unicode 字符集中的码点。也就说,一个码点唯一对应一个字符 - 一个 rune 实例就是一个 Unicode 字符,一个 Go 字符串也可以被视为 rune 实例的集合 - 可以通过字符字面值来初始化一个 rune 变量 #### 字面值 - 通过单引号括起的字符字面值 ```go 'a' // ASCII字符 '中' // Unicode字符集中的中文字符 '\n' // 换行字符 '\'' // 单引号字符 ``` - Unicode 专用的转义字符\u 或\U 作为前缀,来表示一个 Unicode 字符 - \u 后面接两个十六进制数。如果是用两个十六进制数无法表示的 Unicode 字符,可以使用\U - \U 后面可以接四个十六进制数来表示一个 Unicode 字符 ```go '\u4e2d' // 字符:中 '\U00004e2d' // 字符:中 '\u0027' // 单引号字符 ``` - 直接用整型值来给rune赋值 ```go '\x27' // 使用十六进制表示的单引号字符 '\047' // 使用八进制表示的单引号字符 ``` ### 字符串字面值 - 使用双引号给字符串赋值 - 如果需要表示原始的值可以使用转义字符`"\\xe4\\xb8\\xad\xe5\x9b\xbd\xe4\xba\xba"` ```go "abc\n" "中国人" "\u4e2d\u56fd\u4eba" // 中国人 "\U00004e2d\U000056fd\U00004eba" // 中国人 "中\u56fd\u4eba" // 中国人,不同字符字面值形式混合在一起 "\xe4\xb8\xad\xe5\x9b\xbd\xe4\xba\xba" // 十六进制表示的字符串字面值:中国人 ``` ### Go字符串内部表示 - 本身并不真正存储字符串数据 - 由一个指向底层存储的指针和字符串的长度字段组成的 - 获取字符串长度,时间复杂度为O(1) - 直接将 string 类型通过函数 / 方法参数传入也不会带来太多的开销 ```go // $GOROOT/src/reflect/value.go // StringHeader是一个string的运行时表示 type StringHeader struct { Data uintptr Len int } ``` ```go package main import ( "fmt" "reflect" "unsafe" ) func dumpBytesArray(arr []byte) { fmt.Printf("[") for _, b := range arr { fmt.Printf("%c ", b) } fmt.Printf("]\n") } func main() { var s = "hello" hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // 将string类型变量地址显式转型为reflect.StringHeader fmt.Printf("0x%x\n", hdr.Data) // 0x495db9 p := (*[5]byte)(unsafe.Pointer(hdr.Data)) // 获取Data字段所指向的数组的指针 dumpBytesArray((*p)[:]) // [h e l l o ] // 输出底层数组的内容 } ``` ### 字符串转换 - `string`、`[]byte`和`[]rune`互相转换 ```go var s string = "中国人" // string -> []rune rs := []rune(s) fmt.Printf("%x\n", rs) // [4e2d 56fd 4eba] // string -> []byte bs := []byte(s) fmt.Printf("%x\n", bs) // e4b8ade59bbde4baba // []rune -> string s1 := string(rs) fmt.Println(s1) // 中国人 // []byte -> string s2 := string(bs) fmt.Println(s2) // 中国人 ``` ### 字符串拼接 - `+` 操作符 - fmt.Sprintf - strings.Join - strings.Builder - bytes.Buffer ```go var sl []string = []string{ "Rob Pike ", "Robert Griesemer ", "Ken Thompson ", } func concatStringByOperator(sl []string) string { var s string for _, v := range sl { s += v } return s } func concatStringBySprintf(sl []string) string { var s string for _, v := range sl { s = fmt.Sprintf("%s%s", s, v) } return s } func concatStringByJoin(sl []string) string { return strings.Join(sl, "") } func concatStringByStringsBuilder(sl []string) string { var b strings.Builder for _, v := range sl { b.WriteString(v) } return b.String() } func concatStringByStringsBuilderWithInitSize(sl []string) string { var b strings.Builder b.Grow(64) for _, v := range sl { b.WriteString(v) } return b.String() } func concatStringByBytesBuffer(sl []string) string { var b bytes.Buffer for _, v := range sl { b.WriteString(v) } return b.String() } func concatStringByBytesBufferWithInitSize(sl []string) string { buf := make([]byte, 0, 64) b := bytes.NewBuffer(buf) for _, v := range sl { b.WriteString(v) } return b.String() } func BenchmarkConcatStringByOperator(b *testing.B) { for n := 0; n < b.N; n++ { concatStringByOperator(sl) } } func BenchmarkConcatStringBySprintf(b *testing.B) { for n := 0; n < b.N; n++ { concatStringBySprintf(sl) } } func BenchmarkConcatStringByJoin(b *testing.B) { for n := 0; n < b.N; n++ { concatStringByJoin(sl) } } func BenchmarkConcatStringByStringsBuilder(b *testing.B) { for n := 0; n < b.N; n++ { concatStringByStringsBuilder(sl) } } func BenchmarkConcatStringByStringsBuilderWithInitSize(b *testing.B) { for n := 0; n < b.N; n++ { concatStringByStringsBuilderWithInitSize(sl) } } func BenchmarkConcatStringByBytesBuffer(b *testing.B) { for n := 0; n < b.N; n++ { concatStringByBytesBuffer(sl) } } func BenchmarkConcatStringByBytesBufferWithInitSize(b *testing.B) { for n := 0; n < b.N; n++ { concatStringByBytesBufferWithInitSize(sl) } } ``` ### []byte和string互相转换不拷贝方法 - go 1.20之前 ```go // toBytes performs unholy acts to avoid allocations func toBytes(s string) []byte { return *(*[]byte)(unsafe.Pointer(&s)) } // toString performs unholy acts to avoid allocations func toString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } func ByteArray2String(arr []byte) (str string) { sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&arr)) stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&str)) stringHeader.Data = sliceHeader.Data stringHeader.Len = sliceHeader.Len return } func String2ByteArray(str string) (arr []byte) { stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&str)) sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&arr)) sliceHeader.Data = stringHeader.Data sliceHeader.Len = stringHeader.Len sliceHeader.Cap = stringHeader.Len return } ``` - go 1.20之后 > 常用的 reflect.SliceHeader 和 reflect.StringHeader 将会被标注为被废弃 ```go func StringToBytes(s string) []byte { return unsafe.Slice(unsafe.StringData(s), len(s)) } func BytesToString(b []byte) string { return unsafe.String(&b[0], len(b)) } ```