Go by Example - String Functions, String Formatting, Text Templates, Regular Expressions

Go by Example - String Functions, String Formatting, Text Templates, Regular Expressions

介绍go中的字符串函数, 字符串格式化, 文本模板和正则表达式

String Functions

标准库的字符串包提供了许多有用的字符串相关函数。下面是一些示例,让您对字符串包有所了解。

string-functions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"fmt"
s "strings"
)

// 我们将 fmt.Println 别名为一个更简短的名称,因为我们在下面会经常用到它
var p = fmt.Println

func main() {
// 下面是字符串中可用函数的示例
// 由于这些函数是字符串包中的函数,而不是字符串对象本身的方法,因此我们需要将字符串作为第一个参数传递给函数
// 您可以在字符串包文档中找到更多函数: https://pkg.go.dev/strings
p("Contains: ", s.Contains("test", "es"))
p("Count: ", s.Count("test", "t"))
p("HasPrefix: ", s.HasPrefix("test", "te"))
p("HasSuffix: ", s.HasSuffix("test", "st"))
p("Index: ", s.Index("test", "e"))
p("Join: ", s.Join([]string{"a", "b"}, "-"))
p("Repeat: ", s.Repeat("a", 5))
p("Replace: ", s.Replace("foo", "o", "0", -1))
p("Replace: ", s.Replace("foo", "o", "0", 1))
p("Split: ", s.Split("a-b-c-d-e", "-"))
p("ToLower: ", s.ToLower("TEST"))
p("ToUpper: ", s.ToUpper("test"))
}
log
1
2
3
4
5
6
7
8
9
10
11
12
13
$ go run string-functions.go
Contains: true
Count: 2
HasPrefix: true
HasSuffix: true
Index: 1
Join: a-b
Repeat: aaaaa
Replace: f00
Replace: f0o
Split: [a b c d e]
ToLower: test
ToUpper: TEST

String Formatting

Go 在 printf 传统中为字符串格式化提供了出色的支持。下面是一些常见字符串格式化任务的示例。

string-formatting.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package main

import (
"fmt"
"os"
)

type point struct {
x, y int
}

