字符串處理是每門應用語言里都要掌握的,在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 .