基本概念[ 编辑]
正则表达式(通常称为模式)是用于指定特定目的所需的一组字符串的表达式。指定有限字符串集的简单方法是列出其元素或成员。但是,通常有更简洁的方法来指定所需的字符串集。例如,包含三个字符串“Handel”,“Händel”和“Haendel”的集合可以由模式 指定H(ä|ae?)ndel
; 我们说这种模式匹配三个字符串中的每一个。在大多数形式主义中,如果存在至少一个与特定集匹配的正则表达式,则存在无限数量的其他正则表达式也与其匹配 - 规范不是唯一的。大多数形式主义提供了以下操作来构造正则表达式。
布尔“或”
一个竖线分隔的替代品。例如,可以匹配“灰色”或“灰色”。gray|grey
分组
括号用于定义运算符的范围和优先级(以及其他用途)。例如,gray|grey
并且是描述“灰色”或“灰色”集合的等效模式。gr(a|e)y
定量
甲量词一个后令牌(例如字符)或组指定前一元件被允许的频率发生。最常见的量词是问号 ?
,星号 *
(源自Kleene星)和加号 +
(Kleene plus)。
? | 问号表示前一个元素出现零次或一次。例如,colou?r 匹配“颜色”和“颜色”。 |
* | 星号表示前一个元素出现零次或多次。例如,ab*c 匹配“ac”,“abc”,“abbc”,“abbbc”等。 |
+ | 加号表示前一个元素的一次或多次出现。例如,ab+c 匹配“abc”,“abbc”,“abbbc”等,但不匹配“ac”。 |
{n} [18] | 前面的项目恰好匹配n次。 |
{min,} [18] | 前面的项目匹配最少或更多次。 |
{min,max} [18] | 前述项目匹配至少分次,但不超过最大次数。 |
通配符
通配符.
匹配任何字符。例如,a.b
匹配任何包含“a”的字符串,然后匹配任何其他字符,然后a.*b
匹配“b”,匹配任何稍后包含“a”和“b”的字符串。
这些结构可以组合起来形成任意复杂的表达式,就像人们可以用数字和操作+,-,×和÷构造算术表达式一样。例如,H(ae?|ä)ndel
并且都是与前面示例匹配相同字符串的有效模式,。 H(a|ae|ä)ndel
H(ä|ae?)ndel
正则表达式的精确语法因工具和上下文而异; “ 语法”部分提供了更多详细信息。
形式语言理论[ 编辑]
正则表达式在形式语言理论中描述常规语言。它们具有与常规语法相同的表达能力。
正式定义[ 编辑]
正则表达式由常量组成,表示字符串集,运算符符号表示对这些集的操作。以下定义是标准的,并且在大多数关于形式语言理论的教科书中都可以找到。[19] [20]给定有限字母 Σ,以下常量被定义为正则表达式:
给定正则表达式R和S,定义以下操作以生成正则表达式:
- (串联)RS表示可以通过连接R中的字符串和S中的字符串来获得的字符串集。例如,令R = {“ab”,“c”},并且S = {“d”,“ EF“}。然后,RS = {“abd”,“abef”,“cd”,“cef”}。
- (交替)R | S表示由R和S描述的集合的集合。例如,如果R描述{“ab”,“c”}并且S描述{“ab”,“d”,“ef”},则表达式R | S描述{“ab”,“c”,“d”,“ef”}。
- (Kleene星)R *表示由R描述的集合中包含ε 的最小超集,并且在字符串连接下是闭合的。这是可以通过连接R描述的集合中的任何有限数量(包括零)字符串来生成的所有字符串的集合。例如,{“0”,“1”} *是所有有限二进制字符串(包括空字符串)的集合,{ {ab“,”c“} * = {ε,”ab“,”c“ ,“abab”,“abc”,“cab”,“cc”,“ababab”,“abcab”,......}。
为避免括号,假设Kleene星具有最高优先级,然后连接然后交替。如果没有歧义,则可省略括号。例如,(ab)c
可以写成abc
,a|(b(c*))
也可以写成a|bc*
。许多教科书使用符号∪,+或∨进行交替而不是垂直条。
例子:
a|b*
表示{ε,“a”,“b”,“bb”,“bbb”,......}(a|b)*
表示除“a”和“b”之外没有符号的所有字符串的集合,包括空字符串:{ε,“a”,“b”,“aa”,“ab”,“ba”,“bb” ,“aaa”,...}ab*(c|ε)
表示以“a”开头的字符串集,然后是零或更多“b”,最后可选地是“c”:{“a”,“ac”,“ab”,“abc”,“abb”,“abbc” “,...}(0|(1(01*0)*1))*
表示3的倍数的二进制数集合:{ε,“0”,“00”,“11”,“000”,“011”,“110”,“0000”,“0011”,“0110” ,“1001”,“1100”,“1111”,“00000”,...}
富有表现力和紧凑[ 编辑]
正则表达式的形式定义是故意简约并且避免限定冗余量词?
和+
,这可以如下表示:a+
= aa*
,和a?
= (a|ε)
。有时会添加补语运算符,以提供广义正则表达式 ; 这里R c匹配Σ*上与R不匹配的所有字符串。原则上,补码运算符是冗余的,因为它总是可以通过使用其他运算符来限制。然而,用于计算这种表示的过程是复杂的,并且结果可能需要具有双倍指数地增大的大小的表达式。[21] [22]
这种意义上的正则表达式可以表达正则语言,恰好是确定性有限自动机所接受的语言类。然而,紧凑性存在显着差异。某些常规语言类只能通过确定性有限自动机来描述,其大小以最短等效正则表达式的大小呈指数增长。这里的标准的例子是语言 大号ķ由所有字符串过字母表{ 一,b }其ķ 日 -从-最后一个字母等于 一个。一方面,描述L 4的正则表达式由下式给出 {\ displaystyle(a \ mid b)^ {*} a(a \ mid b)(a \ mid b)(a \ mid b)}。
将此模式推广为L k给出表达式: {\ displaystyle(a \ mid b)^ {*} a \ underbrace {(a \ mid b)(a \ mid b)\ cdots(a \ mid b)} _ {k-1 {\ text {times}} } \,}
另一方面,已知接受语言L k的每个确定性有限自动机必须具有至少2k个状态。幸运的是,有一个简单的映射,从正则表达式到更一般的非确定性有限自动机(NFA),不会导致这种大小的爆炸; 因此,NFA通常用作常规语言的替代表示。NFA是Chomsky层次结构的3型语法的简单变体。[19]
在相反的方向上,DFA很容易描述许多语言,这些语言不容易被描述为正则表达式。例如,确定给定ISBN号的有效性需要计算整数基数11的模数,并且可以用11状态DFA容易地实现。然而,用11来回答相同的可除性问题的正则表达式至少是几兆字节的长度。[ 引证需要 ]
给定正则表达式,Thompson的构造算法计算等价的非确定性有限自动机。通过Kleene算法实现相反方向的转换。
最后,值得注意的是,许多真实世界的“正则表达式”引擎实现了正式语言理论意义上的正则表达式无法描述的特征; 相反,他们实施正则表达式。有关详细信息,请参见下文。
确定正则表达式的等价性[ 编辑]
如上面的许多示例所示,构造正则表达式以实现相同结果的方法不止一种。
可以编写一种算法,对于两个给定的正则表达式,决定所描述的语言是否相等; 该算法将每个表达式减少到最小确定性有限状态机,并确定它们是否是同构的(等价的)。
正则表达式的代数定律可以使用Gischer的方法获得,最好沿着一个例子解释:为了检查(X + Y)*和(X * Y *)*是否表示相同的常规语言,对于所有正则表达式X,Y,检查特定正则表达式(a + b)*和(a * b *)*是否表示字母表中的相同语言Σ= { a,b } 是必要且充分的}。更一般地,当且仅当具有由不同符号常数替换的不同变量的实例化成立时,具有变量的正则表达式项之间的等式E = F成立。[23] [24]
通过使用Kleene star和set union来找到仍然完全表达的正则表达式的有趣子集,可以消除冗余,但也许可以限制它们的使用。[ 需要澄清 ]这是一个令人惊讶的难题。就像正则表达式一样简单,没有方法可以系统地将它们重写为某种正常形式。过去缺乏公理导致了星高问题。1991年,Dexter Kozen使用等式和Horn子句公理将正则表达式公理化为Kleene代数。[25] 早在1964年,雷德科已经证明,没有一套有限的纯粹等式公理可以表征常规语言的代数。[26]
语法[ 编辑]
正则表达式模式匹配目标字符串。该模式由一系列原子组成。原子是正则表达式模式中的单个点,它尝试与目标字符串匹配。最简单的原子是文字,但匹配原子的模式的分组部分将需要使用( )
元字符。元字符有助于形成:原子 ; 量词告诉了多少原子(以及它是否是一个贪婪的量词或不); 一个逻辑OR字符,它提供了一组备选方案,以及一个逻辑NOT字符,它否定了原子的存在; 和反向引用指的是完成原子模式的先前原子。进行匹配,而不是当字符串的所有原子匹配时,而是当正则表达式中的所有模式原子匹配时。我们的想法是让一个小的字符模式代表大量可能的字符串,而不是编译所有字面可能性的大型列表。
根据正则表达式处理器,大约有14个元字符,字符可能有也可能没有字面字符含义,具体取决于上下文,或者它们是否“被转义”,即前面是转义序列,在这种情况下是反斜杠\
。现代和POSIX扩展正则表达式使用元字符比它们的字面含义更常用,因此为了避免“反斜杠”或倾斜牙签综合症,将元字符转换为字面模式是有意义的; 但是从最初开始,让四个包围元字符( )
并且{ }
主要是字面意义更有意义,并且“逃避”这个通常意义成为元字符。通用标准同时实现。 {}[]()^$.|*+?
\
。转义时成为元字符的常用字符是dswDSW
和N
。
分隔符[ 编辑]
当用编程语言输入正则表达式时,它们可以表示为通常的字符串文字,因此通常被引用; 这在C,Java和Python中很常见,例如re
输入正则表达式"re"
。但是,它们通常用斜杠作为分隔符编写,就像/re/
正则表达式一样re
。这源于ed,其中/
是用于搜索的编辑器命令,并且表达式/re/
可用于指定一系列行(匹配模式),这些行可以与任何一方的其他命令组合,最着名的g/re/p
是grep(“global”正则表达式打印“),它包含在大多数基于Unix的操作系统中,例如Linux分布。在sed中使用了类似的约定,其中搜索和替换由给定,s/re/replacement/
并且模式可以用逗号连接以指定一系列行,如/re1/,/re2/
。由于它在Perl中的使用,这种表示法特别众所周知,它在语法中构成了与普通字符串文字不同的部分语法。在某些情况下,例如sed和Perl,可以使用替代分隔符来避免与内容冲突,并避免必须转义内容中出现的分隔符字符。例如,在sed命令s,/,X,
将取代/
具有X
,使用逗号作为分隔符。
标准[ 编辑]
在IEEE POSIX标准有三套合规性:BRE(基本正则表达式),[27] ERE(扩展正则表达式),和SRE(简单的正则表达式)。SRE被弃用,[28]赞成BRE,因为它们都提供了向后兼容性。以下有关字符类的小节适用于BRE和ERE。
BRE和ERE一起工作。ERE增加?
,+
以及|
和它不再需要为了躲避元字符( )
和{ }
,这是需要在BRE。此外,只要遵守正则表达式的POSIX标准语法,就可以并且通常是附加语法来为特定(但POSIX兼容)应用程序提供服务。虽然POSIX.2保留了一些未定义的实现细节,但BRE和ERE提供了一个“标准”,后来被用作许多工具的默认语法,其中BRE或ERE模式的选择通常是支持的选项。例如,GNU grep有以下选项:ERE为“grep -E”,BRE为“grep -G”(默认值),Perl正则表达式为“grep -P”。
Perl正则表达式已成为事实上的标准,具有丰富而强大的原子表达式集。Perl没有“基本”或“扩展”级别。如在POSIX ERES,( )
并且{ }
被视为元字符除非转义; 已知其他元字符仅基于上下文是字面的或符号的。其他功能包括延迟匹配,回溯,命名捕获组和递归模式。
POSIX基本和扩展[ 编辑]
在POSIX标准,基本正语法(BRE)要求的元字符 ( )
和{ }
指定\(\)
和\{\}
,而扩展正语法(ERE)没有。
元字符 | 描述 |
---|---|
^ | 匹配字符串中的起始位置。在基于行的工具中,它匹配任何行的起始位置。 |
. | 匹配任何单个字符(许多应用程序排除换行符,并且确切地说哪些字符被视为换行符是flavor,character-encoding-和特定于平台,但可以安全地假设包含换行符)。在POSIX括号表达式中,点字符与文字点匹配。例如,a.c 匹配“abc”等,但[a.c] 仅匹配“a”,“。”或“c”。 |
[ ] | 括号表达式。匹配括号内包含的单个字符。例如,[abc] 匹配“a”,“b”或“c”。[a-z] 指定一个范围,它匹配从“a”到“z”的任何小写字母。这些形式可以混合:[abcx-z] 匹配“a”,“b”,“c”,“x”,“y”或“z”,如同[a-cx-z] 。 如果 |
[^ ] | 匹配括号内未包含的单个字符。例如,[^abc] 匹配“a”,“b”或“c”以外的任何字符。[^a-z] 匹配从“a”到“z”不是小写字母的任何单个字符。同样,文字字符和范围可以混合使用。 |
$ | 匹配字符串的结束位置或字符串结尾换行符之前的位置。在基于行的工具中,它匹配任何行的结束位置。 |
( ) | 定义标记的子表达式。可以在以后调用括号内匹配的字符串(请参阅下一个条目)。标记的子表达式也称为块或捕获组。BRE模式需要。 \n \( \) |
\n | 匹配第n个标记的子表达式匹配的内容,其中n是1到9的数字。这个结构在POSIX.2标准中含糊不清。某些工具允许引用超过9个捕获组。 |
* | 匹配前面的元素零次或多次。例如,ab*c 匹配“ac”,“abc”,“abbbc”等[xyz]* 匹配“”,“x”,“y”,“z”,“zx”,“zyx”,“xyzzy”等。(ab)* 匹配“”,“ab”,“abab”,“ababab”等。 |
{
m,n} | 匹配前面的元素至少m次并且不超过n次。例如,a{3,5} 仅匹配“aaa”,“aaaa”和“aaaaa”。在一些旧版本的正则表达式中找不到这个。BRE模式需要\{
m,n\} 。 |
例子:
.at
匹配以“at”结尾的任何三个字符的字符串,包括“hat”,“cat”和“bat”。[hc]at
匹配“帽子”和“猫”。[^b]at
匹配.at
除“bat”以外的所有匹配的字符串。[^hc]at
匹配.at
除“hat”和“cat”以外的所有匹配的字符串。^[hc]at
匹配“hat”和“cat”,但仅限于字符串或行的开头。[hc]at$
匹配“hat”和“cat”,但仅限于字符串或行的末尾。\[.\]
匹配由“[”和“]”包围的任何单个字符,因为括号被转义,例如:“[a]”和“[b]”。s.*
匹配s后跟零个或多个字符,例如:“s”和“saw”和“seed”。
POSIX扩展[ 编辑]
对于POSIX扩展正则表达式(ERE)语法中的某些字符,使用反斜杠转义的元字符的含义相反。使用此语法,反斜杠会将元字符视为文字字符。所以,例如,现在和现在。此外,删除了对反向引用的支持,并添加了以下元字符: \( \)
( )
\{ \}
{ }
\n
元字符 | 描述 |
---|---|
? | 匹配前面的元素零次或一次。例如,ab?c 仅匹配“ac”或“abc”。 |
+ | 匹配前面的元素一次或多次。例如,ab+c 匹配“abc”,“abbc”,“abbbc”等,但不匹配“ac”。 |
| | 选择(也称为交替或集合并)运算符匹配运算符之前的表达式或运算符之后的表达式。例如,abc|def 匹配“abc”或“def”。 |
例子:
[hc]?at
匹配“at”,“hat”和“cat”。[hc]*at
匹配“at”,“hat”,“cat”,“hhat”,“chat”,“hcat”,“cchchat”等。[hc]+at
匹配“hat”,“cat”,“hhat”,“chat”,“hcat”,“cchchat”等,但不是“at”。cat|dog
匹配“猫”或“狗”。
通过包含命令行标志-E, POSIX扩展正则表达式通常可以与现代Unix实用程序一起使用。
字符类[ 编辑]
字符类是文字匹配后最基本的正则表达式概念。它使一个小的字符序列匹配更大的字符集。例如,可以代表大写字母,可以表示任何数字。字符类适用于两种POSIX级别。 [A-Z]
\d
指定字符范围(例如小写到大写)时,计算机的区域设置通过字符编码的数字排序来确定内容。它们可以存储该序列中的数字,或者排序可以是abc ... zABC ... Z或aAbBcC ... zZ。所以POSIX标准定义了一个字符类,它将由安装的正则表达式处理器知道。这些定义如下表所示: [a-Z]
a
z
POSIX | 非标 | 的Perl / Tcl的 | VIM | Java的 | ASCII | 描述 |
---|---|---|---|---|---|---|
[:ascii:] [29] | \p{ASCII} | [\x00-\x7F] | ASCII字符 | |||
[:alnum:] | \p{Alnum} | [A-Za-z0-9] | 字母数字字符 | |||
[:word:] [29] | \w | \w | \w | [A-Za-z0-9_] | 字母数字字符加“_” | |
\W | \W | \W | [^A-Za-z0-9_] | 非单词字符 | ||
[:alpha:] | \a | \p{Alpha} | [A-Za-z] | 字母字符 | ||
[:blank:] | \s | \p{Blank} | [ [[\t]]] | 空格和标签 | ||
\b | \< \> | \b | (?<=\W)(?=\w)|(?<=\w)(?=\W) | 字界限 | ||
\B | (?<=\W)(?=\W)|(?<=\w)(?=\w) | 非字边界 | ||||
[:cntrl:] | \p{Cntrl} | [\x00-\x1F\x7F] | 控制字符 | |||
[:digit:] | \d | \d | \p{Digit} 要么 \d | [0-9] | 数字 | |
\D | \D | \D | [^0-9] | 非数字 | ||
[:graph:] | \p{Graph} | [\x21-\x7E] | 可见字符 | |||
[:lower:] | \l | \p{Lower} | [a-z] | 小写字母 | ||
[:print:] | \p | \p{Print} | [\x20-\x7E] | 可见字符和空格字符 | ||
[:punct:] | \p{Punct} | [][!"#$%&'()*+,./:;<=>?@\^_`{|}~-] | 标点字符 | |||
[:space:] | \s | \_s | \p{Space} 要么 \s | [ \t\r\n\v\f] | 空白字符 | |
\S | \S | \S | [^ \t\r\n\v\f] | 非空白字符 | ||
[:upper:] | \u | \p{Upper} | [A-Z] | 大写字母 | ||
[:xdigit:] | \x | \p{XDigit} | [A-Fa-f0-9] | 十六进制数字 |
POSIX字符类只能在括号表达式中使用。例如,匹配大写字母和小写“a”和“b”。 [[:upper:]ab]
某些工具可以理解的另一个非POSIX类,通常定义为加下划线。这反映了这样一个事实,即在许多编程语言中,这些是可以在标识符中使用的字符。编辑器Vim进一步区分单词和词头类(使用符号和),因为在许多编程语言中,可以开始标识符的字符与可以在其他位置出现的字符不同。 [:word:]
[:alnum:]
\w
\h
请注意,POSIX正则表达式标准调用字符类通常被称为支持它们的其他正则表达式中的POSIX字符类。对于大多数其他正则表达式,术语字符类用于描述POSIX调用括号表达式。
Perl和PCRE [ 编辑]
另请参阅:Perl兼容的正则表达式
由于其表达能力和(相对)易读性,许多其他实用程序和编程语言采用了类似于Perl的语法 - 例如,Java,JavaScript,Python,Ruby,Qt,Microsoft的.NET Framework和XML Schema。一些语言和工具,如Boost和PHP支持多种正则表达式风格。Perl派生的正则表达式实现并不相同,通常实现1994年发布的Perl 5.0中的功能子集.Perl有时会包含最初在其他语言中发现的功能,例如,Perl 5.10实现了最初在PCRE和Python中开发的语法扩展。[30]
懒人匹配[ 编辑]
在Python和其他一些实现(例如Java)中,三个常见的量词(*
,+
和?
)默认是贪婪的,因为它们匹配尽可能多的字符。[31]".+"
应用于字符串的正则表达式(包括引号)
“Ganymede,”他继续道,“是太阳系中最大的卫星。”
匹配整行而不是仅匹配第一个单词"Ganymede,"
。上述量词可能,但是,作出懒惰或极少或不情愿,匹配尽可能少的字符可能通过附加一个问号:".+?"
只匹配"Ganymede,"
。[31]
但是,这并不能确保在某些情况下整个句子不匹配。问号运算符不会更改点运算符的含义,因此这仍然可以匹配输入中的引号。".*?" EOF
如果这是字符串,那么类似的模式仍将匹配整个输入
“Ganymede,”他继续道,“是太阳系中最大的卫星。” EOF
为了确保引号不能成为匹配的一部分,必须替换点,例如:"[^"]*"
这将匹配引用的文本部分,而不包含其他引号。
占有匹配[ 编辑]
在Java中,量词可以通过附加加号来占有欲,这会禁用后退,即使这样做会使整个匹配成功:[32]正则表达式".*"
应用于字符串
“Ganymede,”他继续道,“是太阳系中最大的卫星。”
匹配整行,正则表达式".*+"
根本不匹配,因为.*+
消耗了整个输入,包括最终输入"
。因此,占有量词对于否定的字符类是最有用的,例如"[^"]*+"
,"Ganymede,"
当应用于相同的字符串时匹配。
占有量词比贪婪和惰性量词更容易实现,并且通常在运行时更有效。[32]
非常规语言的模式[ 编辑]
几乎所有现代正则表达式库中的许多功能都提供了远远超过常规语言的表达能力。例如,许多实现允许使用括号对子表达式进行分组,并在同一表达式中回忆它们匹配的值(反向引用)。这意味着,除其他外,模式可以匹配重复单词的字符串,如“papa”或“WikiWiki”,在形式语言理论中称为正方形。这些字符串的模式是(.+)\1
。
由于泵浦引理,正方形的语言不规则,也没有上下文。然而,由众多现代工具支持的模式匹配与无限数量的反向引用仍然是上下文敏感的。[33]
但是,许多提供此类构造的工具,库和引擎仍然使用术语正则表达式作为其模式。这导致了术语正则表达在形式语言理论和模式匹配中具有不同含义的术语。出于这个原因,一些人已经采取措施来使用期限的正则表达式,正则表达式,或者干脆模式来描述后者。Perl编程语言的作者Larry Wall在一篇关于Perl 6设计的文章中写道:
“正则表达式”[...]仅与实际正则表达式略有关系。尽管如此,这个术语随着模式匹配引擎的功能而增长,所以我不打算在这里尝试对抗语言必需性。然而,我会将它们称为“正则表达式”(或“regexen”,当我处于盎格鲁 - 撒克逊的情绪时)。[17]
实施和运行时间[ 编辑]
至少有三种不同的算法决定给定的正则表达式是否以及如何与字符串匹配。
最古老和最快的依赖于形式语言理论的结果,该理论允许将每个非确定性有限自动机(NFA)转换为确定性有限自动机(DFA)。可以显式构造DFA,然后一次一个符号在结果输入字符串上运行。为大小为m的正则表达式构造DFA 的时间和内存成本为O(2 m),但它可以在时间O(n)的大小为n的字符串上运行。
另一种方法是直接模拟NFA,基本上按需构建每个DFA状态,然后在下一步丢弃它。这样可以隐藏DFA并避免指数构建成本,但运行成本会上升到O(mn)。显式方法称为DFA算法,隐式方法称为NFA算法。将缓存添加到NFA算法通常称为“延迟DFA”算法,或者仅称为DFA算法而不进行区分。这些算法很快,但使用它们来调用分组子表达式,延迟量化和类似特征是棘手的。[34] [35]
第三种算法是通过回溯将模式与输入字符串进行匹配。此算法通常称为NFA,但此术语可能会令人困惑。它的运行时间可以是指数级的,当与包含交替和无界量化的表达式匹配时,简单的实现表现出来,并迫使算法考虑指数增加的子案例数。此行为可能会导致称为正则表达式拒绝服务的安全问题。 (a|aa)*b
虽然回溯实现仅在最坏的情况下给出指数保证,但它们提供了更大的灵活性和表现力。例如,任何允许使用反向引用或实现Perl引入的各种扩展的实现都必须包含某种回溯。一些实现[ 哪些?]尝试通过首先运行快速DFA算法来提供最好的两个算法的,并恢复到仅当匹配过程中遇到一个反向引用潜在较慢回溯算法。
Unicode [ 编辑]
在理论上,任何令牌集都可以由正则表达式匹配,只要它是预定义的。在历史实现方面,尽管正则表达式库支持许多其他字符集,但最初编写的正则表达式使用ASCII字符作为其令牌集。许多现代正则表达式引擎至少提供一些Unicode支持。在大多数方面,字符集的含义没有区别,但在扩展正则表达式以支持Unicode时会出现一些问题。
- 支持的编码。一些正则表达式库期望在某些特定编码上工作,而不是在抽象Unicode字符上工作。其中许多需要UTF-8编码,而其他人可能需要UTF-16或UTF-32。相比之下,Perl和Java在编码上是不可知的,而是在内部对解码的字符进行操作。
- 支持的Unicode范围。许多正则表达式引擎仅支持基本多语言平面,即只能用16位编码的字符。目前(截至2016年)只有少数正则表达式引擎(例如,Perl和Java)可以处理完整的21位Unicode范围。
- 将面向ASCII的构造扩展为Unicode。例如,在基于ASCII的实现方式中,表格的字符范围
[x-y]
是有效的任何地方X和ÿ有代码点在范围[0x00,0x7F]和码点(X)≤码点(Ý)。将此类字符范围自然扩展为Unicode只会将端点位于[0x00,0x7F]的要求更改为它们位于[0x0000,0x10FFFF]的要求。但是,实际上通常情况并非如此。一些实现,例如gawk的实现,不允许字符范围跨越Unicode块。类似[0x61,0x7F]的范围是有效的,因为两个端点都在Basic Latin块内,因为[0x0530,0x0560]因为两个端点都在亚美尼亚块内,但是像[0x0061,0x0532]这样的范围是无效的,因为它包括多个Unicode块。其他引擎(例如Vim编辑器的引擎)允许块交叉,但字符值不得超过256。[36] - 不区分大小写。一些不区分大小写的标志仅影响ASCII字符。其他标志会影响所有字符。有些引擎有两个不同的标志,一个用于ASCII,另一个用于Unicode。究竟属于POSIX类的字符也各不相同。
- 堂兄弟的案件不敏感。由于ASCII具有区分大小写,因此不区分大小写成为文本搜索中的逻辑特征。Unicode引入了字母脚本,没有像Devanagari这样的案例。对于这些,区分大小写不适用。对于像中文这样的脚本,另一个区别似乎是合乎逻辑的:在传统和简 在阿拉伯文字中,可能需要对初始,内侧,最终和孤立位置不敏感。在日语中,平假名和片假名之间的不敏感有时是有用的。
- 规范化。Unicode具有组合字符。与旧打字机一样,普通字母后面可以跟一个或多个非间距符号(通常是重音符号,如重音符号)以形成单个打印字符,但也提供预组合字符,即已包含一个或多个组合字符的字符。字符+组合字符的序列应与相同的单个预组合字符匹配。标准化字符序列+组合字符的过程称为标准化。
- 新的控制代码。Unicode引入了其他,字节顺序标记和文本方向标记。这些代码可能必须以特殊方式处理。
- 引入Unicode块,脚本和许多其他字符属性的字符类。块属性远不如脚本属性有用,因为块可以包含来自多个不同脚本的代码点,而脚本可以具有来自多个不同块的代码点。[37]在Perl和
java.util.regex
库中,块X和/ 中的表单\p{InX}
或\p{Block=X}
匹配字符的属性匹配不在该块中的代码点。同样,,,或在亚美尼亚文字的任何字符匹配。通常,将任何字符与二进制属性X或常规类别X匹配\P{InX}
\P{Block=X}
\p{Armenian}
\p{IsArmenian}
\p{Script=Armenian}
\p{X}
。例如\p{Lu}
,\p{Uppercase_Letter}
或者\p{GC=Lu}
匹配任何大写字母。二进制性质是不一般的类别包括\p{White_Space}
,\p{Alphabetic}
,\p{Math}
,和\p{Dash}
。非二进制性质的实例是\p{Bidi_Class=Right_to_Left}
,\p{Word_Break=A_Letter}
,和\p{Numeric_Value=10}
。
使用[ 编辑]
正则表达式在各种文本处理任务中很有用,更常见的是字符串处理,其中数据不必是文本的。常见的应用程序包括数据验证,数据抓取(尤其是网页抓取),数据争论,简单解析,语法高亮系统的制作以及许多其他任务。
虽然正则表达式在Internet 搜索引擎上很有用,但是根据正则表达式的复杂性和设计,在整个数据库中处理它们可能会消耗过多的计算机资源。虽然在许多情况下系统管理员可以在内部运行基于正则表达式的查询,但大多数搜索引擎都不向公众提供正则表达式支持。值得注意的例外:Google代码搜索,Exalead。截至2012年1月,Google代码搜索已关闭。[38] 它使用了一个trigram索引来加速查询。[39]
示例[ 编辑]
具体的语法规则取决于具体的实现,编程语言或正在使用的库。此外,正则表达式实现的功能可能因版本而异。
因为正则表达式很难在没有示例的情况下解释和理解,所以用于测试正则表达式的交互式网站是通过实验学习正则表达式的有用资源。本节通过说明的方式提供了正则表达式的一些属性的基本描述。
示例中使用以下约定。[40]
元字符;; metacharacters列指定正在演示的正则表达式语法 = ~m // ;; 表示Perl中的正则表达式匹配操作 = ~s /// ;; 表示Perl中的正则表达式替换操作
另外值得注意的是,这些正则表达式都是类似Perl的语法。标准POSIX正则表达式是不同的。
除非另有说明,否则以下示例符合Perl编程语言,版本5.8.8,2006年1月31日。这意味着其他实现可能缺乏对此处所示语法的某些部分的支持(例如,基本与扩展正则表达式,\( \)
对比()
,或缺乏\d
而不是POSIX[:digit:]
)。
这些示例中使用的语法和约定也与其他编程环境的语法和约定一致。[41]
元 字符 | 描述 | 例子[42] |
---|---|---|
. | 通常匹配除换行符之外的任何字符。 在方括号内,点是字面的。 | $ string1 = “Hello World \ n” ; 如果 ($字符串1 =〜 米/...../ ) { 打印 “$字符串1具有长度> = 5。\ n”个; } 输出: 你好,世界 长度> = 5。 |
( ) | 将一系列模式元素分组到单个元素。 当你匹配括号内的模式,你可以使用任何的 $1 ,$2 ......后来指先前匹配的模式。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m /(H ..)。(o ..)/ ) { print “我们匹配'$ 1'和'$ 2'。\ n” ; } 输出: 我们匹配'Hel'和'o W'。 |
+ | 匹配前面的模式元素一次或多次。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / l + / ) { print “ $ string1中有一个或多个连续的字母\”l \“。\ n” ; } 输出: Hello World中有一个或多个连续的字母“l”。 |
? | 匹配前面的模式元素零次或一次。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / H。?e / ) { print “有'H'和'e'分隔” ; 打印 “0-1个字符(例如,He Hue Hee)。\ n” ; } 输出: 有一个'H'和'e'以0-1个字符分隔(例如,He Hue Hee)。 |
? | 修改* ,+ ,? 或者{M,N} “d自带之前的几次地匹配正则表达式。 | $ string1 = “Hello World \ n” ; 如果 ($字符串1 =〜 米/(1 + O)/? ) { 打印 “的非贪婪匹配'L'后面跟着一个或\ n”个; 打印 “更多的字符是'llo'而不是'llo Wo'。\ n” ; } 输出: 与'l'的非贪婪匹配后跟一个或 更多的人物是'llo'而不是'llo Wo'。 |
* | 匹配前面的模式元素零次或多次。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / el * o / ) { print “有一个'e'后跟零到多个” ; 打印 “'l'后跟'o'(例如,eo,elo,ello,elllo)。\ n” ; } 输出: 有一个'e'后跟零到多'l'后跟'o'(例如,eo,elo,ello,elllo)。 |
{M,N} | 表示最小M和最大N匹配计数。 N可以省略,M可以是0: {M} 匹配“完全”M次; {M,} 匹配“至少”M次; {0,N} 匹配“最多”N次。x* y+ z? 因此相当于x{0,} y{1,} z{0,1} 。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / l {1,2} / ) { print “存在一个至少为1的子字符串” ; 打印 “和$ string1 \ n中最多2 l” ; } 输出: 在Hello World中存在至少1和最多2 l的子串 |
[…] | 表示一组可能的角色匹配。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / [aeiou] + / ) { print “$ string1包含一个或多个元音。\ n” ; } 输出: 你好,世界 包含一个或多个元音。 |
| | 分开其他可能性。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m /(Hello | Hi | Pogo)/ ) { print “$ string1至少包含Hello,Hi或Pogo中的一个。” ; } 输出: 你好,世界 包含Hello,Hi或Pogo中的至少一个。 |
\b | 匹配单词类字符(参见下一个)和非单词类字符或边缘之间的零宽度边界; 与...一样
| $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / llo \ b / ) { print “有一个单词以'llo'结尾。\ n” ; } 输出: 有一个单词以'llo'结尾。 |
\w | 匹配字母数字字符,包括“_”; 与 [A-Za-z0-9_] ASCII 相同,和
在Unicode中,[37]其中 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / \ w / ) { print “至少有一个字母数字” ; 打印 $ string1中的字符(AZ,az,0-9,_)。\ n“ ; } 输出: Hello World中至少有一个字母数字字符 (AZ,az,0-9,_)。 |
\W | 匹配非字母数字字符,不包括“_”; 与 [^A-Za-z0-9_] ASCII 相同,和
在Unicode中。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / \ W / ) { print “Hello和”之间的空格; 打印 “世界不是字母数字。\ n” ; } 输出: Hello和World之间的空格不是字母数字。 |
\s | 匹配空格字符, ASCII格式为制表符,换行符,换页符,回车符和空格; 在Unicode中,还匹配无中断空格,下一行和可变宽度空间(以及其他)。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / \ s。* \ s / ) { print “在$ string1中有两个空白字符,可能是” ; 打印 “由其他字符分隔。\ n” ; } 输出: 在Hello World中 有两个空格字符,可以用其他字符分隔。 |
\S | 匹配任何东西但是空白。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / \ S。* \ S / ) { print “在$ string1中有两个非空白字符,” ; print “可能被其他字符分隔。\ n” ; } 输出: 在Hello World中 有两个非空白字符,可以用其他字符分隔。 |
\d | 匹配一个数字; 与 [0-9] ASCII 相同; 在Unicode中,与 \p{Digit} or \p{GC=Decimal_Number} 属性相同,它本身与\p{Numeric_Type=Decimal} 属性相同。 | $ string1 = “墙上挂着99瓶啤酒。” ; if ($ string1 = ~ m /(\ d +)/ ) { print “$ 1是'$ string1'中的第一个数字\ n” ; } 输出: 99年是“99瓶啤酒在墙上的第一个数字”。 |
\D | 匹配非数字; 与 [^0-9] ASCII或\P{Digit} Unicode相同。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / \ D / ) { print “$ string1中至少有一个字符” ; 打印 “这不是数字。\ n” ; } 输出: Hello World中至少有一个字符 这不是一个数字。 |
^ | 匹配行或字符串的开头。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / ^ He / ) { print “$ string1以字符'He'开头。\ n” ; } 输出: 你好,世界 从字符'他'开始。 |
$ | 匹配行或字符串的结尾。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / rld $ / ) { print “$ string1是一个行或字符串” ; 打印 “以'rld'结尾。\ n” ; } 输出: 你好,世界 是以'rld'结尾的行或字符串。 |
\A | 匹配字符串的开头(但不是内部行)。 | $ string1 = “Hello \ nWorld \ n” ; if ($ string1 = ~ m / \ AH / ) { print “$ string1是一个字符串” ; 打印 “以'H'开头。\ n” ; } 输出: 你好 世界 是一个以'H'开头的字符串。 |
\z | 匹配字符串的结尾(但不是内部行)。[43] | $ string1 = “Hello \ nWorld \ n” ; if ($ string1 = ~ m / d \ n \ z / ) { print “$ string1是一个字符串” ; 打印 “以'd \\ n'结尾。\ n” ; } 输出: 你好 世界 是一个以'd \ n'结尾的字符串。 |
[^…] | 匹配除括号内的每个字符。 | $ string1 = “Hello World \ n” ; if ($ string1 = ~ m / [^ abc] / ) { print “$ string1包含除”以外的字符“ ; 打印 “a,b和c。\ n” ; } 输出: 你好,世界 包含a,b和c以外的字符。 |