暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Golang的string解析

lyp分享的地方 2021-03-06
439

本文介绍Golang的内置类型string
(字符串)的一些用法和注意事项。

文件reflect/value.go
,描述了内置类型string
的运行时结构。Data
是一个指针,Len
是长度。

type StringHeader struct {
Data uintptr
Len int
}

Golang中,string
是不可变的,多个数据可以共享同一份底层数据(Data).

// stringptr 返回 string data的指针
func stringptr(s string) uintptr {
return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
}

func TestShare(t *testing.T) {
s1 := "1234"
s2 := s1[:2] / "12"
// s1,s2是不同字符串,指向同一份字符串数据
t.Log(stringptr(s1) == stringptr(s2)) // true

s3 := "12"
s4 := "1" + "2"
// Golang编译期间内部化了字符串常量
t.Log(stringptr(s3) == stringptr(s4)) // true

s5 := "12"
s6 := strconv.Itoa(12)
// 运行时产生的字符串不能内部化
t.Log(stringptr(s5) == stringptr(s6)) // false
}

类型转换

string
Data
byte
切片,string
可以和[]byte
相互转换。

slice的运行时结构如下,和StringHeader
基本一致。

type SliceHeader struct {
Data uintptr
Len int
Cap int
}

在直接使用string
转出[]byte
,会发生内存拷贝。

func String2Bytes(s string) []byte {
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := reflect.SliceHeader{
Data: sh.Data,
Len: sh.Len,
Cap: sh.Len,
}
return *(*[]byte)(unsafe.Pointer(&bh))
}

func Benchmark_NormalString2Bytes(b *testing.B) {
x := "Hello Gopher! Hello Gopher! Hello Gopher!"
for i := 0; i < b.N; i++ {
_ = []byte(x)
}
}

func Benchmark_String2Bytes(b *testing.B) {
x := "Hello Gopher! Hello Gopher! Hello Gopher!"
for i := 0; i < b.N; i++ {
_ = String2Bytes(x)
}
}

运行测试

go test -bench=. -benchmem -run=^Benchmark_$    

goos: darwin
goarch: amd64
pkg: github.com/liangyaopei/GolangTester/str
Benchmark_NormalString2Bytes-8 27215298 40.2 ns/op 48 B/op 1 allocs/op
Benchmark_String2Bytes-8 1000000000 0.306 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/liangyaopei/GolangTester/str 1.494s


遍历

遍历有2中方式:

for-range
: 将字符串按照rune
来解析。
下标遍历: 取到的是byte

for-range
遍历

func TestRangeStr(t *testing.T) {
const nihongo = "日本語"
for index, runeValue := range nihongo {
t.Logf("%#U starts at byte position %d\n", runeValue, index)
}
}

// === RUN TestRangeStr
// str_test.go:59: U+65E5 '日' starts at byte position 0
// str_test.go:59: U+672C '本' starts at byte position 3
// str_test.go:59: U+8A9E '語' starts at byte position 6
// --- PASS: TestRangeStr (0.00s)

下标遍历

func TestRangeStr2(t *testing.T) {
const nihongo = "日本語"
for i := 0; i < len(nihongo); i++ {
t.Logf("%v starts at byte position %d\n", nihongo[i], i)
}
}

内部化

字符串内部化(string intern)是指一种让相同的字符串在内存中只保存一份的技术。对于要存储大量字符串的应用来说,它可以显著降低内存占用。

package main

import (
"fmt"
"strconv"
)

type stringInterner map[string]string

func (si stringInterner) Intern(s string) string {
if interned, ok := si[s]; ok {
return interned
}
si[s] = s
return s
}

func main() {
si := stringInterner{}
s1 := si.Intern("12")
s2 := si.Intern(strconv.Itoa(12))
fmt.Println(stringptr(s1) == stringptr(s2)) // true
}

参考文献

1.Strings, bytes, runes and characters in Go[1]2.String interning in Go[2]

我的公众号:lyp分享的地方

我的知乎专栏: https://zhuanlan.zhihu.com/c_1275466546035740672

我的博客:www.liangyaopei.com[3]

Github Page: https://liangyaopei.github.io/

References

[1]
 Strings, bytes, runes and characters in Go: https://blog.golang.org/strings
[2]
 String interning in Go: https://artem.krylysov.com/blog/2018/12/12/string-interning-in-go/
[3]
 www.liangyaopei.com: http://www.liangyaopei.com


文章转载自lyp分享的地方,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论