7.1 KiB
7.1 KiB
原生字符串 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 变量
字面值
- 通过单引号括起的字符字面值
'a' // ASCII字符
'中' // Unicode字符集中的中文字符
'\n' // 换行字符
'\'' // 单引号字符
- Unicode 专用的转义字符\u 或\U 作为前缀,来表示一个 Unicode 字符
- \u 后面接两个十六进制数。如果是用两个十六进制数无法表示的 Unicode 字符,可以使用\U
- \U 后面可以接四个十六进制数来表示一个 Unicode 字符
'\u4e2d' // 字符:中
'\U00004e2d' // 字符:中
'\u0027' // 单引号字符
- 直接用整型值来给rune赋值
'\x27' // 使用十六进制表示的单引号字符
'\047' // 使用八进制表示的单引号字符
字符串字面值
- 使用双引号给字符串赋值
- 如果需要表示原始的值可以使用转义字符
"\\xe4\\xb8\\xad\xe5\x9b\xbd\xe4\xba\xba"
"abc\n"
"中国人"
"\u4e2d\u56fd\u4eba" // 中国人
"\U00004e2d\U000056fd\U00004eba" // 中国人
"中\u56fd\u4eba" // 中国人,不同字符字面值形式混合在一起
"\xe4\xb8\xad\xe5\x9b\xbd\xe4\xba\xba" // 十六进制表示的字符串字面值:中国人
Go字符串内部表示
- 本身并不真正存储字符串数据
- 由一个指向底层存储的指针和字符串的长度字段组成的
- 获取字符串长度,时间复杂度为O(1)
- 直接将 string 类型通过函数 / 方法参数传入也不会带来太多的开销
// $GOROOT/src/reflect/value.go
// StringHeader是一个string的运行时表示
type StringHeader struct {
Data uintptr
Len int
}
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
互相转换
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
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之前
// 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 将会被标注为被废弃
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))
}