〇、致谢

感谢在github上,把《the way to go》译成中文(Ivo Balbaert授权)的大佬们。推荐广大想入门Go语言的同行移步阅读

感谢 @弈心 王哥的介绍和分享,让我逐渐入门NetDevOps,王哥是中文圈内,最早系统介绍NetDevOps知识的前辈,他在知乎专栏《网路行者》无私地分享着NetDevOps知识。帮助包括我在内的很多同行朋友们用Python,敲开了NetDevOps领域的大门,著有《网络工程师的Python之路》一书。同时,王哥也在准备他的《网络工程师的Golang之路》欢迎志同道合的同行朋友移步至他的专栏阅读。

感谢 @朱嘉盛 盛哥的无私分享。盛哥的知乎专栏《网工手艺》对Python的基础知识、网络自动化分析知识、国产网络设备自动化知识做了体系性的介绍。特别推荐使用国产设备的同行关注。

一、Go语言字符串的字面值分类

Go的字符串是一种不可变的字符序列。按照C语言话语体系来讲,Go的字符串就是由字节组成的定长数组。

1、解释字符串

Go的解释字符串用双引号“”括起来,“”中所含的转译字符

\n  // 换行符
\r  // 回车符
\t // tab 键
\u 或 \U // Unicode 字符
\\ // 反斜杠自身

将被实际类型替换。比如:

package main
​
import "fmt"
​
func main() {
str := "Hello Go\n"   // Println已经空了一行,在有/n,相当于再空一行
fmt.Println(str)
}

运行效果

username@usernamedeMacBookPro1 Go %go run "/Users/username/Coding/Go/code/code.go"
Hello Go
​
username@usernamedeMacBookPro1 Go %

2、非解释字符串

非解释性字符串用反引号``(英文输入法状态下,键盘数字1左侧按键)括起来,``内的转义字符会原样输出。

package main
​
import "fmt"
​
func main() {
str := `Hello Go\n`
fmt.Println(str)
}

运行效果

username@usernamedeMacBookPro1 Go %go run "/Users/username/Coding/Go/code/code.go"
Hello Go\n
username@usernamedeMacBookPro1 Go %

这一点,类似,但又不完全与Python的/r''参数相同

In [1]: a = r'\n\n\n Hello Python'
​
In [2]: a
Out[2]: '\\n\\n\\n Hello Python' # Python自动补全转义字符

二、字符串的长度与拼接

Go的字符串长度是根据元素长度限定的。不会在尾部含有\0,Go的string类型的0值,就是特指""

、、、、

可以通过len()函数来测量字符串的字节长度。但由于Go的字符串本身是一个UTF-8的字符序列。导致了编码对字节长度占用的多样性(英文字母的ASCII码和汉字的Unicode码再测量时就会出现很大差异)需要特别注意。

如果要检测汉字字符串的长度,可以先把汉字转换成rune切片,再测量切片个数。参见王哥《网络工程师的Golang之路 -- Go数据类型(字符串)》https://zhuanlan.zhihu.com/p/454869985一文。

package main
​
import "fmt"
​
func main() {
var str_chinese string = "网络工程师"
rune_chinese := []rune(str_chinese)
fmt.Println(len(rune_chinese))
}     // 5

1、索引

字符串的内容(字节)可以通过索引来获取,索引从0开始(注意编码,一般特指ASCII)

(1)字符串的第一个字节:str[0]
​
(2)字符串的第n个字节: str[n - 1]
​
(3)字符串的最后一个字节:str[len(str) - 1]

由于Go的字符串是不可变序列,所以是不能单独获取字符串的某一个字节地址的

&str[n]   // 错误

其实Go的字符串还可以被当成字节切片(byte slice)来索引。

2、拼接

同Python类似,Go也支持用+来拼接字符串。不过需要注意:Go语句的末尾会隐含分号;(由于Go编译器在编译语句时,会在语句末尾加入分号)Go在做字符串拼接时,+不要跨行。