func main() {
// Go 提供了多个打印 "动词",用于格式化一般的 Go 值
// 例如,这里打印的是我们的 point 结构的一个实例
p := point{1, 2}
fmt.Printf("struct1: %v\n", p)

// 如果值是结构体,%+v 变量将包含结构体的字段名
fmt.Printf("struct2: %+v\n", p)

// %#v 变体打印值的 Go 语法表示,即产生该值的源代码片段
fmt.Printf("struct3: %#v\n", p)

// 要打印数值的类型,请使用 %T
fmt.Printf("type: %T\n", p)

// 布尔值的格式化非常简单
fmt.Printf("bool: %t\n", true)

// 整数格式化有多种选择。使用 %d 表示标准的 10 进制格式
fmt.Printf("int: %d\n", 123)

// 这将打印二进制表示
fmt.Printf("bin: %b\n", 14)

// 打印与给定整数对应的字符
fmt.Printf("char: %c\n", 33)

// %x 提供十六进制编码
fmt.Printf("hex: %x\n", 456)

// 浮点数也有多种格式化选项。对于基本的十进制格式,使用 %f
fmt.Printf("float1: %f\n", 78.9)

// %e 和 %E 用科学记数法(略有不同的版本)格式化浮点数
fmt.Printf("float2: %e\n", 123400000.0)
fmt.Printf("float3: %E\n", 123400000.0)

// 对于基本字符串打印,请使用 %s
fmt.Printf("str1: %s\n", "\"string\"")

// 要像在 Go 源代码中一样给字符串加双引号,请使用 %q
fmt.Printf("str2: %q\n", "\"string\"")

// 与前面看到的整数一样,%x 以基数 16 表示字符串,每个字节输入两个输出字符
fmt.Printf("str3: %x\n", "hex this")

// 要打印指针的表示形式,请使用 %p
fmt.Printf("pointer: %p\n", &p)

// 在格式化数字时,您经常需要控制数字的宽度和精度
// 要指定整数的宽度,请在动词中的 % 后面使用数字
// 默认情况下,结果将右对齐并用空格填充
fmt.Printf("width1: |%6d|%6d|\n", 12, 345)

// 您还可以指定打印浮点数的宽度,不过通常还需要同时使用 width.precision 语法限制小数精度
fmt.Printf("width2: |%6.2f|%6.2f|\n", 1.2, 3.45)

// 要左对齐,请使用"-"标记
fmt.Printf("width3: |%-6.2f|%-6.2f|\n", 1.2, 3.45)

// 在格式化字符串时,您可能还需要控制宽度,尤其是确保字符串在类似表格的输出中对齐。对于基本的右对齐宽
fmt.Printf("width4: |%6s|%6s|\n", "foo", "b")

// 要左对齐,与数字一样使用"-"标记
fmt.Printf("width5: |%-6s|%-6s|\n", "foo", "b")

// 到目前为止,我们已经看过 Printf,它将格式化后的字符串打印到 os.Stdout
// Sprintf 格式化并返回字符串,但不打印到任何地方
s := fmt.Sprintf("sprintf: a %s", "string")
fmt.Println(s)

// 您可以使用 Fprintf 格式化并打印到 os.Stdout 以外的 io.Writers 中
fmt.Fprintf(os.Stderr, "io: an %s\n", "error")
}
log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ go run string-formatting.go
struct1: {1 2}
struct2: {x:1 y:2}
struct3: main.point{x:1, y:2}
type: main.point
bool: true
int: 123
bin: 1110
char: !
hex: 1c8
float1: 78.900000
float2: 1.234000e+08
float3: 1.234000E+08
str1: "string"
str2: "\"string\""
str3: 6865782074686973
pointer: 0xc0000ba000
width1: | 12| 345|
width2: | 1.20| 3.45|
width3: |1.20 |3.45 |
width4: | foo| b|
width5: |foo |b |
sprintf: a string
io: an error

Text Templates

Go 通过 text/template 包为创建动态内容或向用户显示自定义输出提供内置支持。名为 html/template 的同类软件包提供了相同的 API,但具有额外的安全功能,应在生成 HTML 时使用。

templates.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package main

import (
"os"
"text/template"
)

