typora/note/Go/字符串类型.md
2024-12-12 10:48:55 +08:00

278 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

### 原生字符串 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))
}
```