Перейти до вмісту

Регулярні вирази

Регулярні вирази (regex) — це мова шаблонів для пошуку, перевірки й заміни в рядках. В ABAP регулярні вирази використовуються у FIND/REPLACE, вбудованих функціях (find, match, replace, contains, substring_* тощо), функціях ABAP SQL/CDS (like_regexpr, replace_regexpr …) і через об’єктне API класів CL_ABAP_REGEX / CL_ABAP_MATCHER.

Основний синтаксис — PCRE (Perl Compatible Regular Expressions). Підтримуються також XPath і XSD (автоматично конвертуються в PCRE). Застаріло: POSIX (REGEX/regex додатки й параметри; краще одразу PCRE/pcre).

ВиразЗначення
xКонкретний символ
.Будь-який символ крім переводу рядка (у dotall mode — будь-який)
\d / \DЦифра / не цифра
\s / \SWhitespace (пробіл, табуляція, новий рядок) / не whitespace
\w / \WСлівний символ (літера, цифра, _) / не слівний
\n, \t, \r, \RLF, tab, CR, будь-яка послідовність нового рядка
\x{hex}Символ за hex-кодом
\p{..} / \P{..}Юнікод-властивість (напр. \p{Ll} — малі літери)
" Замінити всі цифри на #
DATA(res) = replace( val = `a1-b2 3-4c9` pcre = `\d` with = `#` occ = 0 ). " a#-b# #-#c#
" Замінити всі whitespace
res = replace( val = |ab cd\tef\ngh| pcre = `\s` with = `#` occ = 0 ). " ab#cd#ef#gh

Квантифікатори та альтернативи

Section titled “Квантифікатори та альтернативи”
ВиразЗначення
x*, x+0+ / 1+ повторень (жадібно)
x?Опціонально (0 або 1)
x{m}, x{m,n}, x{m,}Точно m / від m до n / m і більше
x*?, x+?Нежадібні версії
x|yАльтернатива: x або y
" Жадібний vs нежадібний
DATA(a) = replace( val = `<span>Hallo</span>` pcre = `<.+>` with = `#` ). " # (цілий блок)
DATA(b) = replace( val = `<span>Hallo</span>` pcre = `<.+?>` with = `#` ). " #Hallo#
ВиразЗначення
[abc]Будь-який символ із набору
[a-z]Діапазон
[^abc]Заперечення — не зі списку
[^a-z]Заперечення діапазону
DATA(r) = replace( val = `bit bat but bet` pcre = `b[iu]` with = `#` occ = 0 ). " #t bat #t bet
ВиразЗначення
\A, \ZПочаток / кінець всього тексту
^, $Початок / кінець рядка (у multi-line mode — кожного рядка)
\b, \BМежа слова / не межа
\KСкидає стартову позицію збігу (символи до \K не входять у match)
" Знайти останню кому й усе після неї (\K виключає кому)
FIND PCRE `,(?!.*,)\K.*` IN str MATCH OFFSET DATA(off) MATCH LENGTH DATA(len).

Групи та зворотні посилання

Section titled “Групи та зворотні посилання”
ВиразЗначення
(...)Capturing group
(?<name>...), (?'name'...)Іменована група
(?:...)Non-capturing group
\1, \2Back-reference на n-ту групу
$0, $1, ${name}Посилання в заміні ($0 — увесь збіг)
" Переставити пари: APAB → ABAP
DATA(s) = replace( val = `APAB` pcre = `(..)(..)` with = `$2$1` ).
" Іменовані групи: YYYYMMDD → DD.MM.YYYY
DATA(date) = replace(
val = `20241128`
pcre = `(?<yr>\d{4})(?<mo>\d{2})(?<d>\d{2})`
with = `${d}.${mo}.${yr}` ). " 28.11.2024
ВиразЗначення
(?=...)Позитивний lookahead — далі є патерн
(?!...)Негативний lookahead — далі немає
(?<=...)Позитивний lookbehind — перед є патерн
(?<!...)Негативний lookbehind
" 'c' після пробілу
DATA(r) = replace( val = `ab c abcd` pcre = `(?<=\s)c` with = `#` occ = 0 ). " ab # abcd
" 'a', за якою НЕ йде 'b'
r = replace( val = `abc ade` pcre = `a(?!b)` with = `#` occ = 0 ). " abc #de

