今天是 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 有些多, 如果记不过来也没有关系, 我们只需要大概有一个印象即可, 具体可以使用到的时候再去查阅相关的资料.
如果觉得有所收获, 请给我一个 关注 .
来源: http://www.tuicool.com/articles/zyQnEzI