#3 转载:正则表达式历史

2021-12-19

正则表达式在我们日常的软件开发过程中被广泛使用,例如编写 Nginx 配置文件、在 Linux 与 macOS 下查找文件,然而不同软件不同操作系统对于正则的应用有着不一样的行为,主要原因是正则表达式演进过程中,出现 POSIXPCRE 派系之分。

#2 正则表达式

2021-04-14
  1. https://zh.wikipedia.org/wiki/正则表达式
  2. 2021/12/19,正则表达式历史

正则表达式, Regular Expression, 简写: regex, regexp, 正则。
一般用来以指定模式对字符串进行查找,替换,切割。

基本知识

Linux 下, grep, find 等命令支持几种风格:

find -regextype help
# find: 未知的正则表达式类型 ‘help’;合法的类型是 ‘findutils-default’, ‘ed’, ‘emacs’, ‘gnu-awk’, ‘grep’, ‘posix-awk’, ‘awk’, ‘posix-basic’, ‘posix-egrep’, ‘egrep’, ‘posix-extended’, ‘posix-minimal-basic’, ‘sed’。
man grep | grep -E '\-regexp$'
#        -E, --extended-regexp
#        -G, --basic-regexp
#        -P, --perl-regexp
#        -w, --word-regexp
#        -x, --line-regexp

因为历史原因,大体上可以分为:

  • 基本型正则表达式(Basic Regular Expression,BRE)
  • 扩展型正则表达式(Extended Regular Express,ERE)
  • PCRE(Perl兼容正则表达式,Perl Compatible Regular Expressions)

我见过的编程语言中都是采用最为强大的 PCRE 风格:

  • PHP 是 pgeg_xxx 系列方法。
  • Python 是 re 包。
  • Go 是 regexp 包。
  • C 直接使用 regex.h (libc)
  • C 老版本也是 regex.h,或者使用第三方库(如 boost), C 11 之后就内置正则支持了(std::regex, 默认 ECMAScript 风格)

语法

特殊字符

| Characters / constructs | Corresponding article |
| :------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------- | ----------------- |
| \ , . , \cX , \d , \D , \f , \n , \r , \s , \S , \t , \v ,
\w , \W , \0 , \xhh , \uhhhh , \uhhhhh , [\b] | Character classes |
| ^ , $ , x(?=y) , x(?!y) , (?<=y)x , (?<!y)x , \b , \B | Assertions |
| (x) , (?:x) , (?<Name>x) , x | y,[xyz],[^xyz],\Number | Groups and ranges |
| * , + , ? , x{n} , x{n,} , x{n,m} | Quantifiers |
| \p{UnicodeProperty} , \P{UnicodeProperty} | Unicode property escapes |

范围

  • .
  • \w , \d , \s , \W , \D , \S
  • [abc] [^abc] [a-z]

分组

  • (abc)
  • \1
  • (?:abc)
  • (?=abc)
  • (?!abc)

量词

  • a* , a+ , a?
  • a{7,9} , a{7} , a{7,}
  • a+? , a{2,}?

其他

  • ^ , $
  • ab|cd or
  • \b , \B word

修饰符

JavaScript:

  • g global
  • i case insensitive
  • m multiline
  • s single line (dotall)
  • u unicode
  • y sticky

Go:

  • i
  • m
  • s
  • U

Python:

  • re.A, re.ASCII
  • re.I, re.IGNORECASE
  • re.M, re.MULTILINE
  • re.S, re.DOTALL
  • re.DEBUG
  • re.X, re.VERBOSE

参考资料与拓展阅读

#1 Golang: 正则表达式

2020-12-06

Compile 系列

func Compile(expr string) (*Regexp, error)
func CompilePOSIX(expr string) (*Regexp, error)
func MustCompile(str string) *Regexp
func MustCompilePOSIX(str string) *Regexp

函数名称中的 Must 表示:如果正则错误,直接 panic

Match 系列

func (re *Regexp) Match(b []byte) bool
func (re *Regexp) MatchReader(r io.RuneReader) bool
func (re *Regexp) MatchString(s string) bool

Find 系列

func (re *Regexp) Find(b []byte) []byte
func (re *Regexp) FindAll(b []byte, n int) [][]byte
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int
func (re *Regexp) FindAllString(s string, n int) []string
func (re *Regexp) FindAllStringIndex(s string, n int) [][]int
func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string
func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int
func (re *Regexp) FindIndex(b []byte) (loc []int)
func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int)
func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int
func (re *Regexp) FindString(s string) string
func (re *Regexp) FindStringIndex(s string) (loc []int)
func (re *Regexp) FindStringSubmatch(s string) []string
func (re *Regexp) FindStringSubmatchIndex(s string) []int
func (re *Regexp) FindSubmatch(b []byte) [][]byte
func (re *Regexp) FindSubmatchIndex(b []byte) []int

其实好记,Find(All)?(String)?(Submatch)?(Index)? 一组合就有 16 种了,再加上两个 FindReader 方法。

Replace 系列

func (re *Regexp) ReplaceAll(src, repl []byte) []byte
func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte
func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte
func (re *Regexp) ReplaceAllLiteralString(src, repl string) string
func (re *Regexp) ReplaceAllString(src, repl string) string
func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string

其他

func (re *Regexp) Copy() *Regexp // DEPRECATED
func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte
func (re *Regexp) ExpandString(dst []byte, template string, src string, match []int) []byte
func (re *Regexp) LiteralPrefix() (prefix string, complete bool)
func (re *Regexp) Longest()
func (re *Regexp) NumSubexp() int
func (re *Regexp) Split(s string, n int) []string
func (re *Regexp) String() string
func (re *Regexp) SubexpIndex(name string) int
func (re *Regexp) SubexpNames() []string

四个封装方法

func Match(pattern string, b []byte) (matched bool, err error)
func MatchReader(pattern string, r io.RuneReader) (matched bool, err error)
func MatchString(pattern string, s string) (matched bool, err error)
func QuoteMeta(s string) string

示例

func main() {
    text := "Hello, 2021! The year 2020 was great, but 2021 will be even better."

    pattern := `\b\d{4}\b` // 匹配四个数字的单词

    regex, err := regexp.Compile(pattern)
    if err != nil {
        fmt.Println("Error compiling regex:", err)
        return
    }

    matches := regex.FindAllString(text, -1)
    for _, match := range matches {
        fmt.Println(match)
    }
}

Output:

2021
2020
2021