Опції та контрольні дієслова

Section titled “Опції та контрольні дієслова”
ОпціяЗначення
(?i)Ignore case
(?m)Multi-line (^/$ на кожному рядку)
(?s)Single-line / dotall (. матчить \n)
(?-x)Вимкнути extended mode
(*UTF)Увімкнути UTF mode
(*CR), (*LF), (*CRLF), (*ANYCRLF)Тип перенесення рядка
" Extended mode: пробіл у патерні ігнорується
DATA(a) = replace( val = `abc def` pcre = `abc def` with = `#` ). " НЕ замінить
DATA(b) = replace( val = `abc def` pcre = `abc\sdef` with = `#` ). " #
DATA(c) = replace( val = `abc def` pcre = `(?-x)abc def` with = `#` ). " #

Заміна регістру в replacement

Section titled “Заміна регістру в replacement”
МаркерДія
\uПерший символ — uppercase
\UУсе — uppercase до \L/\E
\l / \LТе саме для lowercase
\EЗавершити трансформацію
DATA(r) = replace( val = `abcdefg` pcre = `c(.*)` with = `c\U$1` ). " abcDEFG
r = replace( val = `abcdefg` pcre = `c(..)(..)` with = `c\U$1\E$2` ). " abcDEfg

FIND і REPLACE з додатком PCRE. Корисні додатки: ALL OCCURRENCES, MATCH COUNT, RESULTS, SUBMATCHES, MATCH OFFSET/LENGTH, IN TABLE, IGNORING CASE, FROM ... TO ..., REPLACEMENT COUNT.

DATA(str) = `Cathy's black cat on the mat played with Matt.`.
" Кількість літер
FIND ALL OCCURRENCES OF PCRE `[A-Za-z]` IN str MATCH COUNT DATA(n).
" Усі входження з позиціями в таблицю match_result_tab
FIND ALL OCCURRENCES OF PCRE `\s` IN str RESULTS DATA(res).
" SUBMATCHES — витягти групи напряму
FIND PCRE `(.*)\son\s(.*)` IN str IGNORING CASE SUBMATCHES DATA(left) DATA(right).
" REPLACE із підрахунком
REPLACE ALL OCCURRENCES OF PCRE `p.` IN str WITH `#`
REPLACEMENT COUNT DATA(cnt) RESULTS DATA(repl_res).

Пошук у внутрішній таблиці (тип рядка має бути character-like):

DATA(itab) = VALUE string_table( ( `abc` ) ( `def` ) ( `ghi` ) ).
FIND ALL OCCURRENCES OF PCRE `\w` IN TABLE itab RESULTS DATA(tab_res).
REPLACE ALL OCCURRENCES OF PCRE `\d` IN TABLE itab FROM 1 TO 2 WITH `#`.

Вбудовані функції з regex

Section titled “Вбудовані функції з regex”

Приймають параметр pcre замість sub. Параметр occ вказує номер входження (0 — усі для replace).

DATA(text) = `Pieces of cakes.`.
DATA(p1) = find( val = text pcre = `\.` ). " 15
DATA(p2) = find_end( val = text pcre = `\s` ). " 7
DATA(c) = count( val = text pcre = `\s` ). " 2
" match — повертає сам збіг
DATA(email) = match( val = `Write to jon.doe@email.com please`
pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ). " jon.doe@email.com
" substring_* з regex
text = `Lorem ipsum dolor sit amet`.
DATA(sa) = substring_after( val = text pcre = `\s` occ = 2 ). " dolor sit amet
DATA(sb) = substring_before( val = text pcre = `\s` occ = 2 ). " Lorem ipsum
" Предикати
DATA(ok) = xsdbool( matches( val = `jon.doe@email.com`
pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ) ).

Для SQL/CDS — окремі функції (використовують PCRE1 у HANA).

