ABAP Regular Expressions: Muster suchen und ersetzen

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Regular Expressions (Regex) ermöglichen komplexe Mustersuche und -ersetzung in Strings. ABAP unterstützt POSIX-kompatible reguläre Ausdrücke über Statements und Klassen.

Verwendungsmöglichkeiten

  1. FIND … REGEX – Muster in String suchen
  2. REPLACE … REGEX – Muster ersetzen
  3. matches() – Prüfen ob String dem Muster entspricht
  4. cl_abap_regex / cl_abap_matcher – Objektorientierte API

Regex-Syntax Übersicht

ZeichenBedeutungBeispiel
.Beliebiges Zeichena.c → abc, aXc
*0 oder mehrab*c → ac, abc, abbc
+1 oder mehrab+c → abc, abbc
?0 oder 1ab?c → ac, abc
^Anfang^Hello
$EndeWorld$
[abc]Zeichenklasse[aeiou] → Vokale
[^abc]Negierte Klasse[^0-9] → Nicht-Ziffern
[a-z]Bereich[A-Za-z] → Buchstaben
\dZiffer [0-9]\d{4} → 4 Ziffern
\wWortzeichen [a-zA-Z0-9_]\w+
\sWhitespace\s+ → Leerzeichen
\bWortgrenze\bword\b
{n}Genau n-mala{3} → aaa
{n,m}n bis m-mala{2,4} → aa, aaa, aaaa
(...)Gruppe(ab)+ → ab, abab
|Odercat|dog

Beispiele

1. FIND mit REGEX

DATA: lv_text TYPE string VALUE 'Bestellung 12345 vom 2024-11-15'.
" Zahl finden
FIND REGEX '\d+' IN lv_text MATCH OFFSET DATA(lv_offset)
MATCH LENGTH DATA(lv_length).
IF sy-subrc = 0.
DATA(lv_number) = substring( val = lv_text off = lv_offset len = lv_length ).
WRITE: / 'Gefunden:', lv_number. " 12345
ENDIF.

2. Alle Treffer finden (FIND ALL OCCURRENCES)

DATA: lv_text TYPE string VALUE 'Tel: 030-12345, Fax: 040-67890, Mobil: 0170-9876543'.
" Alle Telefonnummern finden
FIND ALL OCCURRENCES OF REGEX '\d{3,4}-\d+'
IN lv_text
RESULTS DATA(lt_results).
LOOP AT lt_results INTO DATA(ls_result).
DATA(lv_phone) = substring( val = lv_text
off = ls_result-offset
len = ls_result-length ).
WRITE: / 'Telefon:', lv_phone.
ENDLOOP.
" Ausgabe:
" Telefon: 030-12345
" Telefon: 040-67890
" Telefon: 0170-9876543

3. Gruppen extrahieren (Submatches)

DATA: lv_date TYPE string VALUE 'Datum: 2024-11-15'.
" Datum mit Gruppen extrahieren
FIND REGEX '(\d{4})-(\d{2})-(\d{2})'
IN lv_date
SUBMATCHES DATA(lv_year) DATA(lv_month) DATA(lv_day).
IF sy-subrc = 0.
WRITE: / 'Jahr:', lv_year. " 2024
WRITE: / 'Monat:', lv_month. " 11
WRITE: / 'Tag:', lv_day. " 15
ENDIF.

4. REPLACE mit REGEX

DATA: lv_text TYPE string VALUE 'Preis: 123.45 EUR, Rabatt: 10.00 EUR'.
" Zahlen durch XXX ersetzen
REPLACE ALL OCCURRENCES OF REGEX '\d+\.?\d*'
IN lv_text WITH 'XXX'.
WRITE: / lv_text. " Preis: XXX EUR, Rabatt: XXX EUR

5. Rückwärtsreferenzen

DATA: lv_text TYPE string VALUE 'Die die Katze sitzt auf auf dem Dach.'.
" Doppelte Wörter entfernen (Rückwärtsreferenz \1)
REPLACE ALL OCCURRENCES OF REGEX '\b(\w+)\s+\1\b'
IN lv_text WITH '$1'.
WRITE: / lv_text. " Die Katze sitzt auf dem Dach.

6. matches() – Prüfen ob Pattern passt

