Регулярные выражения - это шаблоны для поиска в строках. Их можно
использовать для поиска, замены в строках или проверки входных данных. ABAP разработчики не очень часто используют данный функционал, хотя иногда это довольно удобно. К сожалению, и сам язык поддерживает далеко не все возможности регулярных выражений.
Нет смысла лишний раз описывать операторы, ознакомится с ним можно в документации.
Рассмотрим несколько простых примеров для поиска на конкретных примерах. Так будет немного понятнее.
Для работы с регулярными выражениями в ABAP есть оператор REGEX.
В первом примере найдем позицию буквы ‘t’. Тут все достаточно просто
1 2 3 4 5 6 7 |
DATA: lv_offset_0 TYPE i. DATA(string_0) = |First regex|. FIND REGEX 't' IN string_0 MATCH OFFSET lv_offset_0. cl_demo_output=>write_data( lv_offset_0 ). cl_demo_output=>display( ). |
Но на практике скорее всего придется искать не заренее известное значение, а некоторое значение из диапазона. Допустим, нам надо найти числа в строке. Это будет выглядеть вот так:
1 2 3 |
DATA lt_result_tab TYPE match_result_tab. DATA(string_1) = |From: sender@gmail.com : summa: 32 summa_2: 55|. FIND ALL OCCURRENCES OF REGEX '[0-9]+' IN string_1 RESULTS lt_result_tab. |
В lt_result_tab получим позиции цифр 32, 2, 55.
Как это работает:
1. [ ] - означает диапазон значений, в нашем случае это от 0 до 9.
2. + - повтор шаблона от 1 раза
a)Регулярное выражение последовательно ищет первый символ, который удовлетворяет первой позиции в шаблоне. "F" - не подходит, "r" - не подходит ...., "3" - подходит.
б) Далее в шаблоне идет «+», который означает, что предыдущий шаблон нужно искать от 1 до бесконечности раз подряд. Значит в следующей позиции опять происходит поиск значения 0-9. Находим следующий символ результата - «2»
в)Переходим к следующему символу в строке – это пробел. «Пробел» не входит в диапазон искомых значений, значит все, что хотели мы нашли
г) Сохраняем результат в таблицу
в)Начинаем поиск заново, с участка строки на котором закончили.
..... Продолжаем итерации пока строка не закончится
Замечание
Но abap не был бы абапом если бы не поддерживал какую то странную версию регулярных выражений. Поэтому шаблон '[0-9]' даст нам такой же результат как и '[0-9]+'. Хотя, по хорошему, должен выдать 3, 2, 2, 3, 3. Скорее всего причина в том, что в ABAP используется только жадный алгоритм.
В предыдущем примере, можно заметить, что двойка это часть слова summa_2 и не является отдельным числом. Давайте найдем только отдельностоящие цифры. Для этого немного усложним выражение
1 |
FIND ALL OCCURRENCES OF REGEX '(\<[0-9]+)' IN string_1 RESULTS lt_result_tab. |
В lt_result_tab получим позиции цифр 32, 55
\< - символ начала слова. Означает, что следующий за ним элемент шаблона будем искать только если слово с него начинается.
Хорошо, а что если числа будут дробные ? Причем, нам надо найти только числа, у которых дробная часть имеет два символа.
1 2 3 |
DATA(string_11) = |summa_1 32.5656 rur, summa_2: 32.56 rur |. FIND ALL OCCURRENCES OF REGEX '(\<[0-9]+\.[0-9]{2,2}\>)' IN string_11 RESULTS result_tab. |
{n} – находит ровно n совпадений указанного элемента
{n,} – находит n и более совпадений указанного элемента
или как в нашем случае {n,m} –от n до m совпадений указанного элемента
Перед точкой можем увидеть знак «\» - это экранирование элемента. Т.к. символ «.» является специальным символом и означает любой символ, а мы ищем именно точку. «\.» - как раз говорит, что ищем символ точка.
\> - найденный символ должен быть концом слова. Что позволяет исключить из выборки 32.5656, например.
Алгоритм:
1. «\<» - говорит, что мы осуществляем поиск элемента шаблона с начала слова:
- Первое слово – «summa_1»;
- Анализируем первый символ. Он должен быть в диапазоне [0-9];
- Слово начинается на «s» и нам не подходит;
- Идем дальше и ищем новое слово по нашему шаблону (шаблон отрабатывает с самого начала);
- Слово «32.5656»;
- Анализируем первый символ. Он должен быть в диапазоне [0-9]. Так и есть;
- «+» - говорит о том, что символ может повторяться от 1 до n раз. Значит второй символ в слове подходит под наш шаблон;
- Переходим к анализу третьего символа в слове «.» Это не цифра – значит сдвигаемся по шаблону на следующее условие, а это у нас «\.» что соответствует «.» в строке;
- Переходим к анализу следующего символа после точки - «5». В шаблоне мы тоже сместились на поиск символа от [0-9]. Пятерка нам подходит;
- Анализируем следующий символ «6». Он тоже должен быть [0-9], т.к. мы прописали, что данный элемент должен повторяться два раза. Но при этом «\>»говорит, что данный элемент должен быть концом слова. В нашем слове следующий элемент «5»;
- Слово под шаблон не подходит;
- Опять пробуем найти совпадение по шаблону с самого начала (Ему удовлетворяет только 32.56 )
Еще немного усложним шаблон, что если мы не знаем, разделяются разряды «.» или «,»
1 2 |
DATA(lv_string_12) = |summa_1 32.5656 rur, summa_2: 32.56 rur; 66,88 rur|. FIND ALL OCCURRENCES OF REGEX '(\<[0-9]+(\.|,)[0-9]{2,2}\>)' IN lv_string_12 RESULTS lt_result_tab. |
В результате получим «32,56», «66,88» .
Алгоритм поиска точно такой же как и в прошлый раз, за тем исключением, что добавился оператор x|y – который ищет или x или y.
Думаю, что в целом, как работают выражения понятно.
Для работы с регулярными выражениями существуют классы CL_ABAP_MATCHER и CL_ABAP_REGEX. Их удобно использовать, например, в условиях.
Пример проверки, что в строке только числа и пробелы.
1 2 3 4 |
IF cl_abap_matcher=>matches( pattern = '^[0-9 ]+$' text = 'input data' ) = abap_true. ENDIF. |
^ - начало входных данных;
$ - конец входных данных;
Означает, что наша шаблон должен от начала и до конца удовлетворять тому, что находится между знаками ^ и $.
Проверка, что введенные данные являются email:
1 2 3 4 |
IF cl_abap_matcher=>matches( pattern = '([a-zA-Z0-9_.+-]+@[a-zA-Z0-9.-]+\.[[a-z]{2,4})' text = 'exaple@mail.ru' ) = abap_true. ENDIF. |
Также можно менять значения в строках
Удаление лишних пробелов:
1 2 3 4 5 6 7 8 9 |
DATA lv_string TYPE string. CONCATENATE '' '' INTO lv_string SEPARATED BY space. DATA(lv_string_3) = |Double spaces. More spaces|. cl_demo_output=>write_data( lv_string_3 ). "Удаление лишних пробелов REPLACE ALL OCCURRENCES OF REGEX '\s{2,}' IN lv_string_3 WITH lv_string. cl_demo_output=>write_data( lv_string_3 ). cl_demo_output=>display( ). |
Алгоритм:
\s - символ пробела;
{2,} –повторение предыдущего символа 2 и более раз подряд.
Один из самых популярных примеров - замена формата даты:
1 2 3 4 5 |
DATA(string_6) = 'First date 2020-01-02. Second date 2020-12-31'. cl_demo_output=>write_data( lv_string_6 ). REPLACE ALL OCCURRENCES OF REGEX '(\d{4})-(\d{2})-(\d{2})' IN lv_string_6 WITH '$3.$2.$1'. cl_demo_output=>write_data( lv_string_6 ). cl_demo_output=>display( ). |
Алгоритм:
\d – любое число;
(x) «круглые скобки» – запоминание x;
$1, $2… - найденные зарегестрированные группы.
Удаление всех символов в начале строки перед From:
1 2 3 4 5 6 |
DATA(lv_string_2) = |for del From: sender@gmail.com : summa: 32 rur|. "Удаление всех символов в начале строки перед From: cl_demo_output=>write_data( lv_string_2 ). REPLACE FIRST OCCURRENCE OF REGEX '^(\C*)(?=From:)' IN lv_string_2 WITH ''. cl_demo_output=>write_data( lv_string_2 ). cl_demo_output=>display( ). |
Все довольно просто, но требуется тренировка. Сделать это можно в программе DEMO_REGEX_TOY
UPD.
Переосмысленные регулярки
Помимо FIND и REPLACE, механизм регулярных выражений реализован в классах CL_ABAP_REGEX и CL_ABAP_MATCHER. Классы позволяют более гибко использовать регулярные выражения. Выражения FIND и REPLACE и методы классов CL_ABAP_REGEX и CL_ABAP_MATCHER хорошо задокументированы в документации SAP.