3 Punkte von GN⁺ 14 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • Reguläre Ausdrücke unterscheiden sich je nach Implementierung in unterstützten Funktionen und Syntax; ein Pattern, das in einem Tool funktioniert, kann in einer anderen Umgebung fehlschlagen oder angepasst werden müssen
  • Je vertrauter man mit funktionsreichen Umgebungen wie Perl ist, desto häufiger stößt man auf Kompatibilitätsprobleme; wenn man auch Computer ohne Installationsrechte berücksichtigt, ist es sicherer, eine gemeinsame Teilmenge zu verwenden
  • Definiert man „überall“ besonders streng, wird der Umfang so klein, dass im Wesentlichen nur Literale, […]-Zeichenklassen und grundlegende Sonderzeichen wie . * ^ $ übrig bleiben
  • Nutzt man GNU sed, awk, grep sowie die Option -E für sed und grep, erweitert sich die nutzbare gemeinsame Funktionsmenge; in dieser Kombination ist awk jedoch meist der kleinste gemeinsame Nenner
  • Emacs benötigt Backslashes vor +? ( ) { } |, und auch die Bedeutung von \s und \S unterscheidet sich; wer denselben regulären Ausdruck in mehreren Tools verwenden will, muss daher auch Syntax-Ausnahmen prüfen

Warum Kompatibilität bei regulären Ausdrücken schwierig ist

  • Die größte Unbequemlichkeit bei regulären Ausdrücken entsteht durch Unterschiede zwischen Implementierungen
    • Eine Funktion, die in einem Tool unterstützt wird, kann in einem anderen Tool komplett fehlen
    • Selbst bei derselben Funktion kann sich die Syntax leicht unterscheiden
  • Perl ist eine Umgebung für reguläre Ausdrücke mit vielen Funktionen; Features, die aus Perl-Sicht selbstverständlich wirken, können in anderen Umgebungen fehlen
  • Man kann zwar Perl-ähnliche Alternativen anderer Tools verwenden, doch das ist nicht standardisiert und erschwert es, Kollegen oder Kunden Code zu schicken, der sofort läuft
  • Berücksichtigt man auch Situationen, in denen man auf Computern arbeiten muss, auf denen keine Software installiert werden kann, braucht man einen Ansatz, der eine Teilmenge von Regex-Funktionen findet, die in mehreren Umgebungen funktioniert
  • Je strenger man „überall“ definiert, desto weniger Funktionen kann man verwenden
    • Literale
    • […]-Zeichenklassen
    • Sonderzeichen . * ^ $

Gemeinsamer Umfang in sed, awk, grep und Emacs

  • Beschränkt man die Ziel-Tools auf sed, awk, grep und Emacs, kann man den Maßstab für „überall“ etwas lockerer ansetzen
  • Verwendet man die GNU-Versionen von sed, awk und grep und nutzt bei sed und grep die Option -E, wird die Liste gemeinsamer Funktionen größer
    • Die Regex-Funktionen der drei Tools sind ähnlich
    • Funktionen von awk werden in den anderen Tools größtenteils ebenfalls unterstützt
    • Eine Ausnahme sind Wortgrenzen: In awk verwendet man \< und \>, was sich von \b und \B unterscheidet
  • Emacs deckt den Großteil der awk-Funktionen ab, weist aber Syntaxunterschiede auf
    • Damit + ? ( ) { } | dieselbe Rolle wie in awk übernehmen, müssen sie mit einem vorangestellten Backslash geschrieben werden
    • Die Entsprechungen zu \s und \S aus awk sind in Emacs \s- und \S-
  • In Emacs stehen \s und \S nicht für Whitespace/Nicht-Whitespace, sondern leiten eine Zeichenklasse ein
    • Die Klasse - bedeutet Whitespace
    • \s. bezeichnet ein Satzzeichen
    • \S. bezeichnet ein Nicht-Satzzeichen
  • Nach diesem Maßstab können folgende Funktionen verwendet werden
    • .
    • ^, $
    • […], [^…]
    • *
    • \w, \W, \s, \S
    • Rückreferenzen von \1 bis \9
    • \b, \B
    • ?, +
    • Alternative mit |
    • Wiederholungsanzahl {n,m}
    • Capturing mit (...)
  • Allerdings unterstützt gawk Rückreferenzen in Ersetzungsstrings, nicht jedoch im regulären Ausdruck selbst
  • look-around kann man als fortgeschrittene Funktion ansehen, und \d wirkt zwar wie eine grundlegende Funktion für Ziffern, wird aber in vielen Regex-Varianten nicht unterstützt

