正则表达式详解

  版权声明本文参考了《菜鸟教程》。 未经作者允许,严禁用于商业出版,否则追究法律责任。网络转载请注明出处,这是对原创者的起码的尊重!!!


1 正则表达式简介

  正则表达式描述了一种字符串匹配的模式(pattern),可以用来*检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

1.1 引擎

正则引擎分为:DFA、NFA。

  • DFA——文本主导,控制权在文本。
    • 不支持回溯,不能捕获子表达式,不支持反向引用。
    • 于文本串里的每一个字符只需扫描一次。
    • 最长的左子正则式优先匹配成功。
  • NFA——表达式主导,控制权在表达式。
    • 支持回溯,能捕获子表达式,支持反向引用。
    • NFA又分为两种
      • 传统的 NFA
        • 对同一字符可能测试多次。
        • 最左子正则式优先匹配成功,因此偶尔会错过最佳匹配结果。
      • POSIX NFA
        • 与传统NFA不同之处在于,找到最左侧最长匹配之前,它将继续回溯。
        • 最左最长子正则式优先匹配成功,因此偶尔会错过最佳匹配结果。
引擎 支持程序/语言
DFA awk、egrep、flex、lex、MySQL、Procmail等
传统型NFA GNU Emacs、Java、ergp、less、more、.NET、PCRE library、Perl、PHP、Python、Ruby、sed、vi;
POSIX NFA mawk、Mortice Kern Systems’ utilities、GNU Emacs(使用时可以明确指定);
DFA/NFA混合 GNU awk、GNU grep/egrep、Tcl。
       

1.2 支持情况

命令或环境 . [ ] ^ $ \( \) \{ \} ? + | ( )
vi
Visual C++
awk
sed
delphi
python
java
javascript
php
perl
C#

2 正则表达式语法

  正则表达式是由文本字符和元字符组成的文字模式。模式描述在搜索文本时要匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。

  正则表达式由子表达式构成。子表达式(也叫组件)可以是单个的字符,如aa+中的第二个a、字符集合[xyz]、[^xyz],字符范围[a-z]、[^a-z],字符间的选择(a|b)或者所有这些组件的任意组合

2.1 普通字符

  普通字符包括所有大小写字母、所有数字、所有标点符号和一些其他符号。

2.2 元字符

  元字符包括限定符、定位符和其它一些指定意义的符号。

