正则表达式

前言

本文详细介绍了正则表达式的基础内容

正则表达式基础内容

RE 字符/范例 说明
\ 跳脱/转义字符
占位符 表示一个字符
^word ^行首, 待匹配的字符串word在行首!
…… grep -n '^#' re.txt 显示行首为 # 的行
word$ $行尾, 待匹配的字符串word在行尾!
…… grep -n '!$' re.txt 显示行尾为 ! 的行
. 代表任意一个字符, 必须有一个字符
…… grep -n 'e.e' re.txt 结果为 eve eee e e, 但不能是 ee
[] 字符集合. ^ \ -外, 其它特殊字符在中括号被认为是普通字符. 系统字符集可以包含其中如[\d]
[^] 中括号内的第一个^表示补集, 而不是行尾, 也可以理解为非. 非第一个则为普通字符
…… grep -n '[(+*)]' re.txt 匹配 ( + * ) 中的一个字符.
…… grep -n '[0-9]' re.txt 搜寻含有任意数字的那一行. 中括号内的-表连续(由编码决定),
…… grep -n 'oo[^0-9]' re.txt 排除结果 oo0-oo9, 可以是 ooa oog等等
数量符 跟在一个字符或组的后面, 重复该RE字符或组若干次
* 零个或多个的前一RE字符
…… grep -n 'ess*' re.py 结果为 es(0次s), ess, esss
…… .* 表示任意字符
{m,n} 连续m到n个的前一RE字符
…… {m,} 连续m个及以上的前一RE字符
…… {m} 连续m个的前一RE字符
…… grep -n 'go\{2,3\}g' re.txt 结果为 goog(2次o), gooog(3次o).
? 零个或一个的前一RE字符, 等同于{0,1}
…… egrep -n 'go?d' re.txt 只能匹配gdgod
+ 重复一个或以上的前一RE字符, 等同于{1,}
…… egrep -n 'go+d' re.txt 匹配god good等, 但不能匹配gd
*? +? ?? {m,n}? * + ? {m,n} 变得非贪婪, 即匹配尽可能少的字符
…… <.*>匹配'<H1>title</H1>' 会得到 '<H1>title</H1>' 整个字符串, 而不是预想的'<H1>'
…… <.*?>匹配'<H1>title</H1>' *?变得非贪婪, 会得到 '<H1>' 字符串
逻辑分组 对字符进行分组和判断
() 分组字符串
…… egrep 'A(xyz)+C' 匹配A开头, C结尾, 中间有一个以上”xyz”的字符串, 如 AxyzC AxyzxyzxyzC
\number 反向引用, 引用编号为number的分组()匹配到字符串
…… (\d)abc\1 匹配结果 1abc15abc5
  • | 理解为或即可, 用来隔开多个正则表达式.
  • 譬如 egrep -n 'gd|good' re.txt 只能匹配gdgood
  • 譬如 egrep -n 'g(la|oo)d' re.txt 只能匹配gladgood

正则表达式的兼容性问题

PCRE

即 Perl Compatible Regular Expression
常见的正则表达式记法,其实都源于Perl.
实际上,正则表达式是从Perl衍生出一个显赫的流派, 称为PCRE
\d \w \s 之类的记法,就是这个流派的特征.

Python 的re库就是使用的PCRE. 其系统字符集如下:

系统字符集 含义
\d digital, 数字字符, [0-9]
\D 非数字字符, [^\d]
\s space, 空白字符, [ \t\r\n\f\v]
\S 非空白字符, [^\s]
\w word, 单词字符, [A-Za-z0-9_]
\W 非单词字符, [^\w]
\A 仅匹配字符串开头
\Z 仅匹配字符串结尾
\b 提取指定的\w
\bfoo\b 匹配 foo foo. (foo) b foo z, 不匹配 foobar
\B [^\b], 指定部分内容提取\w
py\B 匹配 python py3, 不匹配 py py. py!

POSIX

linux是遵循POSIX标准的, 因此在linux下使用正则表达式时, 系统字符集如下:

