我正在参加「掘金·启航计划」
1、string标准概念
在go中源代码位于src/builtin/builtin.go
定义如下:
// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string
- string是8bit字节的集合,通常是但并不一定非得是UTF-8编码的文本。
- string可以为空(长度为0),但不会是nil。
- string对象不可以修改。
2、string数据结构
源码包在src/runtime/string.go:stringStruct
中定义了string的数据结构:
type stringStruct struct {
str unsafe.Pointer //字符串首地址,指向底层字节数组的指针
len int //字符串长度
}
对于字符串Hello,实际底层结构如下:
3、string操作
3.1、声明
声明一个string变量并赋值:
var str string
str = "Hello"
字符串构建过程是先根据字符串构建stringStruct,再转换成string,源码如下:
func gostringnocopy(str *byte) string {//根据字符串地址构建string
ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)} //先构造stringStruct
s := *(*string)(unsafe.Pointer(&ss))//再将stringStruct转换成string
return s
}
string在runtime包中就是stringStruct,对外呈现叫做string。
3.2、[]byte转string
[]byte切片转换成string很简单:
func GetStringBySlice(s []byte) string {
return string(s)
}
需要注意的是这种转换需要一次内存拷贝。
扫描二维码关注公众号,回复:
14307007 查看本文章
![](/qrcode.jpg)
转换过程如下:
- 根据切片的长度申请内存空间,假设内存地址为p,切片长度为len(s)
- 构建string(sting.str =p; string.len=len)
- 拷贝数据(切片中数据拷贝到新申请的内存空间)
3.2、string转[]byte
func GetSliceByString(str string) []byte {
return []byte(str)
}
string转换成[]byte切片也需要一次内存拷贝。
- 申请切片内存空间
- 将string拷贝到切片
3.3、字符串拼接
在Go语言中,字符串是不可变得,拼接字符串事实上是创建了一个新的字符串,如果代码中存在大量的字符串拼接,对性能会产生影响。
func concatstrings(buf *tmpBuf, a []string) string {
idx := 0
l := 0 //拼接后的字符串总长度
count := 0
for i, x := range a {
n := len(x)
if n == 0 {
continue
}
if l+n < l {
throw("string concatenation too long")
}
l += n
count++
idx = i
}
if count == 0 {
return ""
}
// If there is just one string and either it is not on the stack
// or our result does not escape the calling frame (buf != nil),
// then we can return that string directly.
if count == 1 && (buf != nil || !stringDataOnStack(a[idx])) {
return a[idx]
}
s, b := rawstringtmp(buf, l)//生成指定大小的字符串,返回一个string和切片,二者共享内存
for _, x := range a {
copy(b, x)
b = b[len(x):]//string无法修改,只能通过切片修改
}
return s
}
// 生成一个新的string,返回的string和切片共享相同的空间
func rawstring(size int) (s string, b []byte) {
p := mallocgc(uintptr(size), nil, false)
stringStructOf(&s).str = p
stringStructOf(&s).len = size
*(*slice)(unsafe.Pointer(&b)) = slice{p, size, size}
return
}
3.3.1、常见拼接方式
- 使用
+
s1 + s2 + s3
- 使用
fmt.Sprintf
fmt.Sprintf("%s%s", s1, s2)
- 使用
strings.Builder
func BuilderConcat(n int, str string) string {
var builder strings.Builder
for i := 0; i < n; i++ {
builder.WriteString(str)
}
return builder.String()
}
- 使用
bytes.Buffer
func bufferConcat(n int, s string) string {
buf := new(bytes.Buffer)
for i := 0; i < n; i++ {
buf.WriteString(s)
}
return buf.String()
}
- 使用
[]byte
func byteConcat(n int, str string) string {
buf := make([]byte, 0)
for i := 0; i < n; i++ {
buf = append(buf, str...)
}
return string(buf)
}
3.4、字符串截取
- 截取普通英文字符串
str := "HelloWorld"
content := str[1 : len(str)-1]
- 截取带中文字符串
一个中文字符确定不止一个字节,需要先将其转为[]rune,再截取后,再转为string
strRune := []rune(str)
fmt.Println("string(strRune[:4]) = ",string(strRune[:4]))
4、为什么字符串不允许修改?
在go实现中,string不包含内存空间,只有一个内存的地址,这样做的好处是string变得非常轻量,可以很方便的进行传递而不用担心内存拷贝。
string通常指向字符串字面量,而字符串字面量存储存储位置是只读段,而不是堆或栈上,所以string不可修改。
修改字符串时,可以将字符串转换为 []byte 进行修改。
var str string = "hello"
strBytes := []byte(str)
strBytes[0] = 'H'
str = string(strBytes)
fmt.Println(str)
5、字符串截取函数封装
func SubString(str string, begin, length int) string {
rs := []rune(str)
lth := len(rs)
if begin < 0 {
begin = 0
}
if begin >= lth {
begin = lth
}
end := begin + length
if end > lth {
end = lth
}
return string(rs[begin:end])
}