为了账号安全,请及时绑定邮箱和手机立即绑定

golang 数据一 (字符串、数组和数组指针)

标签:
Go

从如下几个方面介绍GO语言的数据

1. 字符串

2. 数组

3. 切片

4. 字典

5. 结构

字符串

Go语言中的字符串是由一组不可变的字节(byte)序列组成从源码文件中看出其本身是一个复合结构

string.go 

type stringStruct struct {

    str unsafe.Pointer    

    len int

}

字符串中的每个字节都是以UTF-8编码存储的Unicode字符字符串的头部指针指向字节数组的开始但是没有NULL或'\0'结尾标志。 表示方式很简单用双引号("")或者反引号(``)它们的区别是

双引号之间的转义符会被转义而反引号之间的转义符保持不变

反引号支持跨行编写而双引号则不可以

{

    println("hello\tgo")    //输出hello    go

    println(`hello\tgo`)    //输出hello\tgo

}

{

    println( "hello 

    go" )//syntax error: unexpected semicolon or newline, expecting comma or )

    println(`hello                                                                                                                                                                                                 

        go`) //可以编译通过

}

输出

hello 

    go

在前面类型的章节中描述过字符串的默认值是""而不是nil,比如

var s string

println( s == "" )  //true

println( s == nil ) //invalid operation: s == nil (mismatched types string and nil)

Go字符串支持 "+ , += , == , != , < , >" 六种运算符

Go字符串允许用索引号访问字节数组(非字符)但不能获取元素的地址比如

{         var a = "hello"     println(a[0])       //输出 104     println(&a[1])      //cannot take the address of a[1] }

Go字符串允许用切片的语法返回子串(起始和结束索引号)比如

var a = "0123456"                                                                                                                                                                                               println(a[:3])      //0,1,2 println(a[1:3])     //1,2 println(a[3:])      //3,4,5,6

日常开发中经常会有遍历字符串的场景比如

{     var a = "Go语言"     for i:=0;i < len(a);i++{                //以byte方式按字节遍历         fmt.Printf("%d: [%c]\n", i, a[i])     }     for i, v := range a{                    //以rune方式遍历                                                                                                        fmt.Printf("%d: [%c]\n", i, v)     } } 输出     0: [G] 1: [o] 2: [è] 3: [ˉ] 4: [-] 5: [è] 6: [¨] 7: [] 0: [G] 1: [o] 2: [语] 5: [言]

在Go语言中字符串的底层使用的是不可以改变的byte数组存的所以在用byte轮询方式时每次得到的只有一个byte而中文字符则是占3个byte的。rune采用计算字符串长度的方式与byte方式不同比如

println(utf8.RuneCountInString(a)) // 结果为  4 println(len(a)) // 结果为 8

所以如果想要获得期待的那种结果的话需要先将字符串a转换为rune切片再使用内置的len函数比如:

{     r := []rune(a)     for i:= 0;i < len(r);i++{         fmt.Printf("%d: [%c]\n", i, r[i])     } }

所以在遍历或处理的字符串的情况下如果其中存在中文尽量使用rune方式处理。

转换

前面讲过不能修改原字符串如果修改的话需要将字符串转换成[]byte或[]rune , 然后在转换回来比如

{     var a = "hello go"                                                                                                            a[1] = 'd'              //cannot assign to a[1] } {     var a = "hello go"     bs := []byte(a)     ...                                                                                                                                                                                                                 s2 := string(bs)      rs := []rune(a)     ...     s3 := string(rs) }

Go语言支持用"+"运算符进行字符串拼接但是每次拼接都需要重新分配内存如果频繁构造一个很长的字符串则性能影响就会很大比如

func test1()string{     var s string     for i:= 0;i < 1000 ;i++{         s += "a"      }        return s }  func Benchmark_test1(b *testing.B){     for i:= 0;i < b.N; i++{         test1()                                                                                                                                                                                                         }    } 输出 # go test str1_b_test.go  -bench="test1" -benchmem Benchmark_test1-2         5000      227539 ns/op    530338 B/op      999 allocs/op

常用的改进方法是预分配足够的内存空间然后使用strings.Join函数该函数会统计出所有参数的长度并一次性完成内存分配操作改进一下上面的代码

func test()string{     s := make([]string,1000)     for i:= 0;i < 1000 ;i++{         s[i] = "a"      }        return strings.Join(s,"") } func Benchmark_test(b *testing.B){     for i:= 0;i < b.N; i++{         test()     }    } 输出 # go test -v b_test.go  -bench="test1" -benchmem Benchmark_test1-2      200000       10765 ns/op      2048 B/op        2 allocs/op

在日常开发中可以使用fmt.Sprintf函数来格式化和拼接较少的字符串操作比如

{     a := 10010     as := fmt.Sprintf("%d",a)     fmt.Printf("%T , %v\n",as,as) }

数组

数组是内置(build-in)类型是一组存放相同类型数据的集合数组的数据类型是由存储的元素类型和数组的长度共同决定的,即使元素类型相同但是长度不同数组也不属于同一类型。数组初始化之后长度是固定无法修改的数组也支持逻辑判断运算符 "==","="定义方式如下

{     var a [10]int     var b [20]int     println(a == b)  //invalid operation: a == b (mismatched types [10]int and [20]int) }

数组的初始化相对灵活下标索引值从0开始支持按索引位置初始化对于未初始化的数组编译器将给以默认值。

{     var a[4] int                //元素初始化为0     b := [4] int{0,1}           //未初始化的元素将被初始化为0     c := [4] int{0, 2: 3}       //可指定索引位置初始化     d := [...]int{0,1,2}        //编译器根据初始化值数量来确定数组的长度     e := [...]int{1, 3:3}       //支持索引位置初始化但数组长度与其无关          type user struct{         name string         age int      }         d := [...] user{            //复合数据类型数组可省略元素初始化类型标签         {"a",1},         {"b",2},     } }

定义多维数组时只有数组的第一维度允许使用 "..."

{     x := [2]int{2,2}     a := [2][2]int{{1,2},{2,2}}     b := [...][2]int{{2,3},{2,2},{3,3}}     c := [...][2][2]int{{ {2,3},{2,2} },{{3,3},{4,4}} } }

计算数组长度时无论使用内置的len还是cap返回的都是第一维度的长度比如

{     fmt.Println(x, len(x), cap(x))         fmt.Println(a, len(a), cap(x))     fmt.Println(b, len(b), cap(x))     fmt.Println(c, len(c), cap(x)) } 输出 [2 2] 2 2 [[1 2] [2 2]] 2 2 [[2 3] [2 2] [3 3]] 3 2 [[[2 3] [2 2]] [[3 3] [4 4]]] 2 2

数组指针&指针数组

数组除了可以存放具体类型的数据也可以存放指针比如

{      x, y := 10, 20     a := [...]*int{&x, &y}      //指针数组      p := &a                     //数组的指针 }

数组复制

Go语言数组是值(非引用)类型所以在赋值和参数传递过程中都会复制整个数组数据比如:

func test(x [2]int){     fmt.Printf("x:= %p,%v\n", &x, x) }  func main(){      a := [2] int{1, 2}     test(a)                     //传参过程中完全复制     var b [2]int     b = a                       //赋值过程中完全复制     fmt.Printf("a:= %p,%v\n", &a, a)     fmt.Printf("b:= %p,%v\n", &b, b)                                                                                                                                                                                } 输出 x:= 0xc42000a330,[1 2] a:= 0xc42000a320,[1 2] b:= 0xc42000a370,[1 2]

借鉴: 雨痕<GO学习笔记>

讨论学习: 675020908

©著作权归作者所有:来自51CTO博客作者100018的原创作品,如需转载,请与作者联系,否则将追究法律责任


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消