系统字符集 含义
[:alnum:] 单词字符, [A-Za-z0-9_]
[:alpha:] 字母字符, [A-Za-z]
[:ascii:] ASCII字符, [\x00-\x7F]
[:blank:] 空格字符, [ \t]
[:cntrl:] 控制键字符, [\x00-\x1F\x7F]
[:digit:] 数字字符, [0-9]
[:graph:] 非空字符, [\x21-\x7E], [:blank:]的补集
[:lower:] 小写字母, [a-z]
[:print:] 可被打印的字符, [\x20-\x7E]
[:punct:] 所有标点符号, [][!"#$%&'()*+,./:;<=>?@\^_{}~-]丨`
[:upper:] 大写字母 [A-Z]
[:space:] 空白字符 [ \t\r\n\v\f]
[:word:] 字母字符 [A-Za-z_]
[:xdigit:] 16进制类型 [0-9A-Fa-f]

BRE, 基础RE

即, Basic Regular Expression
BRE只定义了6组元字符:

  • [], 用于在多个字符中选定一个字符进行匹配
  • ., 用于匹配任意字符
  • ^, 用于匹配时表示“非”的含义,还有一个用法是匹配行首
  • $, 用于匹配行尾
  • *, 零个或多个的前一RE字符
  • \, 跳脱/转义字符

在Linux/Unix常用工具中, grep vi sed都属于BRE这一派,
为了向前兼容并使用RE的一些新特性, 导致它的语法看起来比较奇怪. () {} 需要使用 \ 转义后才有特殊含义.
如果直接使用 a{1,2}, 只能去匹配 a{1,2} 字符串. 只有使用 a\{1,2\} 才能匹配为 aaa.
另外, BRE一般不支持 + ? (...|...) \number

ERE, 扩展RE

即, Extention Regular Expression
ERE在BRE上增加了3组元字符的定义:

  • {} 用于表示重复匹配的次数. BRE中将{}当作普通字符对待,必须加\进行转义, 即\{\}
  • (), 用于分组。BRE中只将()当作普通字符对待,必须加\进行转义,即\(\)
  • |, 完全为ERE新增的多项匹配能力定义的,BRE无多项匹配能力,只将|作普通字符对待
  • \number, ERE没有明确规定需要支持反向引用, 但不少工具都支持此功能

linux/unix下的RE表达式汇总

PCRE记法 vi/vim grep egrep awk sed
* * * * * *
+ \+ \+ + + \+
? \= \? ? ? \?
{m,n} \{m,n} \{m,n\} {m,n} {m,n} \{m,n\}
\b \< \> \< \> \< \> \< \> \y \< \>
(…) \(…\) \(…\) (…) (…) (…)
\1 \2 \1 \2 \1 \2 \1 \2 不支持 \1 \2

注意:

  • PCRE中常用\b来表示单词的起始或结束位,
  • Linux工具中, 通常用\<来匹配单词的起始位置, 用\>来匹配单词的结束位置
  • sed中的\y可以同时匹配这两个位置。

与bash shell的一些容易弄混的区别

特殊字符 bash shell中的含义 RE中的含义
* 零个到多个任意字符 重复零个或多个前一RE字符
? 一个任意字符 重复零个或一个前一RE字符
. 运行代码 source 一个任意字符
[0-9a-z] list内的一个字符 list内的一个字符
[0-9]的补集 [!0-9] () [^range] (!在RE里是普通字符)
{} {123,abc} 字符串匹配 {m,n} 重复m到n个前一RE字符
  • 匹配a开头的任意文件
    • bash下 ls a*
    • re下 ls | grep "^a.*"
  • 匹配string1或string2或更多之一字符串
    • bash下 {string1,string2,string3}. 如 touch a{xyz,123}.txt, 结果为 axyz.txt a123.txt
    • re下 (string1|string2|string3). 如 ls | egrep 'g(la|oo)d', 结果为 glad good
  • 连续字符匹配
    • bash下, 有两种连续字符表示法 {0..9}[0-9], 支持[1-3a-z], 不支持{1..3a..z},
    • bash下 touch {ex{1..3},ex4}.shtouch {ex{1..3},ex4}.sh, 结果为 ex1.sh ex2.sh ex3.sh ex4.sh
    • re下 ls | egrep "(ex[1-3]|ex4).sh", 结果为 ex1.sh ex2.sh ex3.sh ex4.sh
  • 简单总结, RE的功能远比bash自带的匹配符功能强大. 特别容易弄混的也就是 * ? [^range]

参考资料


原创于 DRA&PHO