正则表达式(Regular Expressions)
一、简要认识
正则表达式就是用一个“字符串”来描述一个特征,然后去验证另一个“字符串”是否符合这个特征。比如表达式“ab+” 描述的特征是“一个 ‘a’ 和任意个 ‘b’ ”,那么 ‘ab’, ‘abb’, ‘abbbbbbbbbb’ 都符合这个特征。
正则表达式可以用来:(1)验证字符串是否符合指定特征,比如验证是否是合法的邮件地址。(2)用来查找字符串,从一个长的文本中查找符合指定特征的字符串,比查找固定字符串更加灵活方便。(3)用来替换,比普通的替换更强大。
举例
DATA: matcher TYPE REF TO cl_abap_matcher, match TYPE c LENGTH 1. matcher = cl_abap_matcher=>create( pattern = ‘\w+@\w+(\.\w+)+’ text = ‘hubin0809@126.com’ ). match = matcher->match( ). WRITE match. |
输出结果:X |
解释:
1> ‘\w+@\w+(\.\w+)+’中 \w 是表示任意一个字母或数字或下划线,+ 表示前面字符个数为一个或多个,@即为’@’字符
2> matcher参照类cl_abap_matcher,match有匹配的意思,调用静态方法create创建了匹配的对(暂时这么理解,好吧,我承认我不知道怎么形容),然后调用match方法,返回值中’X’表示匹配,SPACE表示不匹配。
具体含义后面会讲到,本程序主要是验证邮件地址是否合法。
二、语法规则
pattern模板,text要匹配的字符,match匹配结果,’X’表示匹配,SPACE表示不匹配。
1、 普通字符
字母、数字、汉字、下划线、以及后面没有特殊定义的标点符号,都是”普通字符”。表达式中的普通字符,在匹配一个字符串的时候,匹配与之相同的一个字符。
Pattern |
Text |
Match |
A |
A |
X |
A |
a |
– |
A |
AB |
– |
AB |
AB |
X |
2、 转义字符
一些不便书写的字符,采用在前面加 “\” 的方法。例如’.’
表达式 |
可匹配 |
\\ |
代表 “\” 本身 |
\. |
匹配小数点(.)本身 |
\Q…\E |
中间的字符作为普通字符 |
Pattern |
Text |
Match |
.\. |
f. |
X |
.\. |
f\f |
– |
\w\d |
\w\d |
– |
\\w\\d |
\w\d |
X |
\Q\w\d\E |
\w\d |
X |
3、 能够与 ‘多种字符‘ 匹配的表达式
正则表达式中的一些表示方法,可以匹配 ‘多种字符’ 其中的任意一个字符。比如,表达式 “\d” 可以匹配任意一个数字。虽然可以匹配其中任意字符,但是只能是一个,不是多个。这就好比玩扑克牌时候,大小王可以代替任意一张牌,但是只能代替一张牌。(没玩过?好吧,去玩qq够级吧,ok,信息泄露了,承认我是山东人)
表达式 |
可匹配 |
\d |
任意一个数字,0~9 中的任意一个 |
\w |
任意一个字母或数字或下划线,也就是 A~Z,a~z,0~9,_ 中任意一个 |
\s |
包括空格、制表符、换页符等空白字符的其中任意一个 |
. |
小数点可以匹配除了换行符(\n)以外的任意一个字符 |
Pattern |
Text |
Match |
\d |
9 |
X |
\d |
25 |
– |
\d\d |
25 |
X |
\w |
A |
X |
\s |
\n |
X |
… |
4zF |
X |
4、 自定义能够与 ‘多种字符‘ 匹配的表达式
使用方括号 [ ] 包含一系列字符,能够匹配其中任意一个字符。用 [^ ] 包含一系列字符,则能够匹配其中字符之外的任意一个字符。同样的道理,虽然可以匹配其中任意一个,但是只能是一个,不是多个。
表达式 |
可匹配 |
[ab5@] |
匹配 “a” 或 “b” 或 “5” 或 “@” |
[^abc] |
匹配 “a”,”b”,”c” 之外的任意一个字符 |
[f-k] |
匹配 “f”~”k” 之间的任意一个字母 |
[^A-F0-3] |
匹配 “A”~”F”,”0″~”3″ 之外的任意一个字符 |
Pattern |
Text |
Match |
[abc] |
a |
X |
[abc] |
abc |
– |
[^abc]b |
cb |
– |
[a-g]b |
cb |
X |
5、 支持的 POSIX 字符集合
POSIX 字符集合 |
可匹配 |
[:alnum:] |
任何一个字母或数字(A – Z, a – z, 0 – 9) |
[:alpha:] |
任何一个字母(A – Z, a – z) |
[:cntrl:] |
任何一个控制字符(\x00 – \x1F, \x7F) |
[:digit:] |
任何一个数字(0 – 9) |
[:space:] |
任何一个空白字符(\x09 – \x0D, \x20) |
[:graph:] |
任何一个可显示的 ASCII 字符,不包含空格 |
[:lower:] |
任何一个小写字母(a – z) |
[:upper:] |
任何一个大写字母(A – Z) |
[:punct:] |
可显示字符 [:print:] 中除去字母数字 [:alnum:] |
[:blank:] |
空格或者制表符(\x20, \x09) |
个人感觉意义不大,可能对一些控制字符有用吧,了解。
Pattern |
Text |
Match |
[[:alnum:]] |
a |
X |
[:lower:][:digit:] |
a9 |
X |
[[:lower:][:digit:]] |
a9 |
– |
[[:lower:][:digit:]] |
b |
X |
6、 修饰匹配次数的特殊符号
前面讲到的表达式,无论是只能匹配一种字符的表达式,还是可以匹配多种字符其中任意一个的表达式,都只能匹配一次。如果使用表达式再加上修饰匹配次数的特殊符号,那么不用重复书写表达式就可以重复匹配,否则会累死的。
表达式 |
作用 |
{n} |
表达式重复n次,比如:”\w{2}” 相当于 “\w\w”;”a{5}” 相当于 “aaaaa” |
{m,n} |
表达式至少重复m次,最多重复n次,比如:”ba{1,3}”可以匹配 “ba”或”baa”或”baaa” |
{m,} |
表达式至少重复m次,比如:”\w\d{2,}”可以匹配 “a12″,”_456”,”M12344″… |
? |
匹配表达式0次或者1次,相当于 {0,1},比如:”a[cd]?”可以匹配 “a”,”ac”,”ad” |
+ |
表达式至少出现1次,相当于 {1,},比如:”a+b”可以匹配 “ab”,”aab”,”aaab”… |
* |
表达式不出现或出现任意次,相当于 {0,},比如:”*b”可以匹配 “b”,”cccb”… |
Pattern |
Text |
Match |
[abc]{3} |
bca |
X |
.{3,5} |
abcd |
X |
\d{5,} |
12345 |
X |
a*b |
b |
X |
a+b |
b |
– |
7、 其他一些代表抽象意义的特殊符号
表达式 |
作用 |
^ |
与字符串开始的地方匹配,不匹配任何字符 |
$ |
与字符串结束的地方匹配,不匹配任何字符 |
\b |
匹配一个单词边界,也就是单词和空格之间的位置,不匹配任何字符 |
| |
左右两边表达式之间 “或” 关系,匹配左边或者右边 |
( ) |
(1). 在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰 (2). 取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到 |
(?: ) |
匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。 |
进一步的文字说明仍然比较抽象,因此,举例帮助大家理解。
举例1:表达式 “^aaa” 在匹配 “xxx aaa xxx” 时,匹配结果是:失败。因为 “^” 要求与字符串开始的地方匹配,因此,只有当 “aaa” 位于字符串的开头的时候,”^aaa” 才能匹配,比如:”aaa xxx xxx”。
举例2:表达式 “aaa$” 在匹配 “xxx aaa xxx” 时,匹配结果是:失败。因为 “$” 要求与字符串结束的地方匹配,因此,只有当 “aaa” 位于字符串的结尾的时候,”aaa$” 才能匹配,比如:”xxx xxx aaa”。
举例3:表达式 “.\b.” 在匹配 “@@@abc” 时,能够找到匹配的内容;匹配到的内容是:”@a”;匹配到的位置是:开始于2,结束于4。
进一步说明:”\b” 与 “^” 和 “$” 类似,本身不匹配任何字符,但是它要求它在匹配结果中所处位置的左右两边,其中一边是 “\w” 范围,另一边是 非”\w” 的范围。
举例4:表达式 “\bend\b” 在匹配 “weekend,endfor,end” 时,能够找到匹配的内容;匹配到的内容是:”end”;匹配到的位置是:开始于15,结束于18。
Pattern |
Text |
Match |
(.{1,3})|(.{5,}) |
bcade |
X |
三、正则表达式中的一些高级规则(ABAP部分支持)
1、 匹配次数中的贪婪与非贪婪
贪婪模式:
在使用修饰匹配次数的特殊符号时,有几种表示方法可以使同一个表达式能够匹配不同的次数,比如:”{m,n}”, “{m,}”, “?”, “*”, “+”,具体匹配的次数随被匹配的字符串而定。这种重复匹配不定次数的表达式在匹配过程中,总是尽可能多的匹配。比如,针对文本 “dxxxdxxxd”,举例如下:
表达式 |
匹配结果 |
(d)(\w+) |
“\w+” 将匹配第一个 “d” 之后的所有字符 “xxxdxxxd” |
(d)(\w+)(d) |
“\w+” 将匹配第一个 “d” 和最后一个 “d” 之间的所有字符 “xxxdxxx”。虽然 “\w+” 也能够匹配上最后一个 “d”,但是为了使整个表达式匹配成功,”\w+” 可以 “让出” 它本来能够匹配的最后一个 “d” |
由此可见,”\w+” 在匹配的时候,总是尽可能多的匹配符合它规则的字符。虽然第二个举例中,它没有匹配最后一个 “d”,但那也是为了让整个表达式能够匹配成功。同理,带 “*” 和 “{m,n}” 的表达式都是尽可能地多匹配,带 “?” 的表达式在可匹配可不匹配的时候,也是尽可能的 “要匹配”。这种匹配原则就叫作 “贪婪” 模式 。
非贪婪模式:(ABAP暂时不支持,但是最好理解吧)
在修饰匹配次数的特殊符号后再加上一个 “?” 号,则可以使匹配次数不定的表达式尽可能少的匹配,使可匹配可不匹配的表达式,尽可能的 “不匹配”。这种匹配原则叫作 “非贪婪” 模式,也叫作 “勉强” 模式。如果少匹配就会导致整个表达式匹配失败的时候,与贪婪模式类似,非贪婪模式会最小限度的再匹配一些,以使整个表达式匹配成功。举例如下,针对文本 “dxxxdxxxd” 举例:
表达式 |
匹配结果 |
(d)(\w+?) |
“\w+?” 将尽可能少的匹配第一个 “d” 之后的字符,结果是:”\w+?” 只匹配了一个 “x” |
(d)(\w+?)(d) |
为了让整个表达式匹配成功,”\w+?” 不得不匹配 “xxx” 才可以让后边的 “d” 匹配,从而使整个表达式匹配成功。因此,结果是:”\w+?” 匹配 “xxx” |
更多的情况,举例如下:
举例1:表达式 “<td>(.*)</td>” 与字符串 “<td><p>aa</p></td> <td><p>bb</p></td>” 匹配时,匹配的结果是:成功;匹配到的内容是 “<td><p>aa</p></td> <td><p>bb</p></td>” 整个字符串, 表达式中的 “</td>” 将与字符串中最后一个 “</td>” 匹配。
举例2:相比之下,表达式 “<td>(.*?)</td>” 匹配举例1中同样的字符串时,将只得到 “<td><p>aa</p></td>”, 再次匹配下一个时,可以得到第二个 “<td><p>bb</p></td>”。
2、 反向引用 \1, \2
表达式在匹配时,表达式引擎会将小括号 “( )” 包含的表达式所匹配到的字符串记录下来。在获取匹配结果的时候,小括号包含的表达式所匹配到的字符串可以单独获取。这一点,在前面的举例中,已经多次展示了。在实际应用场合中,当用某种边界来查找,而所要获取的内容又不包含边界时,必须使用小括号来指定所要的范围。比如前面的 “<td>(.*?)</td>”。
其实,”小括号包含的表达式所匹配到的字符串” 不仅是在匹配结束后才可以使用,在匹配过程中也可以使用。表达式后边的部分,可以引用前面 “括号内的子匹配已经匹配到的字符串”。引用方法是 “\” 加上一个数字。”\1″ 引用第1对括号内匹配到的字符串,”\2″ 引用第2对括号内匹配到的字符串……以此类推,如果一对括号内包含另一对括号,则外层的括号先排序号。换句话说,哪一对的左括号 “(” 在前,那这一对就先排序号。
举例如下:
举例1:表达式 “(‘|”)(.*?)\1″ 在匹配 ” ‘Hello’, “World” ” 时,匹配结果是:成功;匹配到的内容是:” ‘Hello’ “。再次匹配下一个时,可以匹配到 ” “World” “。
举例2:表达式 “(\w)\1{4,}” 在匹配 “aa bbbb abcdefg ccccc 111121111 999999999” 时,匹配结果是:成功;匹配到的内容是 “ccccc”。再次匹配下一个时,将得到 999999999。这个表达式要求 “\w” 范围的字符至少重复5次,注意与 “\w{5,}” 之间的区别。
举例3:表达式 “<(\w+)\s*(\w+(=(‘|”).*?\4)?\s*)*>.*?</\1>” 在匹配 “<td id=’td1’ style=”bgcolor:white”></td>” 时,匹配结果是成功。如果 “<td>” 与 “</td>” 不配对,则会匹配失败;如果改成其他配对,也可以匹配成功。
3、 预搜索
前面的章节中,我讲到了几个代表抽象意义的特殊符号:”^”,”$”,”\b”。它们都有一个共同点,那就是:它们本身不匹配任何字符,只是对 “字符串的两头” 或者 “字符之间的缝隙” 附加了一个条件。理解到这个概念以后,本节将继续介绍另外一种对 “两头” 或者 “缝隙” 附加条件的,更加灵活的表示方法。
正向预搜索:”(?=xxxxx)”,”(?!xxxxx)”
格式:”(?=xxxxx)”,在被匹配的字符串中,它对所处的 “缝隙” 或者 “两头” 附加的条件是:所在缝隙的右侧,必须能够匹配上 xxxxx 这部分的表达式。因为它只是在此作为这个缝隙上附加的条件,所以它并不影响后边的表达式去真正匹配这个缝隙之后的字符。这就类似 “\b”,本身不匹配任何字符。”\b” 只是将所在缝隙之前、之后的字符取来进行了一下判断,不会影响后边的表达式来真正的匹配。
举例1:表达式 “Windows (?=NT|XP)” 在匹配 “Windows 98, Windows NT, Windows 2000” 时,将只匹配 “Windows NT” 中的 “Windows “,其他的 “Windows ” 字样则不被匹配。
举例2:表达式 “(\w)((?=\1\1\1)(\1))+” 在匹配字符串 “aaa ffffff 999999999” 时,将可以匹配6个”f”的前4个,可以匹配9个”9″的前7个。这个表达式可以读解成:重复4次以上的字母数字,则匹配其剩下最后2位之前的部分。当然,这个表达式可以不这样写,在此的目的是作为演示之用。
格式:”(?!xxxxx)”,所在缝隙的右侧,必须不能匹配 xxxxx 这部分表达式。
举例3:表达式 “((?!\bstop\b).)+” 在匹配 “fdjka ljfdl stop fjdsla fdj” 时,将从头一直匹配到 “stop” 之前的位置,如果字符串中没有 “stop”,则匹配整个字符串。
举例4:表达式 “do(?!\w)” 在匹配字符串 “done, do, dog” 时,只能匹配 “do”。在本条举例中,”do” 后边使用 “(?!\w)” 和使用 “\b” 效果是一样的。
表达式 |
方向 |
说明 |
(?=xxx) |
正向预搜索(向右) |
正向预搜索,判断当前位置右侧是否能匹配指定表达式 |
(?!xxx) |
|
正向预搜索否定,判断当前位置右侧是否不能够匹配指定表达式 |
(?<=xxx) |
反向预搜索(向左)[ABAP不支持] |
反向预搜索,判断当前位置左侧是否能够匹配指定表达式 |
(?<!xxx) |
|
反向预搜索否定,判断当前位置左侧是否不能够匹配指定表达式 |
4、 其他补充
1> 在表达式 “\s”,”\d”,”\w”,”\b” 表示特殊意义的同时,其大写字母表示相反的意义
表达式 |
可匹配 |
\S |
匹配所有非空白字符(”\s” 可匹配各个空白字符) |
\D |
匹配所有的非数字字符 |
\W |
匹配所有的字母、数字、下划线以外的字符 |
\B |
匹配非单词边界,即左右两边都是 “\w” 范围或者左右两边都不是 “\w” 范围时的字符缝隙 |
2> 在表达式中有特殊意义,需要添加 “\” 才能匹配该字符本身的字符汇总
字符 |
说明 |
^ |
匹配输入字符串的开始位置。要匹配 “^” 字符本身,请使用 “\^” |
$ |
匹配输入字符串的结尾位置。要匹配 “$” 字符本身,请使用 “\$” |
( ) |
标记一个子表达式的开始和结束位置。要匹配小括号,请使用 “\(” 和 “\)” |
[ ] |
用来自定义能够匹配 ‘多种字符’ 的表达式。要匹配中括号,请使用 “\[” 和 “\]” |
{ } |
修饰匹配次数的符号。要匹配大括号,请使用 “\{” 和 “\}” |
. |
匹配除了换行符(\n)以外的任意一个字符。要匹配小数点本身,请使用 “\.” |
? |
修饰匹配次数为 0 次或 1 次。要匹配 “?” 字符本身,请使用 “\?” |
+ |
修饰匹配次数为至少 1 次。要匹配 “+” 字符本身,请使用 “\+” |
* |
修饰匹配次数为 0 次或任意次。要匹配 “*” 字符本身,请使用 “\*” |
| |
左右两边表达式之间 “或” 关系。匹配 “|” 本身,请使用 “\|” |
四、在ABAP中的应用
规则终于写完了!其实和其他编程语言规则差不多啦!下面结合ABAP讲讲怎么使用吧,Let’s GO!
不过需要注意, SAP只能是ECC6或者更高版本才可以使用正则(ABAP supports POSIX regular expressions as of Release 7.00)
在ABAP中定义了两个类来实现相应功能,分别是
CL_ABAP_REGEX regex就是regular expression的缩写,里面的方法不是很多,可能用到的也就只有构造方法和REATE_MATCHER这个方法。
CL_ABAP_MATCHER matcher匹配的意思,也就是说所有的匹配规则都和它有关,里面具体方法,se24去查看
其实正则表达式的应用无外乎三种:验证(是否符合规则)、查找(包含提取)、替换
1、 验证
实例1
IF CL_ABAP_MATCHER=>MATCHES( PATTERN = ‘\D+’ TEXT = ‘ZF25’ ) = ABAP_TRUE. WRITE ‘IS NUMBER’. ELSE. WRITE ‘IS NOT NUMBER’. ENDIF. |
输出结果:IS NOT NUMBER |
解释:CL_ABAP_MATCHER有一个静态方法,直接进行匹配。
2、 查找
实例2
DATA: MATCHER TYPE REF TO CL_ABAP_MATCHER, MATCHES TYPE MATCH_RESULT_TAB, MATCH LIKE LINE OF MATCHES, W_TEXT TYPE STRING. W_TEXT = ‘<a id=”MyLinks1_NewPostLink”‘ & ‘ href=”http://www.cnblogs.com/VerySky/admin/EditPosts.aspx?opt=1″>’. MATCHER = CL_ABAP_MATCHER=>CREATE( PATTERN = ‘http://.*(?=”)‘ TEXT = W_TEXT ). MATCHES = MATCHER->FIND_ALL( ). LOOP AT MATCHES INTO MATCH. WRITE:/ W_TEXT+MATCH-OFFSET(MATCH-LENGTH). ENDLOOP. |
输出结果:http://www.cnblogs.com/VerySky/admin/EditPosts.aspx?opt=1 |
解释:
- 创建match实例(创建规则),构造方法中有pattern参数输入规则,IGNORE_CASE是否忽略大小写,SIMPLE_REGEX是否使用简单规则(具体参见F1帮助文档),
B.在实例中有FIND_ALL(),FIND_NEXT()方法,可以用来查找。
这个方法是不是太麻烦了啊,不急有简单的方法,其实就是字符串处理中用到的。
实例3
DATA: patt TYPE string VALUE text TYPE string, result_tab TYPE match_result_tab. FIELD-SYMBOLS <match> LIKE LINE OF result_tab. FIND ALL OCCURRENCES OF REGEX patt IN RESULTS result_tab. LOOP AT result_tab ASSIGNING <match>. WRITE: / <match>-offset, <match>-length. ENDLOOP. |
输出结果: 11 3 24 3 |
实例4
DATA: STR TYPE STRING , RESULT_TAB TYPE MATCH_RESULT_TAB , WA LIKE LINE OF RESULT_TAB. *找出STRING里面的双字节字符 str = ‘abc我啊adfsf们’. FIND ALL OCCURRENCES OF REGEX ‘[^\x00-\xff]*’ IN STR RESULTS RESULT_TAB. LOOP AT RESULT_TAB INTO WA. WRITE / STR+WA-OFFSET(WA-LENGTH). ENDLOOP. |
输出结果: 我啊 们 |
解释:大家都知道英文字母是单字节的,中文是双字节的,但是在ABAP里面用strlen等方法是区别不出单双字节的,这个实例中讲的不失为一个很好的办法。
3、 替换
实例4
DATA: MATCHER TYPE REF TO CL_ABAP_MATCHER, COUNT TYPE I, W_TEXT TYPE STRING, W_NEWTEXT TYPE STRING. W_TEXT = ‘hubinshishuibuzhidao’. W_NEWTEXT = ‘FFF’. MATCHER = CL_ABAP_MATCHER=>CREATE( PATTERN = ‘sh.?i’ TEXT = W_TEXT ). WRITE:/ ‘REPLACE BEFORE:’, W_TEXT. COUNT = MATCHER->REPLACE_ALL( NEWTEXT = W_NEWTEXT ). WRITE:/ ‘REPLACE COUNT IS:’, COUNT. WRITE:/ ‘REPLACE AFTER:’, W_TEXT. |
输出结果: REPLACE BEFORE: hubinshishuibuzhidao REPLACE COUNT IS:2 REPLACE AFTER: hubinshishuibuzhidao |
大家肯定会说了,字符串前后没有没替换啊。注意修改的不是W_TEXT本身,他将修改后的值放到了MATCHER->TEXT即match类实例的属性里面,我们只需令W_TEXT = MATCHER->TEXT即可。
修改后
DATA: MATCHER TYPE REF TO CL_ABAP_MATCHER, COUNT TYPE I, W_TEXT TYPE STRING, W_NEWTEXT TYPE STRING. W_TEXT = ‘hubinshishuibuzhidao’. W_NEWTEXT = ‘FFF’. MATCHER = CL_ABAP_MATCHER=>CREATE( PATTERN = ‘sh.?i’ TEXT = W_TEXT ). WRITE:/ ‘REPLACE BEFORE:’, W_TEXT. COUNT = MATCHER->REPLACE_ALL( NEWTEXT = W_NEWTEXT ). W_TEXT = MATCHER->TEXT. WRITE:/ ‘REPLACE COUNT IS:’, COUNT. WRITE:/ ‘REPLACE AFTER:’, W_TEXT. |
输出结果: REPLACE BEFORE: hubinshishuibuzhidao REPLACE COUNT IS:2 REPLACE AFTER: hubinFFFFFFbuzhidao |
同样这里也有简单方法,字符串处理中的方法也是支持正则的
实例5
DATA TEXT TYPE STRING VALUE ‘-dfufdud-‘. REPLACE ALL OCCURRENCES OF REGEX ‘u’ IN text WITH ‘x’. WRITE TEXT. |
输出结果: -dfxfdxd- |
4、 方法语句总结
类结
CL_ABAP_MATCHER类里的重要方法
字符串处理中,支持正则的语句
五、补充
这里给出ABAP F1帮助中给出的一个特殊字符的列表,很有用的
Special Characters in Regular Expressions
The following tables summarize the special characters in regular expressions:
Escape character
Special character |
Meaning |
\ |
Escape character for special characters |
Special character for single character strings
Special character |
Meaning |
. |
Placeholder for any single character |
\C |
Placeholder for any single character |
\d |
Placeholder for any single digit |
\D |
Placeholder for any character other than a digit |
\l |
Placeholder for any lower-case letter |
\L |
Placeholder for any character other than a lower-case letter |
\s |
Placeholder for a blank character |
\S |
Placeholder for any character other than a blank character |
\u |
Placeholder for any upper-case letter |
\U |
Placeholder for any character other than an upper-case letter |
\w |
Placeholder for any alphanumeric character including _ |
\W |
Placeholder for any non-alphanumeric character except for _ |
[ ] |
Definition of a value set for single characters |
[^ ] |
Negation of a value set for single characters |
[ – ] |
Definition of a range in a value set for single characters |
[ [:alnum:] ] |
Description of all alphanumeric characters in a value set |
[ [:alpha:] ] |
Description of all letters in a value set |
[ [:blank:] ] |
Description for blank characters and horizontal tabulators in a value set |
[ [:cntrl:] ] |
Description of all control characters in a value set |
[ [:digit:] ] |
Description of all digits in a value set |
[ [:graph:] ] |
Description of all graphic special characters in a value set |
[ [:lower:] ] |
Description of all lower-case letters in a value set |
[ [:print:] ] |
Description of all displayable characters in a value set |
[ [:punct:] ] |
Description of all punctuation characters in a value set |
[ [:space:] ] |
Description of all blank characters, tabulators, and carriage feeds in a value set |
[ [:unicode:] ] |
Description of all Unicode characters in a value set with a code larger than 255 |
[ [:upper:] ] |
Description of all upper-case letters in a value set |
[ [:word:] ] |
Description of all alphanumeric characters in a value set, including _ |
[ [:xdigit:] ] |
Description of all hexadecimal digits in a value set |
\a \f \n \r \t \v |
Diverse platform-specific control characters |
[..] |
Reserved for later enhancements |
[==] |
Reserved for later enhancements |
Special characters for character string patterns
Special character |
Meaning |
{n} |
Concatenation of n single characters |
{n,m} |
Concatenation of at least n and a maximum of m single characters |
{n,m}? |
Reserved for later enhancements |
? |
One or no single characters |
* |
Concatenation of any number of single characters including ‘no characters’ |
*? |
Reserved for later enhancements |
+ |
Concatenation of any number of single characters excluding ‘no characters’ |
+? |
Reserved for later enhancements |
| |
Linking of two alternative expressions |
( ) |
Definition of subgroups with registration |
(?: ) |
Definition of subgroups without registration |
\1, \2, \3 … |
Placeholder for the register of subgroups |
\Q … \E |
Definition of a string of literal characters |
(? … ) |
Reserved for later enhancements |
Special characters for search strings
Special character |
Meaning |
^ |
Anchor character for the start of a line |
\A |
Anchor character for the start of a character string |
$ |
Anchor character for the end of a line |
\Z |
Anchor character for the end of a character string |
\< |
Start of a word |
\> |
End of a word |
\b |
Start or end of a word |
\B |
Space between characters within a word |
(?= ) |
Preview condition |
(?! ) |
Negated preview condition |
Special characters for replacement texts
Special character |
Meaning |
$0, $& |
Placeholder for the whole found location |
$1, $2, $3… |
Placeholder for the register of subgroups |
$` |
Placeholder for the text before the found location |
$’ |
Placeholder for the text after the found location |
从上面的列表可以看出,ABAP中对“非贪婪模式”(ABAP啊,你不支持啊,你太贪婪了)和“反向预搜索”暂不支持,希望以后能够赶紧扩展功能吧,毕竟很强大的说。
正则表达式是需要反复修改,最后才能找到最匹配的规则的,为此ABAP Program里也提供了一个,测试用得小程序,SE38里找DEMO_REGEX_TOY运行即可。
在ABAP培训资料里面有一个很有意思的表达式,大家有兴趣可以看一下它能匹配什么文字
^(?=\d)(?:(?:31(?!.(?:0?[2469]|11))|(?:30 |29)(?!.0?2)|29(?=.0?2.(?:(?:(?:1[6-9]|[2 -9]\d)?(?:0[48]|[2468][048]|[13579][26])| (?:(?:16|[2468][048]|[3579][26])00)))(?:\ x20|$))|(?:2[0-8]|1\d|0?[1-9]))([-./])(?: 1[012]|0?[1-9])\1(?:1[6-9]|[2-9]\d)?\d\d( ?:(?=\x20\d)\x20|$))?(((0?[1-9]|1[012])(: [0-5]\d){0,2}(\x20[AP]M))|([01]\d|2[0-3]) (:[0-5]\d){1,2})?$ |
以下附上网上的一些资料:
表达式全集
正则表达式有多种不同的风格。下表是在PCRE中元字符及其在正则表达式上下文中的行为的一个完整列表:
字符 |
描述 |
\ |
将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。序列“\\”匹配“\”而“\(”则匹配“(”。 |
^ |
匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。 |
$ |
匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。 |
* |
匹配前面的子表达式零次或多次。例如,zo*能匹配“z”以及“zoo”。*等价于{0,}。 |
+ |
匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。 |
? |
匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”中的“do”。?等价于{0,1}。 |
{n} |
n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。 |
{n,} |
n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。 |
{n,m} |
m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。 |
? |
当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。 |
. |
匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“[.\n]”的模式。 |
(pattern) |
匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“\(”或“\)”。 |
(?:pattern) |
匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。 |
(?=pattern) |
正向预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?!pattern) |
负向预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始 |
x|y |
匹配x或y。例如,“z|food”能匹配“z”或“food”。“(z|f)ood”则匹配“zood”或“food”。 |
[xyz] |
字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。 |
[^xyz] |
负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“p”。 |
[a-z] |
字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。 |
[^a-z] |
负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。 |
\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个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。 |
\nml |
如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。 |
\un |
匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(?)。 |
常用的正则表达式
正则表达式用于字符串处理、表单验证等场合,实用高效。现将一些常用的表达式收集于此,以备不时之需。
用户名:/^[a-z0-9_-]{3,16}$/
密码:/^[a-z0-9_-]{6,18}$/
十六进制值:/^#?([a-f0-9]{6}|[a-f0-9]{3})$/
电子邮箱:/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/
URL:/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
IP 地址:/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
HTML 标签:/^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/
Unicode编码中的汉字范围:/^[u4e00-u9fa5],{0,}$/
匹配中文字符的正则表达式: [\u4e00-\u9fa5]
评注:匹配中文还真是个头疼的事,有了这个表达式就好办了
匹配双字节字符(包括汉字在内):[^\x00-\xff]
评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)
匹配空白行的正则表达式:\n\s*\r
评注:可以用来删除空白行
匹配HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? />
评注:网上流传的版本太糟糕,上面这个也仅仅能匹配部分,对于复杂的嵌套标记依旧无能为力
匹配首尾空白字符的正则表达式:^\s*|\s*$
评注:可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式
匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
评注:表单验证时很实用
匹配网址URL的正则表达式:[a-zA-z]+://[^\s]*
评注:网上流传的版本功能很有限,上面这个基本可以满足需求
匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
评注:表单验证时很实用
匹配国内电话号码:\d{3}-\d{8}|\d{4}-\d{7}
评注:匹配形式如 0511-4405222 或 021-87888822
匹配腾讯QQ号:[1-9][0-9]{4,}
评注:腾讯QQ号从10000开始
匹配中国大陆邮政编码:[1-9]\d{5}(?!\d)
评注:中国大陆邮政编码为6位数字
匹配身份证:\d{15}|\d{18}
评注:中国大陆的身份证为15位或18位
匹配ip地址:\d+\.\d+\.\d+\.\d+
评注:提取ip地址时有用
匹配特定数字:
^[1-9]\d*$ //匹配正整数
^-[1-9]\d*$ //匹配负整数
^-?[1-9]\d*$ //匹配整数
^[1-9]\d*|0$ //匹配非负整数(正整数 + 0)
^-[1-9]\d*|0$ //匹配非正整数(负整数 + 0)
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ //匹配正浮点数
^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ //匹配负浮点数
^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$ //匹配浮点数
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$ //匹配非负浮点数(正浮点数 + 0)
^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$//匹配非正浮点数(负浮点数 + 0)
评注:处理大量数据时有用,具体应用时注意修正
匹配特定字符串:
^[A-Za-z]+$//匹配由26个英文字母组成的字符串
^[A-Z]+$//匹配由26个英文字母的大写组成的字符串
^[a-z]+$//匹配由26个英文字母的小写组成的字符串
^[A-Za-z0-9]+$//匹配由数字和26个英文字母组成的字符串
^\w+$//匹配由数字、26个英文字母或者下划线组成的字符串
如何写出高效率的正则表达式
如果纯粹是为了挑战自己的正则水平,用来实现一些特效(例如使用正则表达式计算质数、解线性方程),效率不是问题;如果所写的正则表达式只是为了满足一两次、几十次的运行,优化与否区别也不太大。但是,如果所写的正则表达式会百万次、千万次地运行,效率就是很大的问题了。我这里总结了几条提升正则表达式运行效率的经验(工作中学到的,看书学来的,自己的体会),贴在这里。如果您有其它的经验而这里没有提及,欢迎赐教。
为行文方便,先定义两个概念。
误匹配:指正则表达式所匹配的内容范围超出了所需要范围,有些文本明明不符合要求,但是被所写的正则式“击中了”。例如,如果使用\d{11}来匹配11位的手机号,\d{11}不单能匹配正确的手机号,它还会匹配98765432100这样的明显不是手机号的字符串。我们把这样的匹配称之为误匹配。
漏匹配:指正则表达式所匹配的内容所规定的范围太狭窄,有些文本确实是所需要的,但是所写的正则没有将这种情况囊括在内。例如,使用\d{18}来匹配18位的身份证号码,就会漏掉结尾是字母X的情况。
写出一条正则表达式,既可能只出现误匹配(条件写得极宽松,其范围大于目标文本),也可能只出现漏匹配(只描述了目标文本中多种情况种的一种),还可能既有误匹配又有漏匹配。例如,使用\w+\.com来匹配.com结尾的域名,既会误匹配abc_.com这样的字串(合法的域名中不含下划线,\w包含了下划线这种情况),又会漏掉ab-c.com这样的域名(合法域名中可以含中划线,但是\w不匹配中划线)。
精准的正则表达式意味着既无误匹配且无漏匹配。当然,现实中存在这样的情况:只能看到有限数量的文本,根据这些文本写规则,但是这些规则将会用到海量的文本中。这种情况下,尽可能地(如果不是完全地)消除误匹配以及漏匹配,并提升运行效率,就是我们的目标。本文所提出的经验,主要是针对这种情况。
掌握语法细节。正则表达式在各种语言中,其语法大致相同,细节各有千秋。明确所使用语言的正则的语法的细节,是写出正确、高效正则表达式的基础。例如,perl中与\w等效的匹配范围是[a-zA-Z0-9_];perl正则式不支持肯定逆序环视中使用可变的重复(variable repetition inside lookbehind,例如(?<=.*)abc),但是.Net语法是支持这一特性的;又如,JavaScript连逆序环视(Lookbehind,如(?<=ab)c)都不支持,而perl和python是支持的。《精通正则表达式》第3章《正则表达式的特性和流派概览》明确地列出了各大派系正则的异同,这篇文章也简要地列出了几种常用语言、工具中正则的比较。对于具体使用者而言,至少应该详细了解正在使用的那种工作语言里正则的语法细节。
先粗后精,先加后减。使用正则表达式语法对于目标文本进行描述和界定,可以像画素描一样,先大致勾勒出框架,再逐步在局步实现细节。仍举刚才的手机号的例子,先界定\d{11},总不会错;再细化为1[358]\d{9},就向前迈了一大步(至于第二位是不是3、5、8,这里无意深究,只举这样一个例子,说明逐步细化的过程)。这样做的目的是先消除漏匹配(刚开始先尽可能多地匹配,做加法),然后再一点一点地消除误匹配(做减法)。这样有先有后,在考虑时才不易出错,从而向“不误不漏”这个目标迈进。
留有余地。所能看到的文本sample是有限的,而待匹配检验的文本是海量的,暂时不可见的。对于这样的情况,在写正则表达式时要跳出所能见到的文本的圈子,开拓思路,作出“战略性前瞻”。例如,经常收到这样的垃圾短信:“发*票”、“发#漂”。如果要写规则屏蔽这样烦人的垃圾短信,不但要能写出可以匹配当前文本的正则表达式 发[*#](?:票|漂),还要能够想到 发.(?:票|漂|飘)之类可能出现的“变种”。这在具体的领域或许会有针对性的规则,不多言。这样做的目的是消除漏匹配,延长正则表达式的生命周期。
明确。具体说来,就是谨慎用点号这样的元字符,尽可能不用星号和加号这样的任意量词。只要能确定范围的,例如\w,就不要用点号;只要能够预测重复次数的,就不要用任意量词。例如,写析取twitter消息的脚本,假设一条消息的xml正文部分结构是<span class=”msg”>…</span>且正文中无尖括号,那么<span class=”msg”>[^<]{1,480}</span>这种写法的思路要好于<span class=”msg”>.*</span>,原因有二:一是使用[^<],它保证了文本的范围不会超出下一个小于号所在的位置;二是明确长度范围,{1,480},其依据是一条twitter消息大致能的字符长度范围。当然,480这个长度是否正确还可推敲,但是这种思路是值得借鉴的。说得狠一点,“滥用点号、星号和加号是不环保、不负责任的做法”。
不要让稻草压死骆驼。每使用一个普通括号()而不是非捕获型括号(?:…),就会保留一部分内存等着你再次访问。这样的正则表达式、无限次地运行次数,无异于一根根稻草的堆加,终于能将骆驼压死。养成合理使用(?:…)括号的习惯。
宁简勿繁。将一条复杂的正则表达式拆分为两条或多条简单的正则表达式,编程难度会降低,运行效率会提升。例如用来消除行首和行尾空白字符的正则表达式s/^\s+|\s+$//g;,其运行效率理论上要低于s/^\s+//g; s/\s+$//g; 。这个例子出自《精通正则表达式》第五章,书中对它的评论是“它几乎总是最快的,而且显然最容易理解”。既快又容易理解,何乐而不为?工作中我们还有其它的理由要将C==(A|B)这样的正则表达式拆为A和B两条表达式分别执行。例如,虽然A和B这两种情况只要有一种能够击中所需要的文本模式就会成功匹配,但是如果只要有一条子表达式(例如A)会产生误匹配,那么不论其它的子表达式(例如B)效率如何之高,范围如何精准,C的总体精准度也会因A而受到影响。
巧妙定位。有时候,我们需要匹配的the,是作为单词的the(两边有空格),而不是作为单词一部分的t-h-e的有序排列(例如together中的the)。在适当的时候用上^,$,\b等等定位锚点,能有效提升找到成功匹配、淘汰不成功匹配的效率。