比如:

package main
​
import "fmt"
​
func main() {
str := "Hello " +   // 不要跨行
"Go"
fmt.Println(str)
}

+= 也可以用于拼接

package main
​
import "fmt"
​
func main() {
str := "Hel" + "lo"
str += " Go"
fmt.Println(str)
}

当然,Go还有strings.Join()、bytes.Buffer等高阶拼接方法。

三、Go的strings包

strings包是Go语言中,用来处理字符串的Package,包含了很多处理字符串的预定义函数。

1、前后缀判断

HasPrefix用来判断字符串str的前缀。返回一个bool值

strings.HasPrefix(str, prefix string) bool

HasSuffix用来判断字符串str的后缀,返回一个bool值

strings.HasSuffix(str, suffix string) bool

示例:

package main
​
import (
"fmt"
"strings"
)
​
func main() {
var str string = "Hello Go"
fmt.Println(strings.HasPrefix(str, "He"))    // true
fmt.Println(strings.HasSuffix(str, "Go")) // true
}

2、包含关系判断

Contains()判断字符串str中是否含有sub_str,返回一个bool值

strings.Contains(str, sub_str string) bool

示例:

package main
​
import (
"fmt"
"strings"
)
​
func main() {
var str string = "Hello Go"
fmt.Println(strings.Contains(str, "Go")) // true
}

3、位置判断

Index()返回sub_str在str中的索引(特指sub_str第一个字符的索引)如果值是-1,表示str中不包含sub_str

strings.Index(str, sub_str string) int

LastIndex()返回sub_str在str中最后出现的的索引(特指sub_str第一个字符的索引)如果值是-1,表示str中不包含sub_str

strings.LastIndex(str, sub_str string) int

IndexRune()返回非ASCII编码的r rune,在字符串str中的索引

strings.IndexRune(str string, r rune) int

示例:

package main
​
import (
"fmt"
"strings"
)
​
func main() {
var str string = "Hello Go"
var chinese_str string = "网络工程师"
fmt.Println(strings.Index(str, "Go"))// 6
fmt.Println(strings.LastIndex(str, "o"))// 7
fmt.Println(strings.IndexRune(chinese_str, '工')) // 6 一个汉字3Byte
}

4、字符串的替换

Replace() 用于将字符串 str 中的前 n 个字符串 old 替换为字符串 new,并返回一个新的字符串,如果 n = -1 则替换所有字符串 old 为字符串 new

strings.Replace(str, old, new string, n int) string

示例:

package main
​
import (
"fmt"
"strings"
)
​
func main() {
var str string = "Hello Python"
fmt.Println(strings.Replace(str, "Python", "Go", -1)) // Hello Go
}

5、修改字符串的大小写

和Python的upper、lower、title类似,Go的strings包也有ToUpper、ToLower、ToTitle函数。

package main
​
import (
"fmt"
"strings"
)
​
func main() {
var str string = "Hello Python"
fmt.Println(strings.ToUpper(str))  // HELLO PYTHON
fmt.Println(strings.ToLower(str))  // hello python
fmt.Println(strings.ToTitle(str))  // HELLO PYTHON 注意,和Python的区别
}

6、修剪字符串

类似于Python的strip()、rsrip()、lstrip(),Go的strings包也有TrimSpace()、TrimLeft()、TrimRight()

package main
​
import (
"fmt"
"strings"
)
​
func main() {
var str string = " Hello Python "
fmt.Println(strings.TrimSpace(str)) // 默认是修剪两侧空格
fmt.Println(strings.TrimLeft(str, " "))  // 需要添加修剪参数
fmt.Println(strings.TrimRight(str, " ")) // 需要添加修剪参数
}

7、字符串分割

strings.Fields(str) 将会利用 1 个或多个空白符号来作为动态长度的分隔符将字符串分割成若干小块,并返回一个 slice,如果字符串只包含空白符号,则返回一个长度为 0 的 slice。