func main() {
// 我们可以创建一个新模板,并从字符串中解析其主体
// 模板由静态文本和{{...}}中的 "操作 "组成,用于动态插入内容。
t1 := template.New("t1")
t1, err := t1.Parse("Value is {{.}}\n")
if err != nil {
panic(err)
}

// 或者,我们也可以使用 template.Must 函数,以防 Parse 返回错误
// 这对于在全局范围内初始化的模板尤其有用
t1 = template.Must(t1.Parse("Value: {{.}}\n"))

// 通过 "执行 "模板,我们将生成带有特定操作值的文本
// {{.}}操作由作为参数传递给 Execute 的值代替
t1.Execute(os.Stdout, "some text")
t1.Execute(os.Stdout, 5)
t1.Execute(os.Stdout, []string{
"Go",
"Rust",
"C++",
"C#",
})

// 我们将在下面使用帮助程序功能
Create := func(name, t string) *template.Template {
return template.Must(template.New(name).Parse(t))
}

// 如果数据是结构体,我们可以使用 {{.FieldName}} 操作来访问其字段
// 这些字段应被导出,以便在执行模板时可以访问
t2 := Create("t2", "Name: {{.Name}}\n")

// 这同样适用于映射;对于maps,对键名称的大小写没有限制
t2.Execute(os.Stdout, struct {
Name string
}{"Jane Doe"})

t2.Execute(os.Stdout, map[string]string{
"Name": "Mickey Mouse",
})

// if/else 为模板提供了条件执行
// 如果某个值是某个类型的默认值,如 0、空字符串、nil 指针等,则该值被视为 false
// 本示例演示了模板的另一个功能:在操作中使用"-"来修剪空白
t3 := Create("t3",
"{{if . -}} yes {{else -}} no {{end}}\n")
t3.Execute(os.Stdout, "not empty")
t3.Execute(os.Stdout, "")

// range 块让我们可以在切片、数组、映射或通道中循环
// 在 range 块中,{{.}} 被设置为迭代的当前项目
t4 := Create("t4",
"Range: {{range .}}{{.}} {{end}}\n")
t4.Execute(os.Stdout,
[]string{
"Go",
"Rust",
"C++",
"C#",
})
}
log
1
2
3
4
5
6
7
8
9
$ go run templates.go 
Value: some text
Value: 5
Value: [Go Rust C++ C#]
Name: Jane Doe
Name: Mickey Mouse
yes
no
Range: Go Rust C++ C#

Regular Expressions

Go 提供对正则表达式的内置支持。下面是 Go 中一些常见的 regexp 相关任务的示例。

regular-expressions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package main

import (
"bytes"
"fmt"
"regexp"
)

func main() {
// 测试模式是否与字符串匹配
match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
fmt.Println(match)

// 上面我们直接使用了字符串模式,但对于其他 regexp 任务,您需要编译一个优化的 Regexp 结构
r, _ := regexp.Compile("p([a-z]+)ch")

// 这些结构体上有许多方法。下面是我们之前看到的一个匹配测试
fmt.Println(r.MatchString("peach"))

// 这将找到与 regexp 匹配的内容
fmt.Println(r.FindString("peach punch"))

// 这也是查找第一个匹配项,但返回的是匹配项的开始和结束索引,而不是匹配文本
fmt.Println("idx:", r.FindStringIndex("peach punch"))

// Submatch变体包括关于整个模式匹配和这些匹配中的子匹配的信息
// 例如,这将返回p([a-z]+)ch和([a-z]+)的信息
fmt.Println(r.FindStringSubmatch("peach punch"))

// 类似地,这将返回关于匹配和子匹配的索引的信息
fmt.Println(r.FindStringSubmatchIndex("peach punch"))

// 这些函数的 All 变体适用于输入中的所有匹配项,而不仅仅是第一个匹配项
// 例如,查找 regexp 的所有匹配项
fmt.Println(r.FindAllString("peach punch pinch", -1))

// 这些 All variants 也适用于我们上面看到的其他功能
fmt.Println("all:", r.FindAllStringSubmatchIndex(
"peach punch pinch", -1))

// 为这些函数提供一个非负整数作为第二个参数将限制匹配次数
fmt.Println(r.FindAllString("peach punch pinch", 2))

// 我们上面的示例有字符串参数,并使用了 MatchString 这样的名称
// 我们也可以提供 []byte 参数,并从函数名中去掉 String
fmt.Println(r.Match([]byte("peach")))

// 使用正则表达式创建全局变量时,可以使用 Compile 的 MustCompile 变体
// MustCompile 会慌乱而不是返回错误,因此用于全局变量更安全
r = regexp.MustCompile("p([a-z]+)ch")
fmt.Println("regexp:", r)

// regexp 包还可用于用其他值替换字符串子集
fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))

// Func变体允许您使用给定函数转换匹配的文本
in := []byte("a peach")
out := r.ReplaceAllFunc(in, bytes.ToUpper)
fmt.Println(string(out))
}
log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ go run regular-expressions.go
true
true
peach
idx: [0 5]
[peach ea]
[0 5 1 3]
[peach punch pinch]
all: [[0 5 1 3] [6 11 7 9] [12 17 13 15]]
[peach punch]
true
regexp: p([a-z]+)ch
a <fruit>
a PEACH

有关 Go 正则表达式的完整参考资料,请查看 regexp 软件包文档

参考链接

Go by Example - String Functions, String Formatting, Text Templates, Regular Expressions

https://blog.wty.cool/2024/04/13/go_by_example/String_Functions-String_Formatting-Text_Templates-Regular_Expressions/

作者

孤独小狼

发布于

2024-04-13

更新于

2024-04-13

许可协议

评论