本文始发于个人公众号:TechFlow,原创不易,求个关注
今天是golang专题的第6篇文章,这篇主要和大家聊聊golang当中的字符串的使用。
字符串定义
golang当中的字符串本质是只读的字符型数组,和C语言当中的char[]类似,但是golang为它封装了一个变量类型,叫做string。知道了string这个类型之后,我们就可以很方便地来初始化:
var str string
str1 := "hello world"
var str2 = "hello world too"
这里应该没什么难度,很好理解。由于这个数组是只读的,所以我们可以通过下标获取某一位的字符,但是不允许修改。
// 允许
fmt.Println(str1[3])
// 错误
str1[3] = 'l'
这个也不是golang的独创,很多语言当中都有这个限制,因为会将字符串作为const类型存储在专门的区域。所以不允许字符串进行修改,比如Python也是如此。
除了像是数组一样,支持下标的访问之外,go中的字符串还支持拼接以及求长度的操作。我们可以用len函数获取一个字符串的长度,用+来表示字符串的拼接:
len("hello")
// 5
c := "hello" + "world"
// c="helloworld"
这些本来也属于常规操作,并不值得一提,但是关于len函数,值得仔细说说。这里有一个坑,关于utf-8编码。我们来看下面这个例子:
str := "hello 世界"
fmt.Println(len(str))
按照我们的设想,它返回的应该是8,但是实际上我们这么操作会得到12。原因很简单,因为在utf-8编码当中,一个汉字需要3个字节编码。那如果我们想要得到字符串本身的长度,而不是字符串占据的字节数,应该怎么办呢?这个时候,我们需要用到一个新的结构叫做rune,它表示单个Unicode字符。
所以我们可以将string转化成rune数组,之后再来计算长度,得到的结果就准确了。
str := "hello 世界"
fmt.Println(len([]rune(str)))
这样我们得到的结果就是8了,和我们预期一致了。如果你在使用golang的时候,需要用到utf-8编码,一定要小心。
类型转换
golang当中的字符串不像Java或者其他语言一样封装地非常完善,当我们想要将整形或者是浮点型转成字符串,或者是想要将字符串转成整形和浮点型的时候并没有方法可以直接调用,而必须要通过库函数。golang当中提供了strconv库,用来实现字符串的一些操作。
字符串转整数、浮点数
字符串转整数的方法有两个,一个是ParseInt还有一个是ParseUint,这两个方法本质上都是将字符串转成整数。区别在于前者会保留符号,后者是无符号的,用于无符号整数。
这两个函数都接受三个参数,第一个参数是要转类型的字符串,第二个参数int的进制,比如二进制、八进制还是16进制、32进制。第三个参数表示返回bit的大小,有效值为0,8,16,32,64,如果传入0就返回int或者是uint类型,如果是32,则会返回int32类型。
函数的返回值有两个,第一个是类型转换之后的结果,第二个是一个error,也就是异常类型,表示在转换的过程当中是否有出现异常。如果没有异常,那么这个值会是一个nil。我们判断异常是否是nil就知道有无错误产生,这也是golang当中判断操作有没有异常的常规做法。
所以,代码写出来会是这样的:
value, err := strconv.ParseInt("33225", 10, 32)
if err != nil {
fmt.Println("error happens")
}
如果你不想要这么多功能,就想简单一点将字符串转成int来使用,也可以通过Atoi函数。相比于ParseInt它要简单一些, 只需要传入字符串即可,它默认按照10进制进行转换,并且转换之后会返回int类型的整数。
value, err := strconv.Atoi("33234")
if err != nil {
fmt.Println("error happens")
}
字符串转浮点数只有一个函数,就是ParseFloat,由于浮点数没有进制一说,所以它只有两个参数。第一个参数是待转的字符串,第二个参数是bit的大小。和ParseInt一样,它会返回两个结果,一个是转换之后的结果,一个是error异常。
value, err := strconv.ParseFloat("33.33", 32)
if err != nil {
fmt.Println("error happens")
}
整数、浮点数转字符串
将整数和浮点数转字符串都是用Format方法,根据我们要转的类型不同,分为FormatInt和FormatFloat。FormatInt可以认为是ParseInt的逆向操作,我们固定传入一个int64的类型,和整数的进制。golang会根据我们的数字和进制,将它转成我们需要的字符串。
如果指定的进制超过10进制,那么会使用a-z字母来表示大于10的数字。
比如我们把180转成16进制,会得到b4
num := 180
fmt.Println(strconv.FormatInt(int64(num), 16))
如果我们固定要按照10进制的整数进行转换,golang还为我们提供了简化的函数Itoa,默认按照10进制转化,它等价于FormatInt(i, 10),这样我们只需要传入一个值即可。
num := 180
fmt.Println(strconv.Itoa(num))
浮点数转字符串逻辑大同小异,但是传参稍有变化。因为浮点数可以用多种方式来表示,比如科学记数法或者是十进制指数法等等。golang当中支持了这些格式,所以允许我们通过传入参数来指定我们希望得到的字符串的格式。
FormatFloat接受4个参数,第一个参数就是待转换的浮点数,第二个参数表示我们希望转换之后得到的格式。一共有'f', 'b', 'e', 'E', 'g', 'G'这几种格式。
看起来有些眼花缭乱,我们仔细说说。
'f' 表示普通模式:(-ddd.dddd)
'b' 表示指数为二进制:(-ddddp±ddd)
'e' 表示十进制指数,也就是科学记数法的模式:(-d.dddde±dd)
'E' 和'e'一样,都是科学记数法的模式,只不过字母e大写:(-d.ddddE±dd)
'g' 表示指数很大时用'e'模式,否则用‘f'模式
'G' 表示指数很大时用’E'模式,否则用'f'模式
我们来看个例子:
num := 23423134.323422
fmt.Println(strconv.FormatFloat(float64(num), 'f', -1, 64))
fmt.Println(strconv.FormatFloat(float64(num), 'b', -1, 64))
fmt.Println(strconv.FormatFloat(float64(num), 'e', -1, 64))
fmt.Println(strconv.FormatFloat(float64(num), 'E', -1, 64))
fmt.Println(strconv.FormatFloat(float64(num), 'g', -1, 64))
fmt.Println(strconv.FormatFloat(float64(num), 'G', -1, 64))
得到的结果如下:
字符串和bool型转换
除了常用的整数和浮点数之外,strconv还支持与bool类型进行转换。
其中将字符串转成bool类型用的是ParseBool,它只有一个参数,只接受0, 1, t, f, T, F, ture, false, True, False, TRUE, FALSE这几种取值,否则会返回错误。
flag, err := strconv.ParseBool('t')
if err != nil {
fmt.Println("error happens")
}
将bool转字符串调用FormatBool方法,它也只有一个参数,就是一个bool类型的变量,返回值也是确定的,如果是True就返回"true", 如果是False就返回"false"。
fmt.Println(strconv.FormatBool(true))
字符串运算包
前面介绍的strconv包是golang当中字符串的一个转换操作包,可以用来将字符串转成其他类型,将其他类型转化成字符串。关于字符串本身的一些操作,还有一个专门的包叫做strings。
字符串比较
我们可以通过strings.Compare来比较两个字符串的大小,这个函数类似于C语言当中的strcmp,会返回一个int。
cmp := strings.Compare(str1, str2)
cmp等于-1表示str1字典序小于str2,如果str1和str2相等,cmp等于0。如果cmp=1,表示str1字典序大于str2.
查找函数
我们可以用Index函数查找一个字符串中子串的位置,它会返回第一次出现的位置,如果不存在返回-1.
var theInd = strings.Index(str, "sub")
类似的方法是LastIndex,它返回的是出现的最后一个位置,同样,如果不存在返回-1.
var theLastIdx = strings.LastIndex(str, "last")
Count和Repeat
我们可以用Count来统计子串在整体当中出现的次数。
strings.Count("abcabcabababc", "abc")
第一个参数是母串,第二个参数是子串。如果子串为空,则返回母串的长度+1.
有count自然就有重复,我们可以用Repeat方法来讲字符串重复指定的次数:
repeat := strings.Repeat("abc", 10)
Replace、Split和Join
还有Replace函数,可以替换字符串中的部分。这个函数接收四个参数,分别是字符串,匹配串和目标串,还有替换的次数。如果小于0,表示全部替换。
str := "aaaddc"
strings.Replace(str, "a", "b", 1) // baaddc
strings.Replace(str, "a", "b", -1)
我们还可以通过Split方法来分割字符串,它的使用方法和Python当中的split一样,我们传入字符串与分隔符,会返回根据分隔符分割之后的字符串数组:
str := "abc,bbc,bbd"
slice := strings.Split(str, ",")
除了Split之外,我们也经常使用它的逆操作也就是Join。通过我们指定的分隔符,将一个字符串数组拼接在一起。
slice := []string{"aab", "aba", "baa"}
str := strings.Join(slice, ",")
strings当中的函数除了刚才列举的之外还有很多,比如用来去除字符串首尾多余字符的Trim和TrimLeft,判断是否包含前缀的HasPrefix和判断是否包含后缀的HasSufix等等,由于篇幅限制,不一一列举了,大家用到的时候可以查阅strings的api文档。
总结
到这里,关于golang当中string的一些基本用法就介绍完了。一般来说,我们日常需要用到的功能,strings和strconv这两个库就足够使用了。初学者可能经常会把这两个库搞混淆,其实很容易分清,strings当中封装的是操作字符串的一些函数。比如字符串判断、join、split等各种处理,而strconv是专门用来字符串和其他类型进行转换的,除此之外基本上没有其他的功能。牢记这两点之后,很容易区分开。
今天介绍的api有些多,如果记不过来也没有关系, 我们只需要大概有一个印象即可,具体可以使用到的时候再去查阅相关的资料。
如果觉得有所收获,请给我一个关注。