package main
​
import (
"fmt"
"strings"
)
​
func main() {
var str string = " Hello Python Hello Go"
venders := strings.Fields(str)
fmt.Println(venders)
for _, v := range venders {
fmt.Println(v)
}
}
​

运行效果

username@usernamedeMacBookPro1 Go %go run "/Users/username/Coding/Go/code/tempCodeRunnerFile.go"
[Hello Python Hello Go]
Hello
Python
Hello
Go
username@usernamedeMacBookPro1 Go %


strings.Split(s, sep) 用于自定义分割符号来对指定字符串进行分割,同样返回 slice。

package main
​
import (
"fmt"
"strings"
)
​
func main() {
var str string = " Hello_Python_Hello_Go"
venders := strings.Split(str, "_")
fmt.Println(venders)
for _, v := range venders {
fmt.Println(v)
}
}
​

运行效果

username@usernamedeMacBookPro1 Go %go run "/Users/username/Coding/Go/code/tempCodeRunnerFile.go"
[ Hello Python Hello Go]
Hello
Python
Hello
Go
username@usernamedeMacBookPro1 Go %

8、字符串的重复

不同于Python字符串的重复,Go的strings包内,有专门的Repeat()函数,用以重复 count 次字符串 str ,并返回一个新的字符串:

strings.Repeat(str, count int) string

示例:

package main
​
import (
"fmt"
"strings"
)
​
func main() {
var str string = " Hello_Python_Hello_Go"
str_new := strings.Repeat(str, 3)
fmt.Println(str_new)
fmt.Println(strings.Repeat("网络工程师的Go语言笔记", 3))
}

运行效果:

username@usernamedeMacBookPro1 Go %go run "/Users/username/Coding/Go/code/tempCodeRunnerFile.go"
Hello_Python_Hello_Go Hello_Python_Hello_Go Hello_Python_Hello_Go
网络工程师的Go语言笔记网络工程师的Go语言笔记网络工程师的Go语言笔记
username@usernamedeMacBookPro1 Go %

9、拼接slice到字符串

Go语言strings包中的Join() 函数,可以把元素类型为 string 的 slice 使用分割符号拼接组成一个字符串:

strings.Join(sl []string, sep string) string

比如:

package main
​
import (
"fmt"
"strings"
)
​
func main() {
cisco_cert_level := []string{"CCIE", "CCNP", "CCNA"}
cisco_cert_level_str := strings.Join(cisco_cert_level, "_")
fmt.Println(cisco_cert_level_str)
}

运行效果:

username@usernamedeMacBookPro1 Go %go run "/Users/username/Coding/Go/code/tempCodeRunnerFile.go"
CCIE_CCNP_CCNA
username@usernamedeMacBookPro1 Go %

10、从字符串中读取内容

strings包中的NewReader()函数, 用来生成一个 Reader 并读取字符串中的内容,然后返回指向该 Reader 的指针,从其它类型读取内容的函数还有:

Read() 从 []byte 中读取内容。 ReadByte() 、ReadRune() 从字符串中读取下一个 byte 或者 rune。这里先不展开。

先来看strings.NewReader(str) 的例子

示例:

package main
​
import (
"fmt"
"strings"
)
​
func main() {
cisco_cert_level := "CCIE CCNP CCNA"
cisco := strings.NewReader(cisco_cert_level)
fmt.Printf("%T\n", cisco)
fmt.Printf("%v\n", cisco)
fmt.Printf("%p\n", cisco)
}

运行效果:

username@usernamedeMacBookPro1 Go %go run "/Users/username/Coding/Go/code/tempCodeRunnerFile.go"
*strings.Reader
&{CCIE CCNP CCNA 0 -1}
0xc000060020
username@usernamedeMacBookPro1 Go %

11、统计字符串出现的次数

strings包中的Count() 函数,用于计算字符串 sub_str 在字符串 str 中出现的非重叠次数

strings.Count(str, sub_str string) int

示例:

package main
​
import (
"fmt"
"strings"
)
​
func main() {
str := "CCIE CCNP CCNA CCIE"
sub_str := "CCIE"
fmt.Println(strings.Count(str, sub_str))
fmt.Println(strings.Count(str, "C"))
}

运行效果:

username@usernamedeMacBookPro1 Go %go run "/Users/username/Coding/Go/code/tempCodeRunnerFile.go"
2
8
username@usernamedeMacBookPro1 Go %

四、Go的strconv包

Go语言中,与字符串类型的转换大多都是通过strconv包来实现的。关于strconv的用法详解,王哥在他的文章《网络工程师的Golang之路 -- Go数据类型(数字型)》中,

有详细描述,由于水平有限,只简单列举整形、浮点型数字和字符串的互转方法。

1、整形、浮点型转换成字符串型

使用ItoA(),把整形数i转换成字符串类型

strconv.Itoa(i int) string

使用FormatFloat(),将 浮点型转换为字符串型, fmt 表示格式(其值可以是 'b'、'e'、'f' 或 'g'),prec 表示精度,bitSize 则使用 32 表示 float32,用 64 表示 float64。

strconv.FormatFloat(f float64, fmt byte, prec int, bitSize int) string

示例:

package main
​
import (
"fmt"
"strconv"
)
​
func main() {
var code_int int = 1024
var code_double float64 = 3.1415926
code_int_str := strconv.Itoa(code_int)
code_double_str := strconv.FormatFloat(code_double, 'f', 2, 64)
fmt.Printf("type: %T, value: %s\n", code_int_str, code_int_str)
fmt.Printf("type: %T, value: %s\n", code_double_str, code_double_str)
}

运行效果:

username@usernamedeMacBookPro1 Go %go run "/Users/username/Coding/Go/code/tempCodeRunnerFile.go"
type: string, value: 1024
type: string, value: 3.14
username@usernamedeMacBookPro1 Go %

2、字符串型转换成整形、浮点型

使用Atoi(),将字符串转换为 整型

strconv.Atoi(s string) (i int, err error)

使用ParseFloat(),将字符串类型转换为浮点型

strconv.ParseFloat(s string, bitSize int) (f float64, err error)

特别注意,在Atoi和ParseFloat两个函数均为多返回值函数,除了返回基本的数字类型外,还会返回一个err类型值。只需要使用两个变量,一个用来装返回值,一个用来装err类型值即可。

示例:

package main
​
import (
"fmt"
"strconv"
)
​
func main() {
var int_str string = "1024"
var double_str string = "3.1415926"
code_int, _ := strconv.Atoi(int_str)
code_double, _ := strconv.ParseFloat(double_str, 64)
fmt.Printf("type: %T, value: %v\n", code_int, code_int)
fmt.Printf("type: %T, value: %v\n", code_double, code_double)
fmt.Printf("type: %T, value: %.3v\n", code_double, code_double)
}
​

运行效果:

username@usernamedeMacBookPro1 Go %go run "/Users/username/Coding/Go/code/tempCodeRunnerFile.go"
type: int, value: 1024
type: float64, value: 3.1415926
type: float64, value: 3.14
username@usernamedeMacBookPro1 Go %

五、后记

由于Python是动态解释型语言,解释器对于第三方Package的解释,普遍受制于Package管理和版本控制。在很多场合,跨机部署Python的开发、运行环境,是一件极为复杂的事。即便可以通过第三方Package把py文件打包成二进制(可执行)程序,但执行的稳定性,仍然不可预知。

Go作为天生支持高并发的静态编译型语言,可以直接把go文件编译成二进制文件(可执行程序)语法的简捷性与Python比,也丝毫不落下风。

随着Go语言中,NetDevOps Package的初步发展,自己也想在学有余力(Python)的同时,掌握一门对NetDevOps有帮助的静态编译型语言(Go)

最后,转发一位同行朋友 @瞿同学(Darren) 的Go语言应用案例

与志同道合的同行朋友们共勉。