1 Kommentare

 
Meinungen auf Hacker News
  • Bei Emacs fühlt es sich besonders mühsam an, weil man fast raten muss, was escaped werden muss
    Es gibt zwar auch eine Alternative namens rx[0], aber angenehm zu benutzen ist sie in der Praxis nicht
    Über die Regex-Syntax selbst hinaus treten auch bei der tatsächlichen Verwendung häufig Probleme mit Encoding und Escaping auf
    Gibt man eine Regex in der Shell ein, muss man sie korrekt escapen; in Python muss man etwa prüfen, ob es ein Raw String ist
    Trotzdem ist es fast ein modernes Wunder, dass die Art, wie Regexes in den meisten Tools verwendet werden, einigermaßen in einem ähnlichen Rahmen liegt
    [0]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Rx...

    • Noch interessanter wird es, wenn man Python schreibt, das Shell-Skripte mit Regexes erzeugt
      Dann entstehen ständig Situationen, in denen unterschiedliche Escaping-Regeln ineinander verschachtelt sind
    • Wenn man sich krampfhaft einen Vorteil suchen will: Wenn das Suchziel Elisp-Code ist, ist es ein wenig praktisch, dass ( und ) literal matchen
    • Regex hätte eine strukturierte Sprache sein sollen, kein Mischmasch aus mehreren DSLs
  • Der Autor scheint es fast auszusprechen, aber letztlich wohl sagen zu wollen, dass POSIX Basic Regular Expressions überall funktionieren
    Allerdings mit dem Vorbehalt, dass noch nicht alle bei Ausgabe 8 der Single Unix Specification angekommen sind und dass sich BRE in dieser Ausgabe etwas geändert hat

    • Diese Einschätzung wirkt dem Autor gegenüber nicht fair
      Ohne solche Vorbehalte hätte es gar keinen Grund gegeben, den Artikel überhaupt zu schreiben
  • Ich habe früher einmal ein Paper über Regexes geschrieben, die sowohl unter greedy Semantics als auch unter leftmost maximal Semantics auf dieselbe Weise matchen
    https://par.nsf.gov/servlets/purl/10534654

  • Ich war schon immer pingelig dabei, klarzustellen, welche Regex-Sprache ein Tool akzeptiert und ob es beliebige Teilstrings, Präfixe, Suffixe, ganze Strings, eine Zeile oder Teilstrings innerhalb einer Zeile matcht
    Dazu gehören [die verbreiteteren Varianten][1], außerdem PCRE und Python
    Es hat eine Weile gedauert, bis ich gelernt habe, dass einige der älteren Formen, die man etwa bei grep sieht, [in POSIX spezifiziert sind][2]
    [1]: https://cppreference.com/cpp/regex#Regular_expression_gramma...
    [2]: https://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd...

  • Ich möchte Russ Cox’ Regex-Seite teilen
    Ich halte sie für gute, lesenswerte Materialien
    https://swtch.com/~rsc/regexp/

  • Aus solchen Gründen ist RFC 9485, I-Regexp: An Interoperable Regular Expression Format, wichtig
    https://datatracker.ietf.org/doc/html/rfc9485

  • Das regexp-Paket der Go-Standardbibliothek unterstützt keine Backreferences, weil es die RE2-Engine verwendet
    Bei Ersetzungen kann man sie nutzen, beim Matching aber nicht

    • regexp verwendet nicht re2, sondern ist eine separate Implementierung derselben Konzepte
  • Nachdem ich ähnliche Frustrationen mit Regel-Engines, Template-Engines und IFTTT-artigen Engines erlebt hatte, habe ich eine Rust-Bibliothek für JSONLogic gebaut und nutze auch Bindings für andere Sprachen
    https://github.com/GoPlasmatic/datalogic-rs

  • Auch die JSON-Schema-Dokumentation enthält eine empfohlene Regex-Teilmenge
    https://json-schema.org/understanding-json-schema/reference/...