`
足至迹留
  • 浏览: 495832 次
  • 性别: Icon_minigender_1
  • 来自: OnePiece
社区版块
存档分类
最新评论

<基础-1> 正则表达式:元字符

阅读更多
主要参考:《精通正则表达式 第三版》 jeffrey E.F Friedl 著

本系列并不是专门针对java语言介绍正则表达式使用的,而是从正则表达式本身的范畴介绍正则的基础,原理,效率,最后是某语言(java)中如何引入使用正则特性。相信看完后会对正则有个不一样的全面认识。当然,理论掌握后还需多写才能化为己有。

正则表达式(Regular Expression)是强大,便捷,高效的文本处理工具。正则表达式本身,加上如同一门袖珍编程语言的通用模式表示法(general pattern notation), 赋予使用者描述和分析文本的能力。配合上特定工具提供的额外支持,正则表达式能够添加,删除,分离,叠加,插入和修正各种类型的文本和数据。

完整的正则表达式由两种字符构成。特殊字符称为“元字符(meta characters)”,其他为“文字(literal)”或者普通文本字符(normal text characters)

1.1 元字符
1.1.1 行的起始和结束
或许最容易理解的元字符就是脱字符号“^”和美元符号“$”了。在检查一行文本时,脱字符号代表一行的开始,美元符号代表一行的结束。后面讲到正则的流派,模式时我们会认识到,"^"可能还会匹配换行符。

我们最好能养成按照字符来理解正则表达式的习惯。如“^cat”不要理解成:以cat开头的行。而是理解成:以c作为一行的第一个字符,紧接着一个a,然后是一个t的文本。这两种理解的结果并无差异,但按照字符来解读更易于明白新遇到的正则表达式的内部逻辑。
脱字符号和美元符号的特别之处在于,他们匹配的是一个位置,而不是具体的字符。也就是说他们不占用任何位置。
“^$”匹配的是什么呢?其实就是行开头,接着就是行末尾。应用意义就是匹配一个空行,没有任何字符,包括空白字符。

1.1.2 字符组
匹配若干字符之一。如果我们需要搜索的是单词“grey”,同时又不确定它是否协作“gray”,就可以使用正则表达式结构体“[…]”.它容许使用者列出在某处期望匹配的字符,通常被称作字符组(character class)。所以上面描述的不确定情况就可以用”gr[ea]y”,意思就是先找到g,跟着是一个r,然后是一个a或e,最后是一个y。

注意:在字符组以外,普通字符(如gr[ae]y中的g和r)都有接下来是(and then)的意思,这与字符组内部的情况是完全相反的。字符组的内容是在同一个位置能够匹配的若干字符,所以他的意思是“或”。如“[0123456789]”就是匹配1到9之间的任意一个数字。如果“<H[1234]>”就是用来匹配<H1>,<H2>,<H3>,<H4>。

在字符组内部有字符组元字符(character-clas metacharacter):“-”(连字符)表示一个范围:“<H[1234]>”与“<H[1-4]>”是完全一样的。[0-9]和[a-z]是常用的匹配数字和小写字母的简便方式。多重范围也是允许的,如“[0123456789abcdeABCDE]“可以写作”[0-9a-eA-E]“,注意只有在字符组内部,连字符才是元字符。否则他就是只能匹配普通的连字符号。连字符还有一个例外,即使在字符组内部,如果连字符出现在字符组的开头(或排除型字符组的开头,下面会讲,如[^-]),那也不是元字符而只是一个普通字符。同样的道理,问号和点号(后面会讲到这两个元字符)通常被当做元字符处理,但在字符组里则不是如此。只有连字符和^(下面马上讲到,是排除字符)才可能是字符组里的元字符(但是转义符,字符组简记法仍然有效,即[\da-zA-Z]或[^\da-zA-Z]或[\\"]中的\仍然有转义的含义,\d仍然代表数字,\\"仍然代表字符串中的“\"”单独的\不会匹配任何字符,\\会匹配字符'\')。相当于字符组内部有一个自己独立的小世界,有自己的规则。

1.1.3 排除型字符组
用“[^…]”取代“[…]”,这个字符组就会匹配任何未列出的字符。例如”[^1-8]”匹配除了1到8以外的任何字符。注意:这句话有两层含义,一个是排除1-8字符另一个是一定要匹配一个字符
例子:匹配q后面不是u的字符,正则是”q[^u]”。那么qi会被匹配。那Qantas呢?Iraq呢?答案是这两个都不会被匹配,一个是因为Q是大写,一个是因为q在最后,后面没有除了u以外的任何字符。再次注意,排除型字符组也是要匹配一个字符的。

1.1.4 用点号匹配任意字符
元字符“.”(通常称为点号dot或者小点point)是用来匹配任意字符的字符组的简便写法(在有些工具中,不能匹配换行符)。例如,我们需要搜索03/19/76或03-19-76或03.19.76,不怕麻烦的话可以用一个明确容许“/”、“-”或“.”的子负责构建正则表达式,如“03[-./]19[-./]76”也可以简单的尝试“03.19.76”,当然后者没有前者那么精确。这里仍然有需要注意的地方,点号在字符组里[-./]并不是元字符,同样在第一位置的连字符也不是元字符。如果连字符不在字符组的开头,如[.-/]就是用来表示范围的,但在这里是个错误的范围用法。

1.1.5 多选结构
匹配任意子表达式。“|”是一个非常简洁的元字符,它的意思是“或(or)”。依靠它,我们能够把不同的子表达式组合成一个总的表达式,而这个总的表达式又能匹配任意的子表达式。假如“Bob”和“Robert”是两个表达式,但“Bob|Robert”就是能够同时匹配其中任意一个的正则表达式。在这样的组合中,子表达式称为“多选分支(alternative)”。

回头来看上面的“gr[ea]y”例子,有意思的是,它还可以写作“grey|gray”或者“gr(a|e)y”。后者用括号来划定多选结构的范围(正常情况下,括号也是元字符)。请注意,“gr[a|e]y”不符合我们的需求,在这里,“|”只是一个和“a”与“e”一样的普通字符。刚说过,字符组内部|不是元字符。

“gr[ea]y”与“gr(a|e)y”的例子可能会让人觉得多选结构与字符组没太大的区别,但是不要混淆这两个概念。一个字符组只能匹配目标文本中的单个字符,而每个多选结构自身可能是完整的正则表达式,都可以匹配任意长度的文本。

字符组基本可以算是一门独立的微型语言(例如,对于元字符它们有自己的规定),而多选结构是“正则表达式语言主体(main regular expression language)”的一部分。字符组中的元字符需要注意,同样,在一个包含多选结构的表达式中使用脱字符和美元符的时候也要小心。比较”^From|Subject|Date:*”和”^(From|Subject|Date):*”会发现匹配结果大不相同。第一个表达式由3个多选分支构成,能匹配”^From”或”Subject”或”Date:*”,实用性不大。我们希望在每一个多选分支前都有脱字符,之后都有”:*”,所以应该使用括号来限制这些多选分支:
”^(From|Subject|Date):*”,含义是匹配以“From:*”或“Subject:*”或“Date:*”开头的文本行。

1.1.6 忽略大小写
我们经常遇到不区分大小写(case-insensitive)的匹配的概念。E-mail header中的字段类型通常是大写字母开头,但又没有严格规定,就可以使用“[Fr][Rr][Oo][Mm]”来匹配任意大小写的“from”形式,缺点就是不方便。许多工具软件提供忽略大小写的参数,比如egrep的命令行参数“-i”表示忽略大小写。如:% egrep –i ‘^(From|Subject|Date): ’ mailbox

1.1.7 单词分界符
使用正则表达式时经常会遇到的一个问题,期望匹配的“单词(其实是字符组合)”包含在另一个单词之中,但这个包含的单词不是我们希望的结果。某些版本的egrep对单词识别提供了有限的支持:也就是单词分解符(单词开头和结束的位置)的匹配。

如果你的egerp支持“元字符序列(meta sequences)””\<”和”\>”,就可以使用它们来匹配单词分解的位置。可以把它们想象为单词版本的”^”和”$”,分别用来匹配单词的开头和结束位置。就像作为行锚点的脱字符和美元符一样,它们锚定了正则表达式的其他部分,但在匹配过程中并不对应到任何字符。表达式“\<cat\>”表示“匹配单词的开头位置,然后是cat三个字母,然后是单词的结束位置”,也就是“匹配cat这个单词”。
后面我们还会看到匹配单词边界的”\b”元字符序列。需要说明的是,这里拿egrep举例,也并不是所有的egrep都支持这样的元字符序列。这里讲述的“正则表达式”并不是特别针对java的,而是针对整个正则的系统,后面我们会看到正则有不同的流派,java只是使用了正则的一个子集而已。

总结一下上面,我们遇到的元字符有:




另外还有几点需要注意:
1) 在字符组内部,元字符的定义规则是不一样的。
2) 不要混淆多选项和字符组。无论列出的字符有多少,字符组只能匹配一个字符,而多选项可以匹配任意长度的文本。
3) 排除型字符组是表示所有未列出的字符组的简便方法。“[^x]”表示匹配一个不等于x的字符,并不是“只有当这个位置不是x时才匹配”。也就是排除型字符组仍然是要匹配一个字符的。
4) -i 参数规定在匹配时不区分大小写。注意,这是在某些正则工具软件中。


下面将介绍更重要的“可选项(optional)”和”计数(counting)”.

1.2 可选项元素(?)
现在来看color和colour的匹配。他们的区别在于,后面的单词比前面的多一个u,我们可以用“colou?r”来同时匹配这两个单词。元字符“?”代表可选项。把它加在一个字符的后面,就表示此处容许出现这个字符,不过它的出现并非匹配成功的必要条件。

“u?”这个元字符与我们之前看到的元字符都不相同,它只作用于之前紧邻的元素。我们可以把“4th|4”简化为“4(th)?”。我们看到“?”作用的元素是整个括号了。括号内的表达式可以任意复杂,但是“从括号外来看”他们时个整体。界定“?”的作用对象是括号的主要用途之一。

1.3 其他量词:重复出现
1.3.1 星号,加号
“+(加号)”和”*(星号)”的作用与问号类似,也都是元字符。元字符”+”表示“之前紧邻的元素出现一次或多次”;而元字符”*”表示“之前紧邻的元素出现任意多次,或者不出现”。换种说法就是”*”表示匹配尽可能多的次数,但如果一次都不匹配也没关系。”+”表示匹配尽可能多的次数,但如果一次都不匹配就报告失败。问号,加号和星号这三个元字符统称为量词。后面会再讲到,这是匹配优先的量词,会尽可能多的匹配结果。他们还有忽略优先,占有优先的形式,后面会说。



1.3.2 规定重现次数的范围:区间
某些版本的egrep能够使用元字符序列来自定义重现次数的区间:“{min, max}”。其中min为空”{,max}”表示没有下限,max为空”{min,}”表示没有上限。这称为“区间量词”。例如”a{3,12}”表示能够容许a出现3到12次。
问号对应“{0,1}”;加号对应”{1,}”; 星号对应”{0,}”

1.4 括号及反向引用
目前为止,我们已经见过括号的两种用途:(1)限制多选项的范围,如“gr(a|e)y”;(2)将若干字符组合为一个单元,受问号或星号之类量词的作用,如:”(cat|dog)+”。
现在再介绍括号的另一种用途,虽然他在egrep中并不常见,不过流行的GNU版本确实支持这一功能,在其他工具软件中很常见。
在许多流派(flavor)的正则表达式中,括号能“记住”他们包含的子表达式匹配的文本。在解决单词重复问题时就会用到这个功能。如果我们确切知道重复单词的第一个单词(比方说这个单词就是”the”),就能够明确无误地找到它。例如“the boy and the girl”,同样也会匹配到”theory”,显然后者不是我们希望的。但如果工具支持单词分界符(1.1.7节介绍)问题就比较容易解决。

然而,穷举所有可能出现的重复单词显然是不可能完成的任务。如果我们先匹配任意一个单词,接下来检查“后面的单词是否与它一样”就好办多了。如果你的egrep(多次提到该工具,只是一个例子而已)支持”反向引用”就可以这么做。反向引用时正则表达式的特性之一,它容许我们匹配与表达式先前部分匹配的同样的文本。
接下来我们就先把“\<the +the\>”(这里的加号限制的是前面的空格)中的第一个“the”替换为能够匹配任意单词的正则表达式“[A-Za-z]+”,然后在两端加上括号;最后把后一个‘the’替换为特殊的元字符序列“\1”,就得到了“\<([A-Za-z]+) +\1\>”(后面的加号前有一个空格)。在支持反向引用的工具软件中,括号能够记忆其中的子表达式匹配的文本,不论这些文本是什么,元字符序列“\1”都能记住他们。
当然,在一个表达式中我们可以使用多个括号。再用“\1”,“\2”,“\3”等来表示第一,第二,第三组括号匹配的文本。括号是按照开括号‘(’从左到右出现顺序进行的,所以“([a-z])([0-9])\1\2”中的“\1”代表“[a-z]”匹配的内容,而“\2”代表”[0-9]”匹配的内容。

在“the the”的例子中,“[A-Za-z]+”匹配第一个“the”.因为这个子表达式在括号中,所以“\1”代表的文本就是”the”。如果“ +”(加号前有一个空格)能够匹配,后面的“\1”要匹配的文本就是“the ”。如果“\1”也能匹配成功,最后的“\>”对应单词的结尾(如果文本是“the theft”,这一条就不满足)。如果整个表达式匹配成功,我们就得到一个重复单词。同时,还可以结合前面提到的“-i”选项忽略大小写。

例如,在支持反向引用的egrep中就可以用:
% egrep –i ‘\<([a-z]+) +\1\>’ test.txt
可以找到所有的重复单词。

尽管这个表达式很有用,但仍然需要重视他的局限。egrep把每行文字都当做一个独立部分来看待,所以如果单词重复的第一个单词在某行末尾,第二个单词在下一行的开头,这个表达式就无法找到。但,后面还有更灵活的工具可以实现。

1.5 神奇的转义
正则表达式里还有一个重要的问题,如果需要匹配的某个字符本身是元字符该怎么处理呢?例如,要检索互联网的主机名ega.att.com,其中的点号本身就是元字符,他可以匹配任何字符,包括空格。
真正匹配文本中点号的元序列应该是反斜线(backslash)加上点号的组合:“ega\.att\.com”。“\.”称为“转义的点号”。这样的办法适用于所有的元字符,不过在字符组内无效。
这样使用的反斜线称为“转义符”。它作用的元字符会失去特殊含义,成了普通字符。我们还可以用“\([a-zA-z]+\)”来匹配一个括号内的单词,例如“(very)”。在开括号之前的反斜线消除了开闭括号的特殊意义,于是他们能够匹配文本中的开闭括号。

1.6 基础知识扩展

1.6.1 语言的差异
前面介绍的是大多数版本的egrep支持的正则表达式的特性,这样的特性还有许多,其中一些并不是所有的版本都支持。任何语言中都存在不同的方言和口音,很不幸,正则表达式也一样。情况似乎是每一种支持正则表达式的语言都提供了自己的“改进”。正则表达式不断发展,但多年的变化也造就了数目众多的流派(flavor)。

1.6.2 正则表达式的目标
从宏观的角度看,一个正则表达式要么能匹配给定的文本(对egrep来说就是一行文本)中的某些字符,要么不能匹配。在编写正则表达式时,我们必须权衡:(1)匹配符合要求的文本(2)同时忽略不符合要求的文本。

1.6.3 更多例子
在任何语言中,经验都是非常重要的,下面我们就研究几个实例。
编写正则表达式时,按照预期获取成功的匹配要花去一半的功夫,另一半的功夫用来考虑如何忽略那些不符合要求的文本,这两方面都很重要。

1.6.3.1 变量名
许多程序设计语言都有标识符(identifier)的概念,标识符只包含字母数字以及下划线,但不能以数字开头。我们可以用“[a-zA-Z_][a-zA-Z_0-9]*”来匹配标识符。如果最长只能有32个字符,可以写作:“[a-zA-Z_][a-zA-Z_0-9]{0,31}”。

1.6.3.2 引号内的字符串
匹配引号内的字符串最简单的办法是使用: "[^"]*"

1.6.3.3 美元金额(可能包含小数)
“\$[0-9]+(\.[0-9][0-9])?”是一种匹配美元的办法。大致分为三部分,“\$”匹配美元符号,然后是一组数字“[0-9]+”,最后是可能出现的两位小数部分“(\.[0-9][0-9])?”。
从几个方面来看,这个表达式还很简陋。比如只能接受$1000不能接受$1,000,无法匹配$.49。
后面我们还会继续讨论这个例子。先记住这回事。

1.6.3.4 表示时刻的文字,如“9:17 am”或“12:30 pm”
匹配表示时刻的文字可能有不同的严格程度。
“[0-9]?[0-9]:[0-9][0-9] (am|pm)”能够匹配9:17 am但也能匹配无意义的时刻,如99:99 pm。
改进下上面的正则表达式,先考虑小时,可能是两位数也可能是一位数,两位数时第一个只可能是[012]再考虑第二位数,分钟一样的分开考虑,就有“(1[012]|[1-9]):[0-5][0-9] (am|pm)”。

1.7 总结



此外,需要理解以下几点:
1)各个egrep程序是由差别的。他们支持的元字符,以及这些字符的确切含义,通常有差别。
2)使用括号有3个理由:限制多选结构,分组和捕获文本。
3)字符组的特殊性在于,关于元字符的规定是完全独立于正则表达式语言主体的。
4)多选结构和字符组是截然不同的,多选可以有多个,字符组只能匹配一个字符。
5)排除型字符组同样是一种“肯定断言”,仍然需要匹配一个字符。
6)-i参数很有用,能忽略大小写的匹配。
7)转义有3种情况:
(7.1)“\”加上元字符,表示匹配元字符所使用的普通字符。
(7.2)”\”加上非元字符,组成一种由具体实现方式规定其意义的元字符序列,如“\<”表示单词的起始边界。
(7.3)“\”加上任意其他字符,默认情况是匹配此字符,也就是说,反斜线被忽略了。

8)由星号和问号限定的对象在“匹配成功”时可能并没有匹配任何字符。

  • 大小: 38.6 KB
  • 大小: 66.1 KB
  • 大小: 201.3 KB
0
4
分享到:
评论

相关推荐

    BGP_AS-PATH的正则表达式

    ip as-path access-list &lt;编号&gt; permit|deny &lt;正则表达式&gt; ``` 其中,`permit`允许满足条件的路由通过,`deny`则拒绝。例如: ```sh ip as-path access-list 1 deny _300_ ip as-path access-list 1 permit .* ```...

    正则表达式不包涵特殊字符(除了 指定字符 以外)

    正则表达式的语法由一系列用于描述字符串模式的字符和元字符组成。 ### 需求分析 根据题目描述,我们需要构建一个正则表达式来匹配一段字符串,该字符串除了允许出现特定字符(例如下划线 `_`)之外,不允许包含...

    使用正则表达式拆分字符串

    1. 正则表达式基础 - **模式匹配**:正则表达式由一系列字符和特殊符号组成,用于定义一个模式,该模式可以匹配特定的字符串。 - **元字符**:如`*`, `+`, `?`, `{}`, `[]`, `\`等,它们具有特殊的含义,用于指定...

    掌控正则表达式:第三版

    - **正则表达式的基础**:介绍正则表达式的概念、基本语法和常见元字符,帮助读者快速入门。 - **正则表达式引擎详解**:深入讲解正则表达式引擎的工作机制,包括模式匹配算法和内部实现细节。 - **性能优化技巧**:...

    Indesign_GREP正则表达式

    GREP正则表达式的元字符集 以下列出了一些常用的元字符及其含义: - `^`:锚定行的开始。 - `$`:锚定行的结束。 - `.`:匹配一个非换行符的字符。 - `*`:匹配零个或多个先前字符。 - `[]`:匹配一个指定范围内...

    正则表达式文法(BNF)

    - 包括`&lt;star&gt;`(星号运算)、`&lt;plus&gt;`(加号运算)和`&lt;elementary-RE&gt;`(元素正则表达式)。 ##### 2.6 `&lt;star&gt;` - 星号运算 ``` &lt;star&gt;::=&lt;elementary-RE&gt;"*" ``` **解释:** - `&lt;star&gt;`定义了星号运算,表示...

    php常用正则表达式

    **正则表达式:** `&lt;(.*)&gt;.*&lt;\/\1&gt;|&lt;(.*)\/&gt;` - **用途:** 匹配HTML标签。 - **示例:** "&lt;div&gt;&lt;/div&gt;", "&lt;span/&gt;" --- **正则表达式:** `(^\s*)|(\s*$)` - **用途:** 匹配字符串前后的空白字符。 - **示例:...

    正则表达式 到 NFA

    这是编译原理的一个实验, 是把一个正则表达式转化为不确定有穷自动机NFA的算法程序,朋兴趣的朋友可以下载来看看哦.&lt;br&gt; 一个正则表达式就是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式...

    文本处理工具三剑客之---grep (正则表达式引擎,正则表达式元字符).md

    - **基本正则表达式(Basic Regular Expressions, BRE)**:提供了一套基础的模式匹配功能,适用于大多数简单的应用场景。 - **扩展正则表达式(Extended Regular Expressions, ERE)**:提供了更多的高级功能,如`|...

    正则表达式大全.docx

    以上给出的正则表达式仅覆盖了一些基础用例,实际的正则表达式可以变得极其复杂,涉及更多的元字符和修饰符,如贪婪与非贪婪匹配、分组、零宽断言等。正则表达式的学习和掌握需要时间和实践,但一旦熟悉,它们将成为...

    EXCEL-VBA-正则表达式-从实例开始.docx

    基础的正则表达式元字符包括 `.`(任意字符)、`*`(零次或多次)、`+`(一次或多次)、`?`(零次或一次)、`^`(开始)、`$`(结束)、`|`(或)等。要深入学习正则表达式,可以参考所提供的教程链接。 2. VBA中的...

    Go-POSIX基本正则表达式伪随机字符串生成器

    POSIX基本正则表达式包括一系列的元字符和操作符,如`.`代表任意字符,`*`表示前面的元素可以出现零次或多次,`+`表示前面的元素至少出现一次,`?`表示前面的元素可以出现零次或一次。这些元字符和操作符可以组合成...

    java/Android 正则表达式详解

    句点符号“.”是正则表达式中最基本的元字符之一,它匹配所有字符,包括空格、Tab 字符甚至换行符。 方括号符号 --------- 方括号符号“[]”用于指定看来有意义的字符,只有方括号里面指定的字符才参与匹配。 ...

    正则表达式转换工具

    1. **正则表达式基本概念** - 元字符:如`.`, `*`, `+`, `?`, `{}`, `[]`, `\`, `^`, `$`等,它们具有特殊含义,在正则表达式中用来定义模式。 - 字符类:例如`[abc]`,匹配其中任意一个字符。 - 量词:`*`, `+`,...

    正则表达式测试工具C#版(src)

    1. **C#中的正则表达式基础** - `System.Text.RegularExpressions`命名空间:C#中的正则表达式操作主要基于这个命名空间下的类和方法。 - `Regex`类:这是处理正则表达式的核心类,提供了多种与正则相关的静态方法...

    Linux常用命令和管理03-grep及正则表达式1

    本文将详细介绍 Linux 中的 grep 命令和正则表达式的使用,涵盖变量类别、Bash 配置文件、计算机理解、编程语言、Bash 编程、文件搜索、grep 命令、正则表达式、基本正则表达式的元字符、次数匹配、位置锚定、分组、...

    正则表达式(一)从初学到精通正则表达式

    本文将对正则表达式的基础知识进行详细的介绍,从什么是正则表达式开始,逐步深入浅出地讲解正则表达式的基本概念、正则表达式引擎、文字符号、特殊字符、不可显示字符、正则表达式引擎的内部工作机制等。...

    Java正则表达式 Java 正则表达式

    Java正则表达式是Java语言中用于处理字符串的强大工具,它允许程序员进行复杂的字符串匹配、查找和替换操作。正则表达式(Regular Expression)是一种模式匹配语言,通过特定的语法来描述字符串的模式,用于在文本中...

    正则表达式 普通字符

    ### 正则表达式基础及应用 #### 一、正则表达式概述 正则表达式是一种用于描述字符串匹配模式的强大工具,广泛应用于文本处理、数据验证和模式匹配等领域。通过构建特定的模式来匹配、查找、替换或提取文本中的...

Global site tag (gtag.js) - Google Analytics