<4>Golang基础进阶——字符串应用

Golang:字符串应用

1. 计算字符串长度

Go语言的内建函数 len() ,可以用来获取切片、字符串、通道(channel)等的长度下面的。代码可以用 len() 来获取字符串的长度。

func main() {
tip1 := "genji is a ninja"
fmt.Println(len(tip1)) // 16

tip2 := "忍者"
fmt.Println(len(tip2)) // 6

}

len()函数的返回值的类型为int表示字符串的ASCII字符个数或字节长度。

  • 输出中第一行的16表示tipl字符个数为16。
  • 输出中第二行tip2符格式,也就是“忍者” 的字符个数是6,然而根据习惯,“忍者”的字符个数应该是2。

这里的差异是由于Go字符串都以UTF-8格式保存,每个中文占用3节,因此使用len()获得两个中文文字对应的6字节。
如果希望按习惯上的字符个数来计算,就需要使用Go言中UTF-8提供的RuneCountlnString()函数,统计Uncode字符数量。
下面的代码展示如何计算UTF-8字符个数。

func main() {
fmt.Println(utf8.RuneCountInString("忍者")) // 2
fmt.Println(utf8.RuneCountInString("忍者,fight!")) // 9

}
  • ASCII 字符串长度使len()函数。
  • Unicode 字符串长度使用utf8.RuneCountlnString()函数。

2. 遍历字符串一一获取每一个字符串元素

2.1. 遍历每一个ASCII字符

遍历 ASCII 字符使用 for 的数值循环进行遍历,直接取每个字符串的下标获取 ASCII字符,如下面的例子所示。

func main() {
theme := "狙击 start"

for i :=0; i<len(theme);i++{
fmt.Printf("ascii: %c %d\n", theme[i], theme[i])
}
// ascii: ç 231
// ascii: ‹ 139
// ascii: ™ 153
// ascii: å 229
// ascii: ‡ 135
// ascii: » 187
// ascii: 32
// ascii: s 115
// ascii: t 116
// ascii: a 97
// ascii: r 114
// ascii: t 116
}

这种模式下取到的汉字“惨不忍睹”。 由于没有使用 Unicode ,汉字被显示为乱码。

2.2. Unicode字符遍历字符串

func main() {
theme := "狙击 start"

for _, s := range theme {
fmt.Printf("Unicode: %c %d\n", s, s)
}
//Unicode: 29401
//Unicode: 20987
//Unicode: 32
//Unicode: s 115
//Unicode: t 116
//Unicode: a 97
//Unicode: r 114
//Unicode: t 116
}

总结:

  • ASCII 字符串遍历直接使用下标。
  • Unicode 字符串遍历用 for range。

 2.3. 获取字符串的某一段字符

下面例子中使用  strings.Index()  函数在字符串中搜索另个子串,代码如下:

func main() {
tracer := "死神来了,死神bye bye"
comma := strings.Index(tracer, "")
pos := strings.Index(tracer[comma:], "死神")

fmt.Println(comma, pos, tracer[comma+pos:]) // 12 3 死神bye bye
}

总结:
字符串索引比较常用的有如下几种方法。

  • strings.Index :正向搜索子字符串。
  • strings.Lastlndex :反向搜索子字符串
  • 搜索的起始位置可以通过切片偏移制作

2.4. 修改字符串

Go 的字符串无法直接修改每一个字符元素,只能通过重新构造新的字符串并赋值给原来的字符串变量实现。请参考下面的代码:

func main() {
angel := "Heros never die"
angelBytes := []byte(angel)

for i := 5; i <= 10; i++{
angelBytes[i] = ' '
}
fmt.Println(string(angelBytes))
// Heros die
}

Go 中的字符串和其他高级语 ( Java C#) 一样,默认是不可变的(immutable)字符串不可变有很多好处,如天生线程安全,大家使用的都是只读对象,无须加锁;再者,方便内存共享,而不必使用写时复制(Copy On Write)等技术;字符串 hash 值也只需要制作一份
所以说,代码中实际修改的是[]byte, []byte在Go言中是可变的 ,本身就是一个切片在完成了对[]byte操作后,使用string()将[]bytey转为字符串时 重新创造了一个新的字符串。

总结:

  • Go 字符串是不可变的。
  • 修改字符串时,可以将字符串转换为[]byte进行修改。
  • []byte 和白string 可以通过强制类型转换互转。

2.5. 连接字符串

连接字符串这么简单,还需要学吗? 确实,Go和大多数其他语言一样,使用“+”对字符串进行连接操作,非常直观
但问题来了,好的事物井非完美 简单的东西未必高效。除了加号连接字符Go语言中也有类似于 StringBuilder 的机制来进行高效的字符串连接,例如:

func main() {
hammer := "吃我一锤"
sickle := "死吧"

// 声明字节缓冲
var stringBuilder bytes.Buffer

stringBuilder.WriteString(hammer)
stringBuilder.WriteString(sickle)

fmt.Println(stringBuilder.String())
}

bytes Buffer 是可以缓冲并可以往里面写入各种字节数组的。字符串也是一种字节数组,使用 WriteString()方法进行写入。
将需要连接的字符串,通过调用 WriteString() 方法,写入 stringBuilder 中,然后再通过stringBuilder String() 方法将缓冲转换为字符串。

 2.6. 格式化

格式化在逻辑中非常常用。使用格式化函数,要注意写法:
fmt.Sprintf (格式化样式,参数列表...)

  • 格式化样式:字符串形式,格式化动词以%开头。
  • 参数列表:多个参数以逗号分隔,个数必须与格式化样式中的个数一 一对应,否则运行时会报错。
func main() {
var progress = 2
var target = 8

// 两参数格式化
title := fmt.Sprintf("已采集%d个草药,还需要%d个完成任务", progress, target)
fmt.Println(title)
// 已采集2个草药,还需要8个完成任务

pi := 3.14159
// 按数值本身的格式输出
variant := fmt.Sprintf("%v %v %v", "月球基地", pi, true)
fmt.Println(variant)
// 月球基地 3.14159 true
}

 

posted @ 2020-04-09 17:15  Wshile  阅读(220)  评论(0编辑  收藏  举报