string
基础
1 .字符串是一个不可改变的字节序列,一系列8位字节的集合
2 .字符串可以包含任何的数据,包括byte和0.
3 .字符串是一种值类型,且值不能改变,创建某个文本之后无法再次改变这个文本的内容
4 .字符串是字节的定长数组
5 .传统过的字符串是由字符组成的,go的字符串是单个字节链接起来的数组
6 .字节使用utf编码的Unicode文本
7 .字符串底层就是Byte数组
8 .[]byte方法和[]rune方法就是将字符串转化成对应类型的slice
9 .字符串常量一般使用单引号括起来的单个字符
10 .在golang中,字符,字符串中打印单个字符的本质就是一个整数,是改字符对应的utf-8编码的码值
11 .可以给某个变量赋一个数字,然后格式化初始时%c,会输出对应数字的unicode字符
12 .因为字符的本质时一个整数,所以字符类型是可以计算的。
13 .字符-计算机:字符-对应码值-二进制
14 .计算机-字符:二进制-码值-字符
type stringStruct struct { str unsafe.Pointer len int } //str:指向某个数组的指针
字符串类型
1 .解释型字符串
1 .使用双引号包起来,其中的转义字符串将被替换 2 .\n 3 .\r 4 .\t 5 .\u 6 .\\
2 .非解释字符串
1 .使用``包起来.支持换行 2 .
字符串基于UTF8编码
1 .通过rune类型,可以方便的对每个UTF8字符进行访问
2 .
s:="123 飞机" b:=[]byte(s) for _,v:=range b{ fmt.Printf("%v \n",v) } fmt.Println(string(b)) for _,v:=range s{ fmt.Printf("%c \n",v) } for _,v:=range []rune(s){ fmt.Printf("%c \n",v) }
字符串的长度
1 .len:计算切片,字符串,通道的长度。返回值的类型为 int,表示字符串的 ASCII
字符个数或字节长度.ASCII 字符串长度使用 len() 函数。
2 .统计utf8字符串的长度.Unicode 字符串长度使用 utf8.RuneCountInString() 函数
字符串遍历
1 .不管是asicc还是utf8都使用for range就可以了,不会出现乱码。唯一缺点就是中文下标不准确,不是递增的
2 .
str:="love 哈哈" for _,v:=range str{ fmt.Printf("%c\n",v) // fmt.Println(v) } //怎么都会输出正确的 for i:=0;i<len(str);i++{ fmt.Printf("%c\n",str[i]) // fmt.Println(str[i]) }
3 .len遍历:按照字节进行遍历,如果有非英文字节,就会出现乱码
4 .切片遍历:先将字符串转为[]rune切片,然后按照常规方式进行遍历
func main(){ str:="hello 北京" s:=[]rune(str) fmt.Println(s) for i:=0;i<len(s);i++{ fmt.Printf("str[%d]=%c",i,s[i]) } } //步长是按照1递增的,没有进行跳跃
字符串截取
1 .索引的起使位置可以通过切片偏移制作
s:="死神来了, 阿斯蒂死神啦啦啦" index:=strings.Index(s,",") // 返回第一个”,“号的索引 fmt.Println(index) pos:=strings.Index(s[index:],"死神") //这样直接切割是有问题的 // 接入第一个”,“号之后的字符串,寻找”死神"在新字符串的位置 fmt.Println(pos) fmt.Println(s[index+pos:]) // 把两个位置相加,取出最后想要的串 fmt.Println(b) // byte表示一个字节 fmt.Println(c) // rune表示四个字节 fmt.Println(s[:1]) fmt.Println(string(b[:1])) fmt.Println(string(c[:1]))
修改字符串
1 .go无法直接修改每一个字符元素,只能重新构造新的字符串赋值给原来的字符串变量实现
2 .修改字符串的时候,可以将字符串转换为[]byte进行修改
3 .[]byte和string可以通过强制类型互相转换
s1:="helllo" s2:=[]byte(s1) //将字符串转为byte切片 for i:=3;i<len(s2);i++{ s2[i]=' ' } //3-len之间的元素使用空格替换 fmt.Println(string(s2)) // 利用string方法将[]byte转为字符串,重新创造一个字符串
字符串拼接
1 .+号:每次加号都会产生一个新的字符串,导致很多临时的,无用的字符串,会给垃圾回收带来很大的压力
2 .高速缓冲实现
var stringBuilder bytes.Buffer // 声明一个字节缓冲 stringBuilder.WriteString("李") stringBuilder.WriteString("巴") // 向里面添加数据 fmt.Println(stringBuilder.String()) // 将缓冲以字符串形式输出
3 .fmt.Sprintf()函数
func main(){ x := "hello" for _, y := range x { x=fmt.Sprintf("%s %s",x,string(y)) } fmt.Println(x) } 1 .格式化样式:字符串形式,如s%,s% 2 .参数列表:多个参数以逗号隔开,个数必须和格式化样式中的个数一一对应 3 .功能:将参数列表中的多个参数以格式化的形式输出 4 .缺点:虽然不会产生临时字符串,但是内部逻辑比较复杂,而且有很多额外的判断,性能也一般
4 .strings.join()函数
func main(){ str:=[]string{"a","b","c","d"} s:=strings.Join(str,"-") //用-来连接 fmt.Println(s) } 1 .Join会根据字符串数组的内容,计算出一个拼接后的长度,然后申请对应大小的内存,依次将该字符串填入。 2 .该种方式在已有一个数组的前提下,可以提高程序的执行效率,但实际上,原先内存中并没有为拼接之后的字符串所创建好的数组,所以本质上创建这个数据的代价并不小
5 .连接性能比较
1 .较少字符串使用+号 2 .如果拼接的不仅仅是字符串,还有数字之类的其他需求,使用fmt.Sprintf函数 3 .在已有字符串数组的情况下,使用strings.Join 4 .在性能要求高的场景下,使用buffer.WriteString()函数有更好的性能 5 .
str:="love 123 飞机" for _,v:=range str{ // fmt.Printf("%T\n",v) // 输出字符的类型 // fmt.Printf("%v\n",v) // 返回本来的值 // fmt.Printf("%U\n",v) // Unicdoe格式 // fmt.Printf("%#v\n",v) // fmt.Printf("%s\n",v) // fmt.Printf("%c\n",v) // 值对应的unicode值 // fmt.Printf("%b\n",v) // 值对应的二进制 // fmt.Printf("%t\n",v) // 布尔值 // fmt.Printf("%%\n",v) // 百分号 fmt.Printf("%p\n",v) }
utf8,unicode,ascii
1 .Unicdoe,ascii都是一种字符集,为每一个字符分配唯一的id
2 .utf8是一种编码规则,将unicode中的id以某种方式进行编码然后输出
3 .