通过示例学习-Go-语言-2023-十五-
通过示例学习 Go 语言 2023(十五)
Golang 高级教程
目录
-
概述
-
Golang 基础教程
-
关于
-
基础
-
循环/迭代/流程控制
-
更多类型
-
并发
-
错误处理
-
-
Golang 高级教程
-
Golang 中的面向对象编程
-
其他
-
-
Golang 中的所有设计模式
-
创建型设计模式
-
行为型设计模式
-
结构型设计模式
-
-
Golang 中的数据结构
概述
欢迎来到 Golang 高级教程系列。本系列教程内容详尽,我们尽量用示例覆盖所有概念。本教程适合那些希望深入理解和掌握 Golang 的人。如果您发现任何错误或改进的地方,我们也欢迎您的反馈。
这是 Golang 官方网站的链接供参考 golang.org/
本教程分为四个部分
-
Golang 基础主题
-
Golang 高级主题
-
所有 Golang 设计模式
-
Golang 中的数据结构
Golang 基础教程
让我们先看看 Go 中的一些基础主题
关于
基础
-
第四章 – 包和模块 – 第一部分
-
第五章 – 包和模块 – 第二部分
-
第六章 – 变量
-
第七章 – 所有基本数据类型
-
第八章 – 函数
-
第九章 – 常量
循环/迭代/流程控制
更多类型
并发
错误处理
Golang 高级教程
现在我们来深入探讨一些 Go 中的高级主题。
Golang 中的面向对象编程
其他
Golang 中的所有设计模式
以下是 Go 中的所有设计模式列表
创建型设计模式
行为设计模式
结构设计模式
Golang 中的数据结构
Golang 映射:不安全用于并发使用
如果你是一名 Golang 网页应用开发者,你可能会遇到将一些数据存储在内存中的用例,以便频繁访问。如果是这种情况,你需要小心,因为 Golang 映射在并发使用时并不安全。
有缺陷的代码: 以下是有缺陷的代码。如果发生映射的并发读写,可能会导致崩溃。
package main
var (
allData = make(map[string]string)
)
func get(key string) string {
return allData[key]
}
func set(key string, value string) {
allData[key] = value
}
func main() {
go set("a", "Some Data 1")
go set("b", "Some Data 2")
go get("a")
go get("b")
go get("a")
}
可能的输出:致命错误:并发映射读和映射写
正确代码:
package main
import (
"fmt"
"sync"
)
var (
allData = make(map[string]string)
rwm sync.RWMutex
)
func get(key string) string {
rwm.RLock()
defer rwm.RUnlock()
return allData[key]
}
func set(key string, value string) {
rwm.Lock()
defer rwm.Unlock()
allData[key] = value
}
func main() {
set("a", "Some Data")
result := get("a")
fmt.Println(result)
}
输出: 一些数据
Golang net/http 包 – 检测超时
下面是一个示例代码,用于检测使用 net/http 包的上游调用是否存在超时。Http 客户端创建时设置为 1 纳秒超时,因此它在访问 google.com 时总是会超时。
package main
import (
"fmt"
"net/http"
"os"
"time"
)
func main() {
client := &http.Client{
Timeout: time.Nanosecond * 1,
}
_, err := client.Get("https://google.com")
isTimeout := false
if os.IsTimeout(err) {
isTimeout = true
}
fmt.Println("isTimeout)
}
输出: true
Golang 正则表达式 – 在方括号或字符类中包含点 – ‘.’
目录
** 概述
- 程序
概述
点或‘.’在方括号或字符类内被视为字面字符。在那里面不需要转义。让我们看看一个工作的程序。
我们将在示例中使用regexp包,它提供正则表达式搜索功能。
程序
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("[.]")
match := sampleRegex.Match([]byte("."))
fmt.Println(match)
}
输出
true
此正则表达式
[.]
将匹配单个点或‘.’
我们首先使用 MustCompile 函数编译给定的正则表达式。如果给定的正则表达式无效,此函数会导致恐慌。成功编译后,它返回 regexp 结构的实例。
sampleRegexp := regexp.MustCompile("[.]")
我们可以在 regexp 实例上调用 Match 方法,将给定模式与正则表达式进行匹配。如果正则表达式与输入字符串匹配,则返回 true,否则返回 false。我们需要将输入字符串的字节传递给此方法。
match := sampleRegexp.Match([]byte("."))
此外,请查看我们的 Golang 高级教程系列 – Golang Advance Tutorial
Golang 正则表达式:回溯引用
目录
-
概述
-
程序
-
第一个示例
-
第二个示例
-
-
替换匹配字符串
概述
Golang 正则表达式包regexp使用re2 引擎,它不支持回溯引用。您可以在这里查看
github.com/google/re2/wiki/Syntax
它确实提到它不支持回溯引用。
然而,还有另一个可用的 golang 包,它使用 libpcre++、Perl 正则表达式,并且支持回溯引用。
https://github.com/glenn-brown/golang-pkg-pcre/tree/master/src/pkg/pcre
程序
所以让我们看看在 golang 中使用这个pcre包的回溯引用示例。
第一个示例
假设我们想匹配数字的重复。有效输入是
1111
888888888
444
匹配同样内容的正则表达式将是
(\d)\1+
让我们分析一下这个正则表达式
-
(\d) – 匹配单个数字。单个数字被括号包围,因此它作为捕获组。
-
\1 – 通过捕获组回溯引用第一个子匹配。因此它将引用第一个数字
-
+ – 前一个数字出现一次或多次
同样的程序
package main
import (
"fmt"
"github.com/glenn-brown/golang-pkg-pcre/src/pkg/pcre"
)
func main() {
regex := pcre.MustCompile(`(\d)\1+`, 0)
matches := regex.MatcherString("1111", 0).Matches()
fmt.Println("For 1111 : ", matches)
matches = regex.MatcherString("88888888", 0).Matches()
fmt.Println("For 88888888 : ", matches)
matches = regex.MatcherString("444", 0).Matches()
fmt.Println("For 444 : ", matches)
matches = regex.MatcherString("123", 0).Matches()
fmt.Println("For 123 : ", matches)
}
输出
For 1111 : true
For 88888888 : true
For 444 : true
For 123 : false
如预期,它能匹配数字的重复
1111
888888888
444
并且它不会匹配以下内容,因为这不是重复
123
第二个示例
假设我们想匹配以冒号分隔的单词重复。有效输入是
John:John
The names are Simon:Simon
匹配同样内容的正则表达式将是
(\w+):\1
让我们分析一下这个正则表达式
-
(\w+) – 匹配一个字符超过一个的单词。它被括号包围,因此它作为捕获组。
-
\1 – 通过捕获组回溯引用第一个子匹配。因此它将引用匹配的单词
同样的程序
package main
import (
"fmt"
"github.com/glenn-brown/golang-pkg-pcre/src/pkg/pcre"
)
func main() {
regex := pcre.MustCompile(`(\w+):\1`, 0)
matches := regex.MatcherString("John:John", 0).Matches()
fmt.Println("For John:John: ", matches)
matches = regex.MatcherString("The names are Simon:Simon", 0).Matches()
fmt.Println("For The names are Simon:Simon: ", matches)
matches = regex.MatcherString("John:Simon", 0).Matches()
fmt.Println("For John:Simon: ", matches)
}
输出
For John:John: true
For The names are Simon:Simon: true
For John:Simon: false
如预期,它能匹配包含重复单词子字符串的字符串
John:John
The names are Simon:Simon
并且它不会匹配以下内容,因为它不包含单词的重复
John:Simon
替换匹配字符串
pcre包还提供了替换匹配字符串的功能。以下是相同的示例。
package main
import (
"fmt"
"github.com/glenn-brown/golang-pkg-pcre/src/pkg/pcre"
)
func main() {
regex := pcre.MustCompile(`(\d)\1+`, 0)
input := "The number is 91-88888888"
result := regex.ReplaceAll([]byte(input), []byte("redacted"), 0)
fmt.Println("result: ", string(result))
}
输出
result: The number is 91-redacted
在上面的例子中,我们有一个包含回溯引用的正则表达式,用于匹配数字的重复。然后我们使用ReplaceAll方法对该数字的重复进行修饰
result := regex.ReplaceAll([]byte(input), []byte("redacted"), 0)
如预期,输出中数字的重复被正确修饰
result: The number is 91-redacted
希望您喜欢这个教程。请在评论中分享反馈
另外,查看我们的 Golang 进阶教程系列 – Golang 进阶教程
Golang 正则表达式: Go(Golang)中的大小写不敏感正则表达式匹配
目录
-
概述
-
程序
概述
在 golang 中,正则表达式匹配的默认行为是大小写敏感的。但通过在正则表达式开头添加一组标志,可以更改默认行为。我们需要在正则表达式开头添加的标志是:
(?i)
标志‘i’用于指示正则表达式将不区分大小写。
这里是大小写敏感和不敏感正则表达式的示例。
大小写敏感正则表达式
abc
大小写不敏感正则表达式
(?i)abc
程序
让我们看看相同的程序。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("(?i)abc")
match := sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
match = sampleRegex.Match([]byte("ABC"))
fmt.Printf("For ABC: %t\n", match)
}
输出
For abc: true
For ABC: true
注意上面程序中的正则表达式。我们用(?i)前缀正则表达式,以指示该正则表达式将不区分大小写。
(?i)abc
从输出中我们可以注意到,它对文本“abc”和“ABC”都给出了正确的匹配。
如果我们移除前缀标志,那么它将会对“ABC”产生错误匹配。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("abc")
match := sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
match = sampleRegex.Match([]byte("ABC"))
fmt.Printf("For ABC: %t\n", match)
}
输出
For abc: true
For ABC: false
注意上面程序中的正则表达式。我们没有用(?i)前缀正则表达式。因此它将回退到默认的大小写敏感行为。
abc
从输出中我们可以注意到,它对文本“abc”给出了正确匹配,但对文本“ABC”没有匹配。
这就是关于 golang 中大小写不敏感正则表达式匹配的全部内容。希望你喜欢这篇文章。
请在评论中分享反馈。此外,查看我们的 Golang 高级教程系列 – Golang 高级教程
Golang 正则表达式:在正则表达式中匹配浮点数
目录
概述
- 程序
概述
浮点数可能具有以下属性
-
它可以有负号和正号
-
当小数部分存在时,整数部分可以是可选的
-
如果整数部分存在,点和小数部分可以是可选的
-
它可以有指数,也可以没有
所以下面是有效的浮点数
1.2
.12
12
12.
+1.2
-1.2
1.2e3
以下是无效的浮点数
-
一个空字符串
-
仅有 + 或 – 号
-
一个点
-
多个 0 的前缀。例如 00.1 或 001
-
任何类似 +. 或 –
-
指数前的一个点。例如 1.e2
-
浮点数前后可以有其他字符。例如 a1.3 或 a1.3b 或 1.3b
以下是无效浮点数的示例
""
.
00.1
001
+
-
+.
-.
1.e2
a1.2
1.2b
a1.2b
让我们首先看看一个简单的正则表达式,它只匹配整数、点和小数部分。
^(?:(?:0|[1-9]\d*)(?:\.\d*)?|\.\d+)$
在高层次上,整个正则表达式有两个部分,它们是或关系
-
(?:0|[1-9]\d)(?:.\d)? – 这捕获整数部分始终存在且小数部分可选的部分
-
.\d+ – 这捕获整数部分缺失且小数部分始终存在的部分。
让我们分析这个正则表达式
让我们更复杂一点,使其接受负号或正号。请注意,负号或正号是可选的
^[+\-]?(?:(?:0|[1-9]\d*)(?:\.\d*)?|\.\d+)$
正则表达式与之前的正则表达式相同。我们只是添加了可选的正负号在前面
-
[+-] – 匹配正号或负号。
-
? – 匹配正号或负号是可选的
让我们再往正则表达式中添加一个指数部分。同样注意,指数部分是可选的。这个正则表达式与之前的正则表达式相同。我们只是将指数部分添加到末尾
^[+\-]?(?:(?:0|[1-9]\d*)(?:\.\d*)?|\.\d+)(?:\d[eE][+\-]?\d+)?$
让我们分析指数部分
-
(?: – 它表示非捕获组
-
\d – 匹配一个数字。这是为了防止出现像 1.e2 这样的数字
-
[eE] – 匹配小写字母 e 或大写字母 E
-
[+-] – 匹配正号或负号。匹配正号或负号是可选的
-
\d+ – 匹配零个或多个数字
-
)? – 整个正则表达式是可选的
程序
现在看这个正则表达式的一个示例
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile(`^[+\-]?(?:(?:0|[1-9]\d*)(?:\.\d*)?|\.\d+)(?:\d[eE][+\-]?\d+)?$`)
fmt.Println("Valid Inputs")
match := sampleRegex.MatchString("1.2")
fmt.Printf("For 1.2: %t\n", match)
match = sampleRegex.MatchString(".12")
fmt.Printf("For .12: %t\n", match)
match = sampleRegex.MatchString("12")
fmt.Printf("For 12: %t\n", match)
match = sampleRegex.MatchString("12.")
fmt.Printf("For 12.: %t\n", match)
match = sampleRegex.MatchString("+1.2")
fmt.Printf("For +1.2.: %t\n", match)
match = sampleRegex.MatchString("-1.2")
fmt.Printf("For -1.2.: %t\n", match)
match = sampleRegex.MatchString("1.2e3")
fmt.Printf("For 1.2e3.: %t\n", match)
fmt.Println("\nInValid Inputs")
match = sampleRegex.MatchString(".")
fmt.Printf("For .: %t\n", match)
match = sampleRegex.MatchString("")
fmt.Printf("For empty string: %t\n", match)
match = sampleRegex.MatchString("00.1")
fmt.Printf("For 00.1: %t\n", match)
match = sampleRegex.MatchString("001")
fmt.Printf("For 001 %t\n", match)
match = sampleRegex.MatchString("+")
fmt.Printf("For +: %t\n", match)
match = sampleRegex.MatchString("-")
fmt.Printf("For -: %t\n", match)
match = sampleRegex.MatchString("+.")
fmt.Printf("For +.: %t\n", match)
match = sampleRegex.MatchString("-.")
fmt.Printf("For -.: %t\n", match)
match = sampleRegex.MatchString("1.e2")
fmt.Printf("For 1.e2: %t\n", match)
match = sampleRegex.MatchString(".e2")
fmt.Printf("For .e2: %t\n", match)
match = sampleRegex.MatchString("a1.2")
fmt.Printf("For a1.2 %t\n", match)
match = sampleRegex.MatchString("1.2b")
fmt.Printf("For 1.2b %t\n", match)
match = sampleRegex.MatchString("a1.2b")
fmt.Printf("For a1.2b %t\n", match)
}
输出
Valid Inputs
For 1.2: true
For .12: true
For 12: true
For 12.: true
For +1.2.: true
For -1.2.: true
For 1.2e3.: true
InValid Inputs
For .: false
For empty string: false
For 00.1: false
For 001 false
For +: false
For -: false
For +.: false
For -.: false
For 1.e2: false
For .e2: false
For a1.2 false
For 1.2b false
For a1.2b false
对于上述讨论的所有有效输入,程序打印为真
Valid Inputs
For 1.2: true
For .12: true
For 12: true
For 12.: true
For +1.2.: true
For -1.2.: true
For 1.2e3.: true
对于上述讨论的所有无效输入,它返回假
InValid Inputs
For .: false
For empty string: false
For 00.1: false
For 001 false
For +: false
For -: false
For +.: false
For -.: false
For 1.e2: false
For .e2: false
For a1.2 false
For 1.2b false
For a1.2b false
请尝试一下,如果在任何情况下这个正则表达式不工作,请在评论中发布。
上述正则表达式用于验证给定字符串是否为数字。如果您想查找输入字符串是否包含数字作为子串,那么我们需要去掉开头和结尾的锚字符,即去掉开头的 脱字符 (^) 和结尾的美元 ($) 字符
所以正则表达式将是
[+\-]?(?:(?:0|[1-9]\d*)(?:\.\d*)?|\.\d+)(?:\d[eE][+\-]?\d+)?
这篇文章主要讨论如何在 Golang 中通过正则表达式匹配浮点数。希望你喜欢这篇文章。请在评论中分享反馈。
另外,请查看我们的 Golang 高级教程系列 – Golang 高级教程
Golang 正则表达式:匹配完整字符串
目录
-
概述**
-
程序
概述
Golang 正则表达式包含两个锚字符,可以用来匹配完整字符串。这两个字符是
-
插入符号‘^’ – 这是一个锚字符,用于正则表达式的开始,以确保给定的输入字符串从开始与正则表达式匹配。它匹配输入字符串的开头。
-
美元字符‘$’ – 这是一个锚字符,用于正则表达式的末尾,以确保给定的输入字符串与正则表达式在末尾匹配。它匹配输入字符串的末尾。
插入符号和美元元字符是锚字符,用于分别匹配字符串的开始和结束。首先,让我们理解如果不使用这些锚字符会发生什么。假设我们有以下正则表达式,其中包含三个简单字符。
abc
这个正则表达式将匹配任何包含abc作为子字符串的字符串。请看下面的示例。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("abc")
match := sampleRegex.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
match = sampleRegex.Match([]byte("1abc23"))
fmt.Printf("For 1abc23: %t\n", match)
match = sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
}
输出
For abcd: true
For 1abc23: true
For abc: true
上述程序会匹配所有包含“abc”作为子字符串的字符串。因此,它会匹配
abc
abcd
1abc23
基本上,它会匹配任何包含“abc”作为子字符串的输入字符串。
程序
如果我们只想匹配完整字符串,那么我们需要在开头用插入符号字符和在末尾用美元字符锚定字符串。这将使我们能够进行完整字符串匹配。请看下面的示例。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("^abc$")
match := sampleRegex.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
match = sampleRegex.Match([]byte("1abc23"))
fmt.Printf("For 1abc23: %t\n", match)
match = sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
}
输出
For abcd: false
For 1abc23: false
For abc: true
在上述程序中,正则表达式是
^abc$
上述程序会匹配
abc
但对于下面的内容不匹配,因为它仅匹配完整字符串。
abcd
1abc23
此外,请查看我们的 Golang 进阶教程系列 – Golang 进阶教程*
Golang 正则表达式:在正则表达式中匹配数字或数值数字。
目录
-
概述
-
匹配单个数字
-
匹配数字重复
概述
\d 可用于在 Golang 中匹配数字。实际上,\d 可用于匹配整个范围。
0-9
匹配任何数字的正则表达式将是。
\d
如果你只想匹配特定数字,比如说 5,那么正则表达式就是那个数字。
5
如果你想匹配两个数字,那么下面是正则表达式。
\d\d
匹配单个数字
让我们看一个示例。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile(`\d`)
fmt.Println("For regex \\d")
match := sampleRegexp.MatchString("1")
fmt.Printf("For 1: %t\n", match)
match = sampleRegexp.MatchString("4")
fmt.Printf("For 4: %t\n", match)
match = sampleRegexp.MatchString("9")
fmt.Printf("For 9: %t\n", match)
match = sampleRegexp.MatchString("a")
fmt.Printf("For a: %t\n", match)
sampleRegexp = regexp.MustCompile(`5`)
fmt.Println("\nFor regex 5")
match = sampleRegexp.MatchString("5")
fmt.Printf("For 5: %t\n", match)
match = sampleRegexp.MatchString("6")
fmt.Printf("For 6: %t\n", match)
}
输出
For regex \d
For 1: true
For 4: true
For 9: true
For a: false
For regex 5
For 5: true
For 6: false
在上述程序中,我们有两个正则表达式的示例。
-
\d – 匹配任何数字。
-
5 – 仅匹配数字五。
第一个匹配任何单个数字。这就是它的匹配原因。
1
4
9
它不会匹配。
a
从输出中也可以明显看出这一点。
第二个正则表达式只匹配 “5”,而不匹配 “6”,这从输出中可以明显看出。
For regex 5
For 5: true
For 6: false
匹配数字重复
量词可用于匹配数字的重复。示例
-
\d+ – 匹配一个或多个数字。
-
\d* – 匹配零个或多个数字。
-
\d{N} – 匹配 N 个数字。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile(`\d+`)
fmt.Println(`For regex \d+`)
match := sampleRegexp.MatchString("12345")
fmt.Printf("For 12345: %t\n", match)
match = sampleRegexp.MatchString("")
fmt.Printf("For empty string: %t\n", match)
sampleRegexp = regexp.MustCompile(`\d*`)
fmt.Println()
fmt.Println(`For regex \d*`)
match = sampleRegexp.MatchString("12345")
fmt.Printf("For 12345: %t\n", match)
match = sampleRegexp.MatchString("")
fmt.Printf("For empty string: %t\n", match)
sampleRegexp = regexp.MustCompile(`\d{2}`)
fmt.Println()
fmt.Println(`For regex \d{2}`)
match = sampleRegexp.MatchString("12")
fmt.Printf("For 12: %t\n", match)
match = sampleRegexp.MatchString("1")
fmt.Printf("For 1: %t\n", match)
}
输出
For regex \d+
For 12345: true
For empty string: false
For regex \d*
For 12345: true
For empty string: true
For regex \d{2}
For 12: true
For 1: false
在上述程序中,我们有三个正则表达式的示例。
-
\d+
-
\d*
-
\d{N}
\d+ 正则表达式会匹配 “12345”,但对空字符串失败。
\d* 会匹配 “12345” 以及一个空字符串。
\d{2} 匹配两个数字的序列。这就是为什么它能匹配 “12” 但对 “1” 失败。
此外,请查看我们的 Golang 高级教程系列 – Golang 高级教程*
Golang 正则表达式:匹配字符串的前缀或后缀
目录
-
概述
-
前缀匹配
-
后缀匹配
概述
Golang 正则表达式包含两个锚字符,可以用于匹配给定正则表达式的字符串的前缀和后缀。这两个字符是
-
插入符号字符‘^’ – 它是一个锚字符,可以用来匹配字符串的前缀。
-
美元字符‘$’ – 它是一个锚字符,可以用来匹配字符串的后缀。
让我们逐个查看前缀和后缀匹配。
前缀匹配
它是一个锚字符,可以用来匹配字符串的前缀。它在正则表达式的开始处使用,以确保给定的输入字符串从开头与正则表达式匹配。基本上,它匹配输入字符串的开头。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("^abc")
match := sampleRegex.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
match = sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
match = sampleRegex.Match([]byte("1abc23"))
fmt.Printf("For 1abc23: %t\n", match)
match = sampleRegex.Match([]byte("ab"))
fmt.Printf("For ab: %t\n", match)
}
输出
For abcd: true
For abc: true
For 1abc23: false
For ab: false
在上面的示例中,我们使用了以下正则表达式
^abc
请注意开头的插入符号字符。它将匹配任何以“abc”开头的字符串。尾部字符无关紧要。
它匹配所有以“abc”开头的字符串。这就是它能匹配的原因。
For abcd: true
For abc: true
并且不会匹配以下内容。
For 1abc23: false
For ab: false
后缀匹配
它是一个锚字符,可以用来匹配字符串的后缀。它在正则表达式的末尾使用,以确保给定的输入字符串与正则表达式在末尾匹配。它匹配输入字符串的结尾。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("abc$")
match := sampleRegex.Match([]byte("1abc"))
fmt.Printf("For 1abc: %t\n", match)
match = sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
match = sampleRegex.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
match = sampleRegex.Match([]byte("ab"))
fmt.Printf("For ab: %t\n", match)
}
输出
For 1abc: true
For abc: true
For abcd: false
For ab: false
在上面的示例中,我们使用了以下正则表达式
abc$
请注意末尾的美元字符。它将匹配任何以“abc”结尾的字符串。开头的字符无关紧要。
它匹配所有以“abc”结尾的字符串。这就是它能匹配的原因。
For 1abc: true
For abc: true
并且不会匹配以下内容。
For abcd: false
For ab: false
如果我们只想匹配完整字符串,则需要在开头用插入符号字符和在末尾用美元符号字符将字符串锚定。这样我们就能进行完整字符串匹配。请参见下面的示例。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("^abc$")
match := sampleRegex.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
match = sampleRegex.Match([]byte("1abc23"))
fmt.Printf("For 1abc23: %t\n", match)
match = sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
}
输出
For abcd: false
For 1abc23: false
For abc: true
在上面的程序中,正则表达式是
^abc$
上面的程序为
abc
但不会匹配以下内容,因为它仅匹配完整字符串。
abcd
1abc23
此外,请查看我们的 Golang 进阶教程系列 – Golang 进阶教程
Golang 正则表达式:匹配原始或字面字符串
目录
概述
+ MatchCompile 函数
+ 匹配方法
概述
在我们的示例中,我们将使用 Golang 中的 regexp 包,它提供了正则表达式搜索功能 golang.org/pkg/regexp/
在查看正则表达式本身之前,让我们先看一些 Go 提供的基本函数或方法来进行正则匹配。
MatchCompile 函数
golang.org/pkg/regexp/#MustCompile
以下是函数的签名
func MustCompile(str string) *Regexp
我们首先使用 MustCompile 函数编译给定的正则表达式字符串。如果给定的正则表达式无效,该函数将引发恐慌。成功编译给定的正则表达式后,它返回 regexp 结构的实例。
sampleRegexp := regexp.MustCompile("some_regular_expression"")
匹配方法
golang.org/pkg/regexp/#Regexp.Match
以下是该方法的签名
func (re *Regexp) Match(b []byte) bool
我们可以在 regexp 实例上调用 Match 方法,将给定模式与正则表达式进行匹配。如果正则表达式与输入字符串匹配,则返回 true,否则返回 false。我们需要将输入字符串的字节传递给此方法。
match := sampleRegexp.Match([]byte("some_string"))
让我们看看一个具有字面或原始字符串的基本正则表达式的简单程序
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("abc")
match := sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
match = sampleRegex.Match([]byte("1abc2"))
fmt.Printf("For 1abc2: %t\n", match)
match = sampleRegex.Match([]byte("xyz"))
fmt.Printf("For xyz: %t\n", match)
}
输出
For abc: true
For 1abc2: true
For xyz: false
在上面的程序中,我们有一个简单的字面字符串正则表达式
我们首先调用 MustCompile 来检查给定的正则表达式是否有效。之后,我们将其与下面的示例字符串或文本进行匹配
-
字符串 “abc” – 它匹配并返回 true
-
字符串 “1abc2” – 它匹配并返回 true。它匹配是因为它包含 “abc” 作为子字符串。
-
字符串 “xyz” – 它不匹配,返回 false
从上面的程序中可以看到,如果给定的字符串或文本包含正则表达式作为子字符串,它仍然会返回匹配。如果我们想进行全字符串匹配,则需要在正则表达式的开始和结束使用锚字符。插入符号锚字符将在开头使用,美元符号锚字符将在末尾使用。
有关全字符串匹配的详细信息,请参考本文
golangbyexample.com/golang-regex-match-full-string
此外,查看我们的 Golang 进阶教程系列 – Golang 进阶教程*
Golang 正则表达式:正则表达式中的可选操作符或问号(?)
目录
-
概述**
-
程序
-
问号操作符是非懒惰
-
关于双问号操作符
-
量词后的问号
概述
问号是正则表达式中的可选操作符。这意味着它可选地匹配问号之前的字符。
例如。
abcd?
这将匹配“abc”和“abcd”。
程序
让我们来看一个相同的例子。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile("abcd?")
match := sampleRegexp.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
match = sampleRegexp.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
}
输出
For abc: true
For abcd: true
通过用括号将多个字符闭合并在它们后面加上问号,也可以使多个字符变为可选。例如
abc(de)?
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile("abc(de)?")
match := sampleRegexp.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
match = sampleRegexp.Match([]byte("abcde"))
fmt.Printf("For abcde: %t\n", match)
match = sampleRegexp.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
}
输出
For abc: true
For abcde: true
For abcd: true
它匹配“abc”和“abcde”。
它也匹配了“abcd”。你一定在想为什么它会匹配“abcd”。
如果给定的字符串或文本包含正则表达式作为子字符串,那么它也会返回匹配。这就是为什么它会匹配,因为“abcd”包含“abc”作为子字符串,这对正则表达式来说是一个匹配。如果我们想进行完整字符串匹配,那么我们需要在正则表达式的开头和结尾使用锚字符。插入符号锚字符将用于开头,美元符号锚字符将用于结尾。
让我们来看一个相同的例子。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile("^abc(de)?$")
match := sampleRegexp.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
match = sampleRegexp.Match([]byte("abcde"))
fmt.Printf("For abcde: %t\n", match)
match = sampleRegexp.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
}
输出
For abc: true
For abcde: true
For abcd: false
问号操作符是非懒惰的
问号操作符是非懒惰或贪婪的。这意味着它将首先匹配可选模式。
在正则表达式的世界中,非懒惰(有时也称为贪婪)意味着尽可能多地匹配。而懒惰(有时也称为非贪婪)意味着仅匹配所需的部分。
例如,对于给定的正则表达式
https?
如果你尝试匹配以下输入字符串
Better is https
然后有两个选项
-
匹配http
-
匹配https
然后它将总是匹配https而从不匹配http。原因在于它是非懒惰的。即使它匹配http,也不会停止,而是尝试匹配可选字符。如果可选字符匹配,则返回https,否则返回http。
让我们来看一个相同的例子。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile("https?")
match := sampleRegexp.Find([]byte("Better is https"))
fmt.Printf("Match: %s\n", match)
}
输出
Match: https
在上述程序中,我们使用了查找函数,该函数返回与正则表达式匹配的实际子字符串。正如你在输出中注意到的,它匹配了“https”而不是“http”,因为问号操作符是非懒惰的。
关于双问号操作符
它是懒惰的。它一旦找到第一个匹配,就不再尝试进一步匹配。因此,对于上述文本,它总是返回结果为“http”,而从不返回“https”。
让我们来看一个例子。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile("https??")
match := sampleRegexp.Find([]byte("Better is https"))
fmt.Printf("Match: %s\n", match)
}
输出
Match: http
量词后的问号
在量词后的问号‘?’是懒惰或非贪婪的。量词可以是
-
加号‘+’ – 一个或多个
-
星号‘*’ – 零个或多个
见下面的示例
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile("http(s+?)")
match := sampleRegexp.Find([]byte("Better is httpsss"))
fmt.Printf("Match: %s\n", match)
sampleRegexp = regexp.MustCompile("http(s*?)")
match = sampleRegexp.Find([]byte("Better is httpsss"))
fmt.Printf("Match: %s\n", match)
}
输出
Match: https
Match: http
在上面的程序中,我们有两种情况
-
在加号运算符后面加上问号
-
在星号运算符后面的问号
在这两种情况下,输入字符串是
Better is httpsss
在第一种情况下,我们在正则表达式中的加号运算符后使用了问号
"http(s+?)"
它返回的匹配是“https”而不是“httpsss”,因为在加号运算符后使用的问号是非贪婪的
在第二种情况下,我们在正则表达式中的星号符号后使用了问号
"http(s*?)"
它返回的匹配是“http”而不是“httpsss”,因为在星号运算符后使用的问号是非贪婪的
让我们看另一个示例
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile("(a+?)(a*)")
match := sampleRegexp.FindStringSubmatch("aaaaaaa")
fmt.Printf("Match: %s Length: %d\n", match, len(match))
sampleRegexp = regexp.MustCompile("(a*?)(a*)")
match = sampleRegexp.FindStringSubmatch("aaaaaaa")
fmt.Printf("Match: %s Length: %d\n", match, len(match))
}
输出
Match: [aaaaaaa a aaaaaa] Length: 3
Match: [aaaaaaa aaaaaaa] Length: 3
在上面的程序中,我们又有两种情况
-
在加号运算符后加上问号
-
在星号运算符后加上问号
在第一种情况下,我们有带有两个捕获组的正则表达式
(a+?)(a*)
第一个捕获组给出一个匹配‘a’,而第二个捕获组给出其余部分。这表明问号运算符在加号运算符后使用是非贪婪或懒惰的
在第二种情况下,我们再次有带有两个捕获组的正则表达式
(a*?)(a*)
第一个捕获组给出零个匹配‘a’,而第二个捕获组给出其余部分。这表明问号运算符在星号运算符后使用是非贪婪或懒惰的
这就是关于 Go 中问号运算符的全部内容。希望你喜欢这篇文章。请在评论中分享反馈。
另外,查看我们的 Golang 高级教程系列 – Golang 高级教程
Golang 正则表达式:替换所有匹配正则表达式的字符串
目录
-
概述
-
替换字符串作为字面字符串
-
一个包含未命名子匹配的捕获组的字符串
-
一个包含命名子匹配的捕获组的字符串
概述
Golang regexp 包提供了一种名为 ReplaceAllString 的方法,该方法可以用来替换与正则表达式匹配的字符串中的所有子字符串。
golang.org/pkg/regexp/#Regexp.ReplaceAllString
下面是该方法的签名
func (re *Regexp) ReplaceAllString(src, repl string) string
它接受两个参数
-
第一个是输入字符串
-
第二个是替换字符串。
ReplaceAll 返回src字符串的副本,将正则表达式的匹配项替换为替换字符串repl。
替换字符串可以是
-
一个字面字符串
-
一个包含未命名子匹配的捕获组的字符串。
-
一个包含命名子匹配的捕获组的字符串
听起来很混乱?当我们看到所有的示例时,一切都会变得清晰。让我们来看看它们
替换字符串作为字面字符串
在这种情况下,替换字符串是一个字面字符串。下面是相同的示例。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile(`\w+:[0-9]\d{1,3}`)
input := "The persons are John:21, Simon:23, and Mike:19"
result := sampleRegexp.ReplaceAllString(input, "redacted")
fmt.Println(string(result))
}
输出
The persons are redacted, redacted, and redacted
在上述示例中,我们有下面的正则表达式
`\w+:[0-9]\d{1,3}`
它匹配形式的名称和年龄对
name:age
然后我们有下面的输入字符串,它包含三个name:age对
The persons are John:21, Simon:23, Mike:19
我们通过用屏蔽关键字替换它,删除了所有的name:age对。
result := sampleRegexp.ReplaceAllString(input, "redacted")
请注意,替换字符串是一个字面字符串
redacted
一个包含未命名子匹配的捕获组的字符串
这是我们在正则表达式中有捕获组的情况。有关捕获组的详细信息,请参考这篇文章
在替换字符串中
-
\(1** 或 **\){1} 代表第一个子匹配
-
\(2** 或 **\){2} 代表第二个子匹配
-
…等等
让我们看下面的示例,这样会更清楚
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile(`(\w+):([0-9]\d{1,3})`)
input := "The names are John:21, Simon:23, and Mike:19"
result := sampleRegexp.ReplaceAllString(input, "$2:$1")
fmt.Println(string(result))
}
输出
The names are 21:John, 23:Simon, and 19:Mike
在上述示例中,我们有下面的正则表达式
(\w+):([0-9]\d{1,3})
它与上面相同,匹配name和age对,但它有两个捕获组(用括号括起来),其中$1捕获名称,$2捕获年龄
- \(0** 或 **\){0} – 完整匹配
(\w+):([0-9]\d{1,3})
- \(1** 或 **\){1} – 第一个子匹配
(\w+)
- \(2** -或 **\){2} – 第二个子匹配
([0-9]\d{1,3})
然后我们有下面的输入字符串,它包含三个name:age对
The names are John:21, Simon:23, and Mike:19
然后在替换字符串中,我们交换了顺序,使age先于name
result := sampleRegexp.ReplaceAllString(input, "$2:$1")
这就是为什么在输出中我们先有age然后是name
The names are 21:John, 23:Simon, and 19:Mike
你也可以只替换一个子匹配。例如,如果你只想遮蔽年龄,那也可以做到。见下面的程序。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile(`(\w+):([0-9]\d{1,3})`)
input := "The names are John:21, Simon:23, and Mike:19"
result := sampleRegexp.ReplaceAllString(input, "$1")
fmt.Println(string(result))
}
输出
The names are John, Simon, and Mike
在上面的代码中,替换字符串是。
$1
所以它用仅有的姓名替换整个姓名:年龄对。
你也可以使用$0。它代表整个匹配。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile(`(\w+):([0-9]\d{1,3})`)
input := "The names are John:21, Simon:23, and Mike:19"
result := sampleRegexp.ReplaceAllString(input, "-$0-")
fmt.Println(string(result))
}
输出
The names are -John:21-, -Simon:23-, and -Mike:19-
在上面的示例中,我们在$0前后添加了‘-‘。
-$0-
这就是为什么输出如上所示。
如果替换字符串包含字面美元符号,我们可以使用ReplaceAllLiteralString方法。
golang.org/pkg/regexp/#Regexp.ReplaceAllLiteralString
在这里,替换字符串直接被替代,没有任何分析,即被字面使用。见下面的示例。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile(`(\w+):([0-9]\d{1,3})`)
input := "The names are John:21, Simon:23, and Mike:19"
result := sampleRegexp.ReplaceAllLiteralString(input, "$1")
fmt.Println(string(result))
}
输出
The names are $1, $1, and $1
如上所示,输出打印了字面上的美元符号。
一个包含命名子匹配的捕获组字符串
这是当我们在正则表达式中有命名捕获组的情况。要了解捕获组的详细信息,请参阅这篇文章。
首先让我们看看一个程序,在这个程序中我们将看到命名捕获组。然后我们会详细分析这个程序以更清楚地理解。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegexp := regexp.MustCompile(`(?P<name>\w+):(?P<age>[0-9]\d{1,3})`)
input := "The names are John:21, Simon:23, Mike:19"
result := sampleRegexp.ReplaceAllString(input, "$Age:$Name")
fmt.Println(string(result))
}</age></name>
输出
The names are 21:John, 23:Simon, 19:Mike
在上面的示例中,我们有以下正则表达式。
`(?P<name>\w+):(?P<age>[0-9]\d{1,3})`</age></name>
它与上述的姓名和年龄配对相匹配,但有两个姓名捕获组。
- 第一个捕获组的名称是“姓名”。
(?P<name>\w+)</name>
- 第二个捕获组的名称是“年龄”。
(?P<age>[0-9]\d{1,3})</age>
然后我们有下面的输入字符串,它包含三个姓名:年龄对。
The names are John:21, Simon:23 and Mike:19
然后在替换字符串中,我们交换了位置,使年龄在前,姓名在后。
result := sampleRegexp.ReplaceAllString(input, "$Age:$Name")
这就是为什么在输出中我们先有年龄,然后是姓名。
The names are 21:John, 23:Simon, 19:Mike
这就是关于在 Golang 中替换匹配正则表达式的字符串。希望你喜欢这篇文章。请在评论中分享反馈。
此外,查看我们的 Golang 进阶教程系列 – Golang 进阶教程。
Golang 正则表达式:理解插入符号和美元字符
来源:
golangbyexample.com/golang-regex-understanding-caret-and-dollar-character/
目录
-
概述
-
程序
概述
插入符号字符‘^’ 和 美元字符‘$’ 是在 golang 中使用的元字符。元字符是指在正则表达式中具有特殊意义的字符。下面是 golang 中插入符号字符和美元字符的简要介绍。
-
插入符号字符‘^’ – 它是一个锚字符,通常用在正则表达式的开头,以确保给定的输入字符串从开头与正则表达式匹配。它匹配输入字符串的开头
-
美元字符‘$’ – 它也是一个锚字符,通常用在正则表达式的末尾,以确保给定的输入字符串在末尾与正则表达式匹配。它匹配输入字符串的末尾
程序
插入符号和美元元字符是分别用于匹配字符串的开始和结束的锚字符。不清楚吗?让我们通过一个例子来理解。假设你有下面的正则表达式,包含三个简单字符
abc
这个正则表达式将匹配任何包含abc作为子字符串的字符串。请看下面的例子
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("abc")
match := sampleRegex.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
match = sampleRegex.Match([]byte("1abc23"))
fmt.Printf("For 1abc23: %t\n", match)
match = sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
}
输出
For abcd: true
For 1abc23: true
For abc: true
上述程序会匹配所有包含“abc”作为子字符串的字符串。因此它会提供匹配
abc
abcd
1abc23
基本上,它会为任何包含“abc”作为子字符串的输入字符串提供匹配。
如果我们只想匹配完整字符串,则需要在开头用插入符号字符锚定字符串,并在末尾用美元字符锚定。这将使我们能够进行完整字符串匹配。请看下面的例子。
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("^abc$")
match := sampleRegex.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
match = sampleRegex.Match([]byte("1abc23"))
fmt.Printf("For 1abc23: %t\n", match)
match = sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
}
输出
For abcd: false
For 1abc23: false
For abc: true
上述程序提供匹配
abc
但不匹配下面的内容,因为它仅匹配完整字符串
abcd
1abc23
这是我们想匹配完整字符串的情况。如果我们还想匹配以“abc”开头的字符串。尾随字符无关紧要。我们该怎么做?你猜对了。在这种情况下,我们只需在正则表达式的开头使用插入符号字符,而不使用美元字符。请看下面的例子
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("^abc")
match := sampleRegex.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
match = sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
match = sampleRegex.Match([]byte("1abc23"))
fmt.Printf("For 1abc23: %t\n", match)
match = sampleRegex.Match([]byte("ab"))
fmt.Printf("For ab: %t\n", match)
}
输出
For abcd: true
For abc: true
For 1abc23: false
For ab: false
它为所有以“abc”开头的字符串提供匹配。这就是为什么它提供匹配的原因
For abcd: true
For abc: true
并且不匹配
For 1abc23: false
For ab: false
如果我们想匹配字面上的插入符号 ^,则需要用反斜杠转义它。请看下面的例子
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("\\^abc")
match := sampleRegex.Match([]byte("^abc"))
fmt.Printf("For ^abc: %t\n", match)
match = sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
}
输出
For abcd: true
For abcd: false
如果我们还想匹配以“abc”结尾的字符串。起始字符无关紧要。我们该怎么做?在这种情况下,我们只需在正则表达式的开头使用美元字符,而不在开头使用插入符号字符。请看下面的例子
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("abc$")
match := sampleRegex.Match([]byte("1abc"))
fmt.Printf("For 1abc: %t\n", match)
match = sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
match = sampleRegex.Match([]byte("abcd"))
fmt.Printf("For abcd: %t\n", match)
match = sampleRegex.Match([]byte("ab"))
fmt.Printf("For ab: %t\n", match)
}
输出
For 1abc: true
For abc: true
For abcd: false
For ab: false
它为所有以“abc”结尾的字符串提供匹配。这就是为什么它提供匹配的原因
For 1abc: true
For abc: true
并且不匹配
For abcd: false
For ab: false
如果我们想匹配字面上的美元符号 $,则需要用反斜杠进行转义。请看下面的示例
package main
import (
"fmt"
"regexp"
)
func main() {
sampleRegex := regexp.MustCompile("abc\\$")
match := sampleRegex.Match([]byte("abc$"))
fmt.Printf("For abc$: %t\n", match)
match = sampleRegex.Match([]byte("abc"))
fmt.Printf("For abc: %t\n", match)
}
输出
For abc$: true
For abc: false
此外,请查看我们的 Golang 进阶教程系列 – Golang 进阶教程*