字符串处理是每门应用语言里都要掌握的,在SAP ABAP语言里也不例外,说到字符串处理,必然少不了正则式的出现,有了正则式,字符串才可以灵活处理,不过我在ABAP项目中看到的还都是使用的以前R3中老式的字符串处理语句。这节的内容是非常重要的,学好后,多复杂的字符串处理也不在话下。如果你以前学过其他语言正则式,比如Java中的正则式,这节内容看得就很轻松了。当然,正则式不是哪门语言特有的,正则式有如数据结构一样,只是使用不同语言去实现他而已,就是我一样,以前研究过Java中的正则式,然后在JavaScript中一样,最后到ABAP中,精髓还是一样,只是语句不同而已!
1.7. 字符串处理常用函数
SPLIT dobj AT sep INTO { {result1 result2 ... } | { TABLE result_tab} } 必须指定足够目标字段。否则,用字段 dobj 的剩余部分填充最后目标字段并包含分界符;或者使用内表动态接收
SHIFT dobj { [ { BY num PLACES }|{ UP TO sub_string} ][ [ LEFT | RIGHT ][ CIRCULAR ] ] }
| { {LEFT DELETING LEADING }|{ RIGHT DELETING TRAILING } } pattern
对于固定长度字符串类型, shift 产生的空位会使用空格或十六进制的 0 (如果为 X 类型串时)来填充
向右移动时前面会补空格,固定长度类型字符串与 String 结果是不一样: String 类型右移后不会被截断,只是字串前面补相应数量的空格,但如果是 C 类型时,则会截断;左移后后面是否被空格要看是否是固定长度类型的字符串还是变长的 String 类型串,左移后 C 类型会补空格, String 类型串不会(会缩短)
CIRCULAR :将移出的字符串放在左边或者左边
pattern :只要前导或尾部字符在指定的 pattern 字符集里就会被去掉,直到第一个不在模式 pattern 的字符止
CONDENSE <c> [NO - GAPS] . 如果是 C 类型只去掉前面的空格(因为是定长,即使后面空格去掉了,左对齐时后面会补上空格),如果是 String 类型,则后面空格也会被去掉; 字符串中间的多个连续的空格使用一个空格替换 (String 类型也是这样 ) ; NO-GAPS :字符串中间的所有空格都也都会去除 (String 类型也是这样 ) ;空格去掉后会左对齐
CONCATENATE {dobj1 dobj2 ... }|{ LINES OF itab}
INTO result
[ SEPARATED BY sep]
[ RESPECTING BLANKS ] .
CDNT 类型的前导空格会保留,尾部空格都会被去掉,但对 String 类型所有空格都会保留;对于 c, d, n, t 类型的字符串有一个 RESPECTING BLANKS 选项可使用,表示尾部空格也会保留。注: 使用 `` 对 String 类型进行赋值时才会保留尾部空格 字符串连接可以使用 && 来操作,具体请参考这里
strlen ( arg ) 、 Xstrlen( arg ) String 类型的尾部空格会计入字符个数中,但 C 类型的变量尾部空格不会计算入
substring ( val = TEXT [off = off] [len = len] )
count ( val = TEXT {sub = substring}|{regex = regex} ) 匹配指定字符串 substring 或正则式 regex 出现的子串次数,返回的类型为 i 整型类型
contains ( val = TEXT REGEX = REGEX ) 是否包含。返回布尔值 ,注:只能用在 if 、 While 等条件表达式中
matches ( val = TEXT REGEX = REGEX ) regex 表达式要与 text 完全匹配 ,这与 contains 是不一样的。返回布尔值,也只能用在 if 、 While 等条件表达式中
match ( val = TEXT REGEX = REGEX occ = occ ) 返回的为匹配到的字符串。注:每次只匹配一个。 occ :表示需匹配到第几次出现的子串。如果为正,则从头往后开始计算,如果为负,则从尾部向前计算
find ( val = TEXT {sub = substring}|{regex = regex}[occ = occ] ) 查找 substring 或者匹配 regex 的子串的位置。如果未找到,则返回 -1 ,返回的为 offset ,所以从 0 开始
FIND ALL OCCURRENCES OF REGEX regex IN dobj
[ MATCH COUNT mcnt] 成功匹配的次数
{ { [ MATCH OFFSET moff][ MATCH LENGTH mlen] } 最后一次整体匹配到的串(整体串,最外层分组,而不是指正则式最内最后一个分组)起始位置 与长度
| [ RESULTS result_tab|result_wa] } result_tab 接收所有匹配结果, result_wa 只能接收最后一次匹配结果
[ SUBMATCHES s1 s2 ... ] . 通常与前面的 MATCH OFFSET/ LENGTH 一起使用。只会接收使用括号进行分组的子组。如果变量 s1 s2 ... 比分组的数量多,则多余的变量被 initial ;如果变量 s1 s2 ... 比分组的数量少,则多余的分组将被忽略;且只存储第一次或最后一次匹配到的结果
replace ( val = TEXT REGEX = REGEX WITH = NEW ) 使用 new 替换指定的子符串,返回 String 类型
REPLACE ALL OCCURRENCES OF REGEX regex IN dobj WITH new
1.7.1. count 、 match 结合
DATA : text TYPE string VALUE `Cathy's cat with the hat sat on Matt's mat.` ,
regx TYPE string VALUE `\<.at\>` . "\< 单词开头 , \> 单词结尾
DATA : counts TYPE i ,
index TYPE i ,
substr TYPE string .
WRITE / text .
NEW-LINE .
counts = count ( val = text regex = regx ). " 返回匹配次数
DO counts TIMES .
index = find ( val = text regex = regx occ = sy - index ). " 返回匹配到的的起始位置索引
substr = match ( val = text regex = regx occ = sy - index ). " 返回匹配到的串
index = index + 1 .
WRITE AT index substr .
ENDDO .
1.7.2. FIND …SUBMATCHES
DATA : moff TYPE i ,
mlen TYPE i ,
s1 TYPE string ,
s2 TYPE string ,
s3 TYPE string ,
s4 TYPE string .
FIND ALL OCCURRENCES OF REGEX `((\w+)\W+\2\W+(\w+)\W+\3)` "\2 \3 表示反向引用前面匹配到的第二与第三个子串
IN ` Hey hey, my my , Rock and roll can never die Hey hey, my my ` " 会匹配二次,但只会返回第二次匹配到的结果,第一次匹配到的子串不会存储到 s1 、 s2 、 s3 中去
IGNORING CASE
MATCH OFFSET moff
MATCH LENGTH mlen
SUBMATCHES s1 s2 s3 s4 . " 根据从外到内,从左到右的括号顺序依次存储到 s1 s2… 中,注:只取出使用括号括起来的子串,如想取整体子串则也要括起来,这与 Java 不同
WRITE : / s1 , / s2 , / s3 , / s4 , / moff , / mlen . "s4 会被忽略
1.7.3. FIND …RESULTS itab
DATA : result TYPE STANDARD TABLE OF string WITH HEADER LINE .
" 与 Java 不同,只要是括号括起来的都称为子匹配(即使用整体也用括号括起来了),
" 不管括号嵌套多少层,统称为子匹配,且匹配到的所有子串都会存储到,
"MATCH_RESULT- SUBMATCHES 中,即使最外层的括号匹配到的子串也会存储到 SUBMATCHES
" 内表中。括号解析的顺序为: 从外到内,从左到右 的优先级顺序来解析匹配结构。
"Java 中的 group(0) 存储的是整体匹配串,即使整体未(或使用)使用括号括起来
PERFORM get_match TABLES result
USING '2011092131221032' '(((\d{2})(\d{2}))(\d{2})(\d{2}))' .
LOOP AT result .
WRITE : / result .
ENDLOOP .
FORM get_match TABLES p_result " 返回所有分组匹配 (括号括起来的表达式)
USING p_str
p_reg .
DATA : result_tab TYPE match_result_tab WITH HEADER LINE .
DATA : subresult_tab TYPE submatch_result_tab WITH HEADER LINE .
" 注意:带表头时 result_tab 后面一定要带上中括号,否则激活时出现奇怪的问题
FIND ALL OCCURRENCES OF REGEX p_reg IN p_str RESULTS result_tab[] .
" result_tab 中存储了匹配到的子串本身 (与 Regex 整体匹配的串,存储在
"result_tab-offset 、 result_tab-length 中) 以及所子分组 (括号部分,存储在
"result_tab-submatches 中)
LOOP AT result_tab .
" 如需取整体匹配到的子串(与 Regex 整体匹配的串),则使用括号将整体 Regex 括起来
" 来即可,括起来后也会自动存储到 result_tab-submatches ,而不需要在这里像这样读取
* p_result = p_str+result_tab-offset(result_tab-length).
* APPEND p_result.
subresult_tab[] = result_tab - submatches .
LOOP AT subresult_tab .
p_result = p_str+subresult_tab - offset ( subresult_tab - length ).
APPEND p_result .
ENDLOOP .
ENDLOOP .
ENDFORM .
1.7.4. 正则式类
regex = Regular expression [?reɡjul?]
cl_abap_ regex : 与 Java 中的 java.util.regex. Pattern 的类对应
cl_abap_matcher : 与 Java 中的 java.util.regex. Matcher 的类对应
1.7.4.1. matches 、 match
是否完全匹配 ( 正则式中不必使用 ^ 与 $ ); matches 为静态方法 , 而 match 为实例方法 , 作用都是一样
DATA : matcher TYPE REF TO cl_abap_matcher ,
match TYPE match_result ,
match_line TYPE submatch_result .
" ^$ 可以省略 , 因为 matches 方法本身就是完全匹配整个 Regex
IF cl_abap_matcher => matches ( pattern = '^(db(ai).*)$' text = 'dbaiabd' ) = 'X' .
matcher = cl_abap_matcher => get_object ( ). " 获取最后一次匹配到的 Matcher 实例
match = matcher -> get_match ( ). " 获取最近一次匹配的结果 ( 注 : 是整体匹配的结果 )
WRITE / matcher -> text+match - offset ( match - length ).
LOOP AT match - submatches INTO match_line . " 提取子分组 ( 括号括起来的部分 )
WRITE : /20 match_line - offset , match_line - length , matcher -> text+match_line - offset ( match_line - length ).
ENDLOOP .
ENDIF .
DATA : matcher TYPE REF TO cl_abap_matcher ,
match TYPE match_result ,
match_line TYPE submatch_result .
"^$ 可以省略,因为 matche 方法本身就是完全匹配整个 Regex
matcher = cl_abap_matcher => create ( pattern = '^(db(ai).*)$' text = 'dbaiabd' ).
IF matcher -> match ( ) = 'X' .
match = matcher -> get_match ( ). " 获取最近一次匹配的结果
WRITE / matcher -> text+match - offset ( match - length ).
LOOP AT match - submatches INTO match_line . " 提取子分组(括号括起来的部分)
WRITE : /20 match_line - offset , match_line - length , matcher -> text+match_line - offset ( match_line - length ).
ENDLOOP .
ENDIF .
1.7.4.2. contains
是否包含 ( 也可在正则式中使用 ^ 与 $ 用于完全匹配检查 , 或者使用 ^ 检查是否匹配开头 , 或者使用 $ 匹配结尾 )
DATA : matcher TYPE REF TO cl_abap_matcher ,
match TYPE match_result ,
match_line TYPE submatch_result .
IF cl_abap_matcher => contains ( pattern = '(db(ai).{2}b)' text = 'dbaiabddbaiabb' ) = 'X' .
matcher = cl_abap_matcher => get_object ( ). " 获取最后一次匹配到的 Matcher 实例
match = matcher -> get_match ( ). " 获取最近一次匹配的结果
WRITE / matcher -> text+match - offset ( match - length ).
LOOP AT match - submatches INTO match_line . " 提取子分组(括号括起来的部分)
WRITE : /20 match_line - offset , match_line - length , matcher -> text+match_line - offset ( match_line - length ).
ENDLOOP .
ENDIF .
1.7.4.3. find_all
DATA : matcher TYPE REF TO cl_abap_matcher ,
match_line TYPE submatch_result ,
itab TYPE match_result_tab WITH HEADER LINE .
matcher = cl_abap_matcher => create ( pattern = '<[^<>]*(ml)>' text = '<html>hello</html>' ). " 创建 matcher 实例
" 注 :子分组存储在 itab-submatches 字段里
itab [] = matcher -> find_all ( ).
LOOP AT itab .
WRITE : / matcher -> text , itab - offset , itab - length , matcher -> text+itab - offset ( itab - length ).
LOOP AT itab - submatches INTO match_line . " 提取子分组(括号括起来的部分)
WRITE : /20 match_line - offset , match_line - length , matcher -> text+match_line - offset ( match_line - length ).
ENDLOOP .
ENDLOOP .
1.7.4.4. find_next
逐个找出匹配的子串,包括子分组(括号括起的部分)
DATA : matcher TYPE REF TO cl_abap_matcher ,
match TYPE match_result , match_line TYPE submatch_result ,
itab TYPE match_result_tab WITH HEADER LINE .
matcher = cl_abap_matcher => create ( pattern = '<[^<>]*(ml)>' text = '<html>hello</html>' ).
WHILE matcher -> find_next ( ) = 'X' .
match = matcher -> get_match ( ). " 获取最近一次匹配的结果
WRITE : / matcher -> text , match - offset , match - length , matcher -> text+match - offset ( match - length ).
LOOP AT match - submatches INTO match_line . " 提取子分组(括号括起来的部分)
WRITE : /20 match_line - offset , match_line - length , matcher -> text+match_line - offset ( match_line - length ).
ENDLOOP .
ENDWHILE .
1.7.4.5. get_length 、 get_offset 、 get_submatch
DATA : matcher TYPE REF TO cl_abap_matcher ,
length TYPE i , offset TYPE i ,
submatch TYPE string .
matcher = cl_abap_matcher => create ( pattern = '(<[^<>]*(ml)>)' text = '<html>hello</html>' ).
WHILE matcher -> find_next ( ) = 'X' . " 循环 2 次
" 为 0 时,表示取整个 Regex 匹配到的子串,这与 Java 一样,但如果整个 Regex 使用括号括起来后,
" 则分组索引为 1 ,这又与 Java 不一样( Java 不管是否使用括号将整个 Regex 括起来,分组索引号都为 0 )
" 上面 Regex 中共有两个子分组,再加上整个 Regex 为隐含分组,所以一共为 3 组
DO 3 TIMES .
" 在当前匹配到的串(整个 Regex 相匹配的串)中返回指定子分组的匹配到的字符串长度
length = matcher -> get_length ( sy - index - 1 ).
" 在当前匹配到的串(整个 Regex 相匹配的串)中返回指定子分组的匹配到的字符串起始位置
offset = matcher -> get_offset ( sy - index - 1 ).
" 在当前匹配到的串(整个 Regex 相匹配的串)中返回指定子分组的匹配到的字符串
submatch = matcher -> get_submatch ( sy - index - 1 ).
WRITE : / length , offset , matcher -> text+offset ( length ), submatch .
ENDDO .
SKIP .
ENDWHILE .
1.7.4.6. replace_all
DATA : matcher TYPE REF TO cl_abap_matcher ,
count TYPE i ,
repstr TYPE string .
matcher = cl_abap_matcher => create ( pattern = '<[^<>]*>' text = '<html>hello</html>' ).
count = matcher -> replace_all ( `` ). " 返回替换的次数
repstr = matcher -> text . " 获取被替换后的新串
WRITE : / count , repstr .