Golang Regex:正则表达式中的可选操作符或问号(?)

1,137 阅读5分钟

概述

问号是regex中的可选操作符。这意味着它可以选择匹配问号前的字符

例如。

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"这个子串**,这是一个匹配的反义词。如果我们想做完整的字符串匹配,那么我们需要在regex的开头和结尾使用锚点字符。 锚点字符将被用在开头,Dollar锚点字符将被用在结尾。

让我们看一个同样的例子。

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

问号操作符是非疲劳的

问号运算符是非懒惰或贪婪的。这意味着它将首先匹配可选的模式。

在正则表达式的世界里,非懒惰(有时也称为贪婪)意味着要尽可能多地匹配。而懒惰(有时也称为非贪婪)意味着只匹配需要的内容。

例如,对于给定的regex

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

在上面的程序中,我们使用了Find函数,它返回与regex匹配的实际子串。你可以注意到,在输出中,它匹配的是**"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',而第二个捕获组给出了其余的。这表明在Asterisk**运算符之后使用的问号运算符是非贪婪的或懒惰的。

以上是关于Go中的问号运算符的全部内容。希望你喜欢这篇文章。请在评论中分享反馈。

另外,请查看我们的Golang高级教程系列------。 Golang高级教程

The postGolang Regex:正则表达式中的可选操作符或问号(?)首次出现在Welcome To Golang By Example上。