ABAP 正则表达式(Regular Expressions)详解编程语言

正则表达式(Regular Expressions)


正则表达式在其他编程语言中的应用非常广泛,网上资料也非常多,而网上在ABAP语言中应用的资料却很少,尽管各语言中正则表达式语法知识都很类似,但仍然有一些区别,本文主要是简单介绍一下其基本语法。总结一下,方便大家查阅。

欢迎转载,请注明出处,文中不足之处还望指正。(Email:[email protected]

一、简要认识

正则表达式就是用一个“字符串”来描述一个特征,然后去验证另一个“字符串”是否符合这个特征。比如表达式“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        = [email protected]‘ ).
match = matcher->match( ).
WRITE match.

输出结果:X

解释:

1>     ‘/w+@/w+(/./w+)+’中 /w 是表示任意一个字母或数字或下划线,+ 表示前面字符个数为一个或多个,@[email protected]

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

解释:

  1. 创建match实例(创建规则),构造方法中有pattern参数输入规则,IGNORE_CASE是否忽略大小写,SIMPLE_REGEX是否使用简单规则(具体参见F1帮助文档),

B.在实例中有FIND_ALL(),FIND_NEXT()方法,可以用来查找。

 

这个方法是不是太麻烦了啊,不急有简单的方法,其实就是字符串处理中用到的。

 

实例3

DATA: patt       TYPE string VALUE `n.?w`,
      text       TYPE string,
      result_tab TYPE match_result_tab.
FIELD-SYMBOLS <match> LIKE LINE OF result_tab.
FIND ALL OCCURRENCES OF REGEX patt IN
      `Everybody knows this is nowhere`
     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}

mn均为非负整数,其中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的向后引用。如果前面的条件都不满足,若nm均为八进制数字(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等等定位锚点,能有效提升找到成功匹配、淘汰不成功匹配的效率。

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/20081.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论