DATA: lv_email TYPE string VALUE 'test@example.com'.
" Einfache E-Mail-Validierung
IF matches( val = lv_email regex = '^\w+@\w+\.\w+$' ).
WRITE: / 'Gültige E-Mail'.
ELSE.
WRITE: / 'Ungültige E-Mail'.
ENDIF.
" Mehrere Prüfungen
DATA: lv_phone TYPE string VALUE '+49-170-1234567'.
DATA(lv_valid_phone) = xsdbool(
matches( val = lv_phone regex = '^\+?\d{2,3}-\d{2,4}-\d{4,}$' )
).

7. contains() mit Regex

DATA: lv_text TYPE string VALUE 'Bestellung Nr. 12345 wurde versandt'.
" Enthält Zahl?
IF contains( val = lv_text regex = '\d+' ).
WRITE: / 'Text enthält Zahlen'.
ENDIF.
" Startet mit Muster
IF contains( val = lv_text regex = '^Bestellung' ).
WRITE: / 'Ist eine Bestellung'.
ENDIF.

8. count() mit Regex

DATA: lv_text TYPE string VALUE 'a1b2c3d4e5'.
" Anzahl der Ziffern
DATA(lv_digit_count) = count( val = lv_text regex = '\d' ).
WRITE: / 'Anzahl Ziffern:', lv_digit_count. " 5
" Anzahl der Wörter
DATA: lv_sentence TYPE string VALUE 'Dies ist ein Beispielsatz mit Wörtern'.
DATA(lv_word_count) = count( val = lv_sentence regex = '\b\w+\b' ).
WRITE: / 'Anzahl Wörter:', lv_word_count. " 6

9. cl_abap_regex – Objektorientiert

DATA: lv_text TYPE string VALUE 'Name: Max Mustermann, Alter: 30'.
" Regex-Objekt erstellen
DATA(lo_regex) = cl_abap_regex=>create_pcre( pattern = '(\w+):\s*(\S+)' ).
" Matcher erstellen
DATA(lo_matcher) = lo_regex->create_matcher( text = lv_text ).
" Alle Treffer durchlaufen
WHILE lo_matcher->find_next( ).
DATA(lv_full) = lo_matcher->get_match( ).
DATA(lv_key) = lo_matcher->get_submatch( 1 ).
DATA(lv_value) = lo_matcher->get_submatch( 2 ).
WRITE: / 'Match:', lv_full.
WRITE: / ' Key:', lv_key, 'Value:', lv_value.
ENDWHILE.
" Ausgabe:
" Match: Name: Max
" Key: Name Value: Max
" Match: Alter: 30
" Key: Alter Value: 30

10. Praktisch: E-Mail extrahieren

DATA: lv_text TYPE string VALUE
'Kontakt: info@firma.de, support@firma.de oder sales@firma.de'.
DATA: lt_emails TYPE string_table.
" Alle E-Mails finden
FIND ALL OCCURRENCES OF REGEX '[\w.+-]+@[\w.-]+\.\w{2,}'
IN lv_text
RESULTS DATA(lt_results).
LOOP AT lt_results INTO DATA(ls_result).
APPEND substring( val = lv_text
off = ls_result-offset
len = ls_result-length ) TO lt_emails.
ENDLOOP.
LOOP AT lt_emails INTO DATA(lv_email).
WRITE: / lv_email.
ENDLOOP.
" Ausgabe:
" info@firma.de
" support@firma.de
" sales@firma.de

11. Praktisch: Daten bereinigen

" Telefonnummer normalisieren
DATA: lv_phone TYPE string VALUE '+49 (0) 170 / 123 45 67'.
" Alle Nicht-Ziffern außer + entfernen
REPLACE ALL OCCURRENCES OF REGEX '[^\d+]' IN lv_phone WITH ''.
WRITE: / lv_phone. " +491701234567
" Mehrfache Leerzeichen reduzieren
DATA: lv_text TYPE string VALUE 'Zu viele Leerzeichen hier'.
REPLACE ALL OCCURRENCES OF REGEX '\s{2,}' IN lv_text WITH ' '.
WRITE: / lv_text. " Zu viele Leerzeichen hier

12. Praktisch: Validierungen

" IBAN validieren (vereinfacht)
DATA: lv_iban TYPE string VALUE 'DE89370400440532013000'.
IF matches( val = lv_iban regex = '^[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}([A-Z0-9]?){0,16}$' ).
WRITE: / 'Gültiges IBAN-Format'.
ENDIF.
" PLZ validieren (Deutschland)
DATA: lv_plz TYPE string VALUE '12345'.
IF matches( val = lv_plz regex = '^\d{5}$' ).
WRITE: / 'Gültige deutsche PLZ'.
ENDIF.
" Datum validieren (YYYY-MM-DD)
DATA: lv_date TYPE string VALUE '2024-11-15'.
IF matches( val = lv_date regex = '^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$' ).
WRITE: / 'Gültiges Datumsformat'.
ENDIF.