元字符 描述
\ 将下一个字符标记为特殊字符、或原义字符、或向后引用、或八进制转义符。例如,\n 匹配一个换行符。序列 \\ 匹配 \\( 则匹配 (
^ 匹配输入字符串的开始位置。并不是一个单词的开头,如果设置了 RegExp 对象的 Multiline 属性,^也匹配 \n\r之后的位置。
$ 匹配输入字符串的结束位置。并不是一个单词的结尾,如果设置了RegExp 对象的 Multiline 属性,$也匹配 \n\r 之前的位置。
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 z 以及 zoo。* 等价于{0,}
+ 匹配前面的子表达式一次或多次。例如,zo+ 能匹配zo 以及 zoo,但不能匹配 z+等价于 {1,}
? 匹配前面的子表达式零次或一次。例如,do(es)? 可以匹配 dodoes 中的do?等价于 {0,1}
{n} n 是一个非负整数。匹配确定的 n 次*。例如,o{2} 不能匹配 Bob 中的 o,但是能匹配 food 中的两个o
{n,} n 是一个非负整数。至少匹配n 次。例如,o{2,} 不能匹配 Bob 中的 o,但能匹配 foooood 中的所有 oo{1,} 等价于 o+o{0,} 则等价于 o*
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,o{1,3} 将匹配 fooooood 中的前三个 oo{0,1} 等价于 o?。请注意在逗号和两个数之间不能有空格。
限定符+ 占有优先量词与匹配优先量词(又名限定符)很相似,只不会交还已经匹配的字符。
(?>expr) 固化分组与正常的匹配并无区别,只是当匹配完括号中的内容后,括号中的备用状态会全部舍去。
? 当该字符紧跟在任何一个其他限制符(*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 ooooo+? 将匹配单个 o,而 o+ 将匹配所有 o
. 匹配\n 之外的任何单个字符。要匹配包括 \n 在内的任何字符,请使用(.|\n)或者[\s\S]的模式。
(expr) 匹配 pattern 并获取匹配结果,可用\number 引用匹配结果。匹配结果可以从产生的 Matches 集合得到,VBScript中使用SubMatches集合,JScript中则使用\$0…\$9属性。
(?:expr) 非获取匹配,匹配expr但不获取匹配结果,不进行存储供以后使用。这在使用或字符(|)来组合一个模式的各个部分时很有用。例如industr(?:y|ies)就是一个比industry|industries更简略的表达式。
exp1(?=expr2) 非获取匹配,正向肯定预查,在任何匹配expr2的字符串开始处查找exp1。例如,Windows(?=95|98|NT|2000)能匹配Windows2000中的Windows,但不能匹配Windows3.1中的Windows。预查不消耗字符,也就是说,在一个匹配发生后立即开始下一次匹配的搜索,而不是从预查的字符之后开始。
(?<=expr2)exp1 非获取匹配,反向肯定预查,在任何匹配expr2的字符串结尾处查找exp1。例如,(?<=95|98|NT|2000)Windows能匹配2000Windows中的Windows,但不能匹配3.1Windows中的Windows
exp1(?!expr2) 非获取匹配,正向否定预查,在任何不匹配expr2的字符串开始处查找exp1。例如Windows(?!95|98|NT|2000)能匹配Windows3.1中的Windows,但不能匹配Windows2000中的Windows
(? 非获取匹配,反向否定预查,在任何不匹配expr2的字符串结尾处查找exp1。例如(?<\!95|98|NT|20)Windows能匹配3.1Windows中的Windows,但不能匹配20Windows中的Windows。注意,任意一项都不能超过2个字符,如(?<\!95|98|NT|20)Windows正确(?<\!95|980|NT|20)Windows报错,若是单独使用则无限制,如(?<\!2000)Windows 正确匹配
x|y 匹配 x 或 y。例如,z|food 能匹配 zfood(z|f)ood 则匹配 zoodfood
[xyz] 字符集合。匹配所包含的任意一个字符。例如, [abc] 可以匹配 plain 中的 a
[^xyz] 负值字符集合。匹配未包含的任意一个字符。例如, [^abc] 可以匹配 plain 中的plin
[a-z] 字符范围。匹配指定范围内的任意字符。例如,[a-z] 可以匹配 az 范围内的任意小写字母字符。
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,[^a-z] 可以匹配任何不在 az 范围内的任意字符。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, er\b 可以匹配never 中的 er,但不能匹配 verb 中的 er
\B 匹配非单词边界er\B 能匹配 verb 中的 er,但不能匹配 never 中的 er
\cx 匹配由 x 指明的控制字符。例如, \cM匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 c 字符。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]。
\f 匹配一个换页符。等价于\x0c\cL
\n 匹配一个换行符。等价于\x0a\cJ
\r 匹配一个回车符。等价于 \x0d\cM
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]
\S 匹配任何非空白字符。等价于[^ \f\n\r\t\v]
\t 匹配一个制表符。等价于\x09\cI
\v 匹配一个垂直制表符。等价于 \x0b\cK
\w 匹配一个单词字符,包括字符、数字、下划线。等价于[A-Za-z0-9_]
\W 匹配一个非单词字符。等价于 [^A-Za-z0-9_]
\xn 匹配 n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,\x41 匹配 A\x041 则等价于 \x04 & 1。正则表达式中可以使用 ASCII 编码。
\num 匹配 num,其中num是一个正整数。对所获取的匹配的反向引用。例如,(.)\1匹配两个连续的相同字符。
\n 标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
\nm 标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。
\nml 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
\un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如,\u00A9匹配版权符号 (?)
\p{P} 小写 p 是 property 的意思,表示 Unicode 属性,用于 Unicode 正表达式的前缀。大括号内的P表示Unicode字符集七个字符属性之一:标点字符。其他六个属性:L:字母;M:标记符号(一般不会单独出现);Z:分隔符(比如空格、换行等);S:符号(比如数学符号、货币符号等);N:数字(比如阿拉伯数字、罗马数字等);C:其他字符。注:此语法部分语言不支持,例:javascript。
\<,\> 匹配词(word)的开始(\<)和结束(\>)。例如正则表达式\能够匹配字符串for the wise中的the,但是不能匹配字符串otherwise中的the。注意:这个元字符不是所有的软件都支持的。
      
[:alpha:] 大写和小写字母
[:alnum:] 数字、大写和小写字母
[:digit:] 数字
[:lower:] 小写字母
[:space:] 空格
[:upper:] 大写字母
[:whitespace:] 空白字符
[:space:] 空白字符
[:ascii:] 匹配任何七位的 ASCII 字符(0 到 127 之间的顺序值)
[:blank:] 匹配一个空格或水平制表符。[[:blank:]] 等效于 [ \t]
[:cntrl:] 匹配顺序值小于 32 或字符值为 127 的 ASCII 字符(控制字符)。控制字符包括换行符、换页符、退格符,等等。
[:graph:] 匹配打印字符。[[:graph:]] 等效于 [[:alnum:][:punct:]]
[:print:] 匹配打印字符和空格。[[:print:]] 等效于[[:graph:][:whitespace:]]
[:punct:] 匹配其中一个ascii字符,: "!#$%&'()*+,-./:;<=>?@[\]^_\{|}~`。但不包括非 ASCII 标点字符。
[:word:] 匹配字母、数字或下划线字符。[[:word:]]等效于 [[:alnum:]_]
[:xdigit:] 匹配字符类 [0-9A-Fa-f]中的字符。

2.2.1 限定符

  限定符用来指明前面的紧邻组件必须要出现多少次(只能管理紧紧在其之前的组件,也叫子表达式)才能满足匹配。限定符有*、+、?、{n}、{n,}、{n,m}六种。限定符*、+和?都是贪婪的,它们会尽可能多的匹配文字,只有在它们的后面加上一个?就可以转为非贪婪或最小匹配。

2.2.2 定位符

  定位符用来指明开始匹配和结束匹配的位置。定位符有^、$、\b、\B四种。不能将限定符放在定位符后。

2.2.3 选择

  用圆括号将所有选择项括起来,相邻的选择项之间用|分隔。但用圆括号会有一个副作用,是相关的匹配结果会被缓存,此时可用?:放在第一个选项前来禁止缓存匹配结果。

2.2.4 反向引用

  对一个正则表达式或组件两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从1开始,最多可存储99个捕获的子表达式。每个缓冲区都可以使用\n引用,其中 n为一个标识特定缓冲区的一位或两位十进制数。可以使用非捕获元字符 ?:、?= 或 ?!来取消捕获,忽略对相关匹配的保存。

2.2.5 固化分组和占有量词

  假设我们有这样的问题,把类似 3.690000023 的小数保留两位小数,类似 2.3563895 的小数保留三位小数,也就是说如果小数的第三位是 0, 则保留两位小数,如果是非 0, 就保留三位小数,该怎么呢? 这个问题比较简单,用下面的 Perl 代码。

$number =~ s/(\.\d\d[1-9]?)\d*/$1/;

  这个表达式完全可以工作,美中不足的一点是,当 $number 类似 3.695 的时候,我们把 .695 替换为了 .695,浪费了工夫。为了解决这个问题,我们把表达式稍稍修改一下。

$number =~ s/(\.\d\d[1-9]?)\d+/$1/;

  仅仅把星号替换成了加号,这样表示括号外至少有一位数字的时候才进行替换。看上去很完美,但是却出现了致命的错误,.695 被替换成了 .69了。这是为什么呢?在表达式 (.\d\d[1-9]?) 匹配了 .695 后, 后面的 \d+ 无法匹配了,为了使整个表达式匹配成功,引擎必须回溯,[1-9]?必须把匹配的数字吐出去,所以 5 被 \d+ 匹配了。这就是回溯造成的问题。事实上在这种情况下,我们不希望引擎回溯,有两种办法可以强迫引擎放弃回溯,固化分组和占有量词。

$number =~ s/(\.\d\d(?>[1-9]?))\d+/$1/; # 固化分组
$number =~ s/(\.\d\d[1-9]?+)\d+/$1/;    # 占有量词

  引擎放弃回溯后, 上面的表达式将无法匹配 .695,这正是我们想要的

2.2.6 零宽断言

  零宽指的是不消耗字符。断言指的是匹配表达式要满足断言。
  exp1(?=exp2),正向肯定断言,它断言exp1后面能匹配表达式exp。
  (?<=exp2)exp1,反向肯定断言,它断言exp1的前面能匹配表达式exp2。
  exp1(?!exp2),正向否定断言,断言exp1的后面不能匹配表达式exp2。
  (?<\!exp2)exp1,反向否定断言,断言exp1的前面不能匹配表达式exp2。

2.3 运算符优先级

  正则表达式从左到右进行计算,并遵循优先级顺序,这与算术表达式非常类似。
  相同优先级的从左到右进行运算,不同优先级的运算先高后低。

运算符 描述
\ 转义符
(), (?:), (?=),(?<=),(?!), (? 圆括号和方括号
*, +, ?, {n}, {n,}, {n,m} 限定符
^, $, \元字符、普通字符 定位符和序列(即:位置和顺序)
| 或操作

3 正则表达式分类

  • 基本的正则表达式(Basic Regular Expression 又叫 Basic RegEx 简称 BREs)
  • 扩展的正则表达式(Extended Regular Expression 又叫 Extended RegEx 简称 EREs)
  • Perl 的正则表达式(Perl Regular Expression 又叫 Perl RegEx 简称 PREs)

3.1 三种正则表达式对比

说明 BREs EREs PREs
转义符号 \ \ \
匹配行首,awk 指令中,’^’则是匹配字符串的开始 ^ ^ ^
匹配行尾,awk 指令中,’$’则是匹配字符串的结尾 $ $ $
匹配单词开始 \< \<
匹配单词结束 \> \>
匹配表达式并获取结果 \( \) () ()
跟在限定符(*, +, ?, {n},{n,}, {n,m})后面时,匹配模式是非贪婪的。
匹配除换行符(’\n’)之外的任意单个字符(awk 的句点能匹配换行符) . . .
匹配前面的子表达式 0 次或 1 次 \? ? ?
匹配前面的子表达式 0 次或多次 * * *
匹配前面的子表达式 1 次或多次 \+ + +
匹配前面的子表达式 n次 \{n\} {n} {n}
匹配前面的子表达式至少n次 \{n,\} {n,} {n,}
匹配前面的子表达式至少n次,至多m次 \{n,m\} {n,m} {n,m}
匹配 x 或 y x\|y x|y x|y
匹配例出的任意一个字符 [xyz] [xyz] [xyz]
匹配未例出的任意一个字符(不包括换行符,在awk中则包含换行符) [^xyz] [^xyz] [^xyz]
匹配范围内任意一个字符 [x-z] [x-z] [x-z]
匹配范围外的任意一个字符(不包括换行符,在awk中则包含换行符) [^x-z] [^x-z] [^x-z]
匹配一个数字字符 \d
匹配一个非数字字符 \D
匹配任何空白字符 \s
匹配任何非空白字符 \S
匹配一个单词字符,包括字符、数字、下划线。 \w \w \w
匹配一个非单词字符 \W \W \W
匹配一个单词边界,也就是指单词和空格间的位置 \b \b \b
匹配非单词边界 \B \B \B
匹配一个换页符 \f
匹配一个换行符 \n
匹配一个回车符 \r
匹配一个制表符 \t
匹配一个垂直制表符 \v
匹配转义字符本身”\” \\ \\ \\
匹配由 x 指明的控制字符, \cx
匹配 十六进制表示ASCIi字符 \xn
对所获取的匹配的引用 \num \num
匹配任何一个字母或数字 [:alnum:] [:alnum:] [:alnum:]
匹配任何一个字母 [:alpha:] [:alpha:] [:alpha:]
匹配任何一个数字 [:digit:] [:digit:] [:digit:]
匹配任何一个小写字母 [:lower:] [:lower:] [:lower:]
匹配任何一个大写字母 [:upper:] [:upper:] [:upper:]
匹配任何一个空白字符:制表符、空格 [:space:] [:space:] [:space:]
匹配空格和制表符(横向和纵向) [:blank:] [:blank:] [:blank:]
任何一个可以看得见的且可以打印的字符,不包括空格和换行符等 [:graph:] [:graph:] [:graph:]
任何一个可以打印的字符和空格 [:print:] [:print:] [:print:]
任何一个控制字符(ASCII 字符集中的前 32 个字符) [:cntrl:] [:cntrl:] [:cntrl:]
任何一个标点符号,不包括:[:alnum:]、[:cntrl:]、[:space:] [:punct:] [:punct:] [:punct:]
任何一个十六进制数(即:0-9,a-f,A-F) [:xdigit:] [:xdigit:] [:xdigit:]
匹配任何七位的 ASCII 字符(0 到 127 之间的顺序值) [:ascii:] [:ascii:] [:ascii:]
匹配字母、数字或下划线字符`。 [:word:] [:word:] [:word:]

3.2 Linux 正则表达式与运用

  • 字符集[[:class:]]表示,而不是[:class:]。
  • grep , egrep

    • 正则表达式特点:

      • grep 支持:BREs、EREs、PREs 正则表达式
        • grep 指令后不跟任何参数,则表示要使用 ”BREs“
        • grep 指令后跟 ”-E” 参数,则表示要使用 “EREs“
        • grep 指令后跟 “-P” 参数,则表示要使用 “PREs”
      • egrep 支持:EREs、PREs 正则表达式
        • egrep 指令后不跟任何参数,则表示要使用 “EREs”
        • egrep 指令后跟 “-P” 参数,则表示要使用 “PREs”
    • 功能与作用
      • grep 与 egrep 的处理对象:文本文件
      • grep 与 egrep 的处理过程:查找文本文件中是否含要查找的 “关键字”(关键字可以是正则表达式) ,如果含有要查找的 ”关健字“,那么默认返回该文本文件中包含该”关健字“的该行的内容,并在标准输出中显示出来,除非使用了“>” 重定向符号,
      • grep 与 egrep 在处理文本文件时,是按行处理的
  • sed

    • 正则表达式特点
      • sed 文本工具支持:BREs、EREs
      • sed 指令默认是使用”BREs”
      • sed 命令参数 “-r ” ,则表示要使用“EREs”
    • 功能与作用
      • sed 处理的对象:文本文件
      • sed 处理操作:对文本文件的内容进行 — 查找、替换、删除、增加等操作
      • sed 在处理文本文件的时候,也是按行处理的
  • awk(gawk)

    • 正则表达式特点
      • Awk 文本工具支持:EREs
      • awk 指令默认是使用 “EREs”
    • Awk 文本工具处理文本的特点
      • awk 处理的对象:文本文件
      • awk 处理操作:主要是对列进行操作

3.3 三种不同类型正则表达式比较

  • 当使用 BERs(基本正则表达式)时,必须在下列“?,+,|,{,},(,)”前加上转义字符(’\’),屏蔽掉它们的 原始含义。

  • 注意:修饰符用在正则表达式结尾,例如:/dog/i,其中 “ i “ 就是修饰符,它代表的含义就是:匹配时不区分大小写,那么修饰符有哪些呢?常见的修饰符如下:

    • g 全局匹配(即:一行上的每个出现,而不只是一行上的第一个出现
    • s 把整个匹配串当作一行处理
    • m 多行匹配
    • i 忽略大小写
    • x 允许注释和空格的出现
    • U 非贪婪匹配

  版权声明本文参考了《菜鸟教程》。 未经作者允许,严禁用于商业出版,否则追究法律责任。网络转载请注明出处,这是对原创者的起码的尊重!!!


你可能感兴趣的:(程序员基础知识)