ФункціяПризначення
like_regexpr( pcre = ... value = ... )1/0 — знайдено чи ні
locate_regexpr / locate_regexpr_afterOffset збігу (початок / після)
occurrences_regexprКількість входжень
replace_regexprЗаміна
substring_regexprПідрядок за патерном
SELECT SINGLE
carrid,
url,
like_regexpr( pcre = '\..', value = url ) AS has_dot,
locate_regexpr( pcre = '\..', value = url, occurrence = 2 ) AS pos_2nd,
occurrences_regexpr( pcre = '\..', value = url ) AS n_matches,
replace_regexpr( pcre = '\..', value = url, with = '#' ) AS url_masked,
substring_regexpr( pcre = '\...', value = url ) AS fragment
FROM zdemo_abap_carr
WHERE carrid = 'LH'
INTO @DATA(result).

Для багаторазового використання шаблону або складнішої обробки — об’єктне API.

  • CL_ABAP_REGEX — об’єктне представлення regex. Метод create_pcre створює інстанс із PCRE-шаблоном. Параметри: ignore_case, extended, enable_multiline, table тощо.
  • CL_ABAP_MATCHER — застосовує regex до рядка або таблиці. Методи: find_all, find_next, replace_all, get_match, get_offset, get_length, get_submatch, set_callout.
DATA(str) = `a1 # B2 ? cd . E3`.
" Створити regex і matcher
DATA(regex) = cl_abap_regex=>create_pcre( pattern = `\D\d` ignore_case = abap_true ).
DATA(matcher) = regex->create_matcher( text = str ).
" Або одним викликом
DATA(matcher2) = cl_abap_matcher=>create_pcre( pattern = `\D\d`
text = str
ignore_case = abap_true ).
" Усі збіги
DATA(all) = matcher->find_all( ). " type match_result_tab
" Послідовний обхід через find_next
DATA strtab TYPE string_table.
WHILE matcher2->find_next( ) = abap_true.
DATA(off) = matcher2->get_offset( ).
DATA(len) = matcher2->get_length( ).
APPEND |{ off }:{ len } { str+off(len) }| TO strtab.
ENDWHILE.
" Заміна всіх
DATA(cnt) = matcher2->replace_all( newtext = `#$1#` ).
DATA(out) = matcher2->text.

Пошук у таблиці через matcher:

DATA(tab) = VALUE string_table( ( `abZdez` ) ( `zZfghZ` ) ( `ijkZZz` ) ).
DATA(m) = cl_abap_matcher=>create_pcre( pattern = `z+`
table = tab
ignore_case = abap_true ).
m->replace_all( newtext = `#` ).
DATA(tab_out) = m->table.

Multiline / extended параметри:

" Multi-line: ^ матчить початок кожного рядка
DATA(mm) = cl_abap_matcher=>create_pcre( pattern = `^`
text = |abc\ndef\nghi|
enable_multiline = abap_true ).
mm->replace_all( newtext = `#` ). " #abc\n#def\n#ghi
" Вимкнути extended — пробіли враховуються
DATA(me) = cl_abap_matcher=>create_pcre( pattern = `abc def`
text = `abc def`
extended = abap_false ).
me->replace_all( newtext = `#` ). " #

Коли використовувати що

Section titled “Коли використовувати що”
ЗадачаРішення
Разова перевірка форматуmatches( val = ... pcre = ... )
Перший збіг як рядокmatch( ... )
Заміна всіх входженьreplace( ... occ = 0 ) або REPLACE ALL OCCURRENCES OF PCRE
Позиції всіх збігівFIND ALL OCCURRENCES OF PCRE ... RESULTS ...
Витяг групSUBMATCHES DATA(...) або RESULTS зі submatches
Регулярка в SQL*_regexpr функції ABAP SQL/CDS
Складна обробка з callbacksCL_ABAP_REGEX + CL_ABAP_MATCHER + set_callout
Reuse шаблону в цикліОдин cl_abap_regex, багато create_matcher
ВинятокКоли
CX_SY_INVALID_REGEXНевалідний синтаксис patterns
CX_SY_INVALID_REGEX_FORMATНевалідне посилання в replacement (напр. $2 без групи 2)
CX_SY_REGEX_TOO_COMPLEXНадто складний regex (обмеження рушія)

Адаптовано з 28_Regular_Expressions.md (Apache 2.0).