13. Praktisch: Parsing

" Log-Zeile parsen
DATA: lv_log TYPE string VALUE '2024-11-15 10:30:45 [ERROR] Database connection failed'.
FIND REGEX '^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})\s+\[(\w+)\]\s+(.+)$'
IN lv_log
SUBMATCHES DATA(lv_date) DATA(lv_time) DATA(lv_level) DATA(lv_message).
IF sy-subrc = 0.
WRITE: / 'Datum:', lv_date.
WRITE: / 'Zeit:', lv_time.
WRITE: / 'Level:', lv_level.
WRITE: / 'Nachricht:', lv_message.
ENDIF.

14. Praktisch: CSV-Parsing

DATA: lv_csv TYPE string VALUE 'Max;Mustermann;30;Berlin'.
DATA: lt_fields TYPE string_table.
" Split by Semikolon (alternative zu SPLIT)
FIND ALL OCCURRENCES OF REGEX '[^;]+' IN lv_csv RESULTS DATA(lt_matches).
LOOP AT lt_matches INTO DATA(ls_match).
APPEND substring( val = lv_csv off = ls_match-offset len = ls_match-length )
TO lt_fields.
ENDLOOP.
" Oder einfacher mit SPLIT:
SPLIT lv_csv AT ';' INTO TABLE lt_fields.

15. Case-Insensitive Suche

DATA: lv_text TYPE string VALUE 'ABAP ist super, abap ist toll'.
" Case-insensitive mit (?i)
FIND ALL OCCURRENCES OF REGEX '(?i)abap'
IN lv_text
MATCH COUNT DATA(lv_count).
WRITE: / 'Gefunden:', lv_count, 'mal'. " 2

16. Escape-Funktion

DATA: lv_search TYPE string VALUE 'a.b*c?'.
" Sonderzeichen escapen für literale Suche
DATA(lv_escaped) = escape( val = lv_search format = cl_abap_format=>e_regex ).
WRITE: / 'Escaped:', lv_escaped. " a\.b\*c\?
" Jetzt kann lv_escaped in REGEX verwendet werden
DATA: lv_text TYPE string VALUE 'Test a.b*c? Ende'.
FIND REGEX lv_escaped IN lv_text.
IF sy-subrc = 0.
WRITE: / 'Gefunden!'.
ENDIF.

Häufige Regex-Patterns

ZweckPattern
Zahl\d+ oder [0-9]+
Dezimalzahl\d+\.?\d*
Wort\w+ oder [A-Za-z]+
E-Mail (einfach)[\w.+-]+@[\w.-]+\.\w{2,}
URLhttps?://[\w./%-]+
Datum (YYYY-MM-DD)\d{4}-\d{2}-\d{2}
PLZ (DE)\d{5}
Telefon (DE)(\+49|0)\d{2,4}[-/]?\d+
IP-Adresse\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
Leerzeichen entfernen\s+ → “
HTML-Tags<[^>]+>

FIND/REPLACE Optionen

FIND REGEX pattern IN text
[ IGNORING CASE ] " Groß-/Kleinschreibung ignorieren
[ MATCH OFFSET off ] " Startposition des Treffers
[ MATCH LENGTH len ] " Länge des Treffers
[ MATCH COUNT cnt ] " Anzahl der Treffer
[ SUBMATCHES s1 s2 ... ] " Gruppeninhalte
[ RESULTS result_tab ]. " Alle Treffer als Tabelle

Wichtige Hinweise / Best Practice

  • PCRE-Syntax (Perl-kompatibel) mit cl_abap_regex=>create_pcre().
  • Standard ABAP Regex ist POSIX-kompatibel.
  • escape() verwenden um Sonderzeichen zu escapen.
  • Submatches mit (...) für Gruppenextraktion.
  • (?i) am Anfang für case-insensitive Suche.
  • Performance: Kompilierte Regex (cl_abap_regex) bei Mehrfachverwendung.
  • matches() prüft ob gesamter String dem Muster entspricht.
  • contains( ... regex = ...) prüft ob Muster enthalten ist.
  • \d, \w, \s sind Kurzformen für Zeichenklassen.
  • Testen Sie Regex mit Online-Tools (regex101.com) vor der Implementierung.