3 Punkte von GN⁺ 2024-03-21 | 1 Kommentare | Auf WhatsApp teilen

Verständnis des Verhaltens des Zeichens $ in regulären Ausdrücken in Python

  • Bei Verwendung des re-Moduls von Python ist allgemein bekannt, dass ^ den „Anfang des Strings“ bedeutet und $ das „Ende des Strings“.
  • Allerdings bedeutet $ nicht immer ausschließlich das „Ende des Strings“, und das Verhalten kann je nach Plattform unterschiedlich sein.
  • In Python kann das Zeichen $, wenn der Multiline-Modus deaktiviert ist, mit dem Ende des Strings oder mit dem Zeilenumbruch vor dem Stringende übereinstimmen.

Unterschied zwischen Stringende und Übereinstimmung mit Zeilenumbruch

  • Wenn der Multiline-Modus deaktiviert ist, reicht in Python die Verwendung von $ allein nicht aus, um ohne Zeilenumbruch genau das Ende des Strings zu treffen.
  • Um das Ende des Strings abzugleichen, können \z und \Z verwendet werden.
  • Wenn in Python re.MULTILINE verwendet wird, stimmt $ sowohl mit dem Ende des Strings als auch mit dem Ende jeder Zeile überein, also unmittelbar vor dem Zeilenumbruch.

Vergleich des Verhaltens regulärer Ausdrücke auf verschiedenen Plattformen

  • Eine Tabelle, die das Pattern-Matching für "cat\n" auf mehreren Plattformen vergleicht, zeigt: Wenn eine Übereinstimmung einschließlich des Zeilenumbruchs erlaubt ist, funktioniert die Verwendung von $ im Multiline-Modus konsistent.
  • Wenn ohne Einbeziehung des Zeilenumbruchs abgeglichen werden soll, sollte auf allen Plattformen außer Python und ECMAScript \z verwendet werden; in Python und ECMAScript jeweils \Z oder $ ohne Multiline-Modus.

Meinung von GN⁺

  • Dieser Artikel kann Entwickler, die reguläre Ausdrücke verwenden, auf das unerwartete Verhalten des Zeichens $ in Python aufmerksam machen.
  • Reguläre Ausdrücke sind bei der String-Verarbeitung sehr mächtig, doch das Verhalten kann sich je nach Plattform unterscheiden, weshalb Vorsicht geboten ist.
  • Entwickler sollten sich dieser Unterschiede bewusst sein und zusätzliche Tests durchführen, um Kompatibilitätsprobleme bei der Entwicklung plattformübergreifender Anwendungen zu vermeiden.
  • Andere Bibliotheken für reguläre Ausdrücke mit ähnlicher Funktionalität sind etwa Javas java.util.regex und .NETs System.Text.RegularExpressions; auch bei ihnen sollte man die plattformspezifischen Verhaltensunterschiede verstehen und berücksichtigen.
  • Bei der Einführung neuer Syntax oder neuen Verhaltens in regulären Ausdrücken sollten die Kompatibilität mit bestehendem Code, Performance-Auswirkungen und die Lernkurve im Team berücksichtigt werden; die daraus entstehenden Vorteile und Kosten sollten sorgfältig abgewogen werden.

1 Kommentare

 
GN⁺ 2024-03-21
Hacker-News-Kommentare
  • Wer mit regulären Ausdrücken vertraut ist, weiß, dass ^ den "Anfang der Zeichenkette" und $ das "Ende der Zeichenkette" bedeutet. Ich persönlich denke dabei aber eher an den "Anfang der Zeile" und das "Ende der Zeile". In den meisten Fällen ist das Ergebnis dasselbe, weil man Text meist zeilenweise verarbeitet, aber meine Sicht auf diese Operatoren ändert sich dadurch nicht. Vermutlich liegt das daran, dass ich reguläre Ausdrücke zuerst über grep kennengelernt habe und Eingaben daher vor allem als "Zeilen" betrachte.

    • POSIX-reguläre Ausdrücke und Python-reguläre Ausdrücke sind unterschiedlich. Im Allgemeinen sollte man die Dokumentation der Regex-Implementierung konsultieren, die man verwendet; die Syntax ist nicht universell.
    • Laut POSIX Kapitel 9 stehen reguläre Ausdrücke im Allgemeinen im Zusammenhang mit Textverarbeitung und arbeiten auf NUL-terminierten Strings, bei denen NUL das Ende der Zeichenkette markiert. Einige Utilities beschränken die Verarbeitung auf Zeilen. $ kann dem Ende eines Strings oder dem Ende einer Zeile entsprechen; das ist je nach Utility (oder Modus) definiert. Die meisten gängigen Utilities (grep, sed, awk, Python usw.) behandeln es standardmäßig als Zeilenende.
    • Es gibt keine einzelne universelle Regex-Syntax. Wenn man die Sprache und die Optionen nicht kennt, kann man reguläre Ausdrücke nicht zuverlässig lesen oder schreiben.
  • Die perfekte Gelegenheit, Robert Elder vorzustellen. Er macht YouTube- und Blog-Inhalte und hat eine Serie über reguläre Ausdrücke, in der er tief in die Verhaltensunterschiede verschiedener Tools eintaucht.

    • Auch sein neuester Inhalt ist großartig: https://www.youtube.com/watch?v=ys7yUyyQA-Y
    • Er hat viele Inhalte, die HN-Nutzer interessant finden dürften, zum Beispiel über die Realität und die Mühen des Consultings.
  • Reguläre Ausdrücke waren eines der ersten Dinge, die ich wirklich verinnerlicht habe, als ich Perl zum ersten Mal lernte. (Perl hat dank des "Camel"-Buchs noch immer einen warmen Platz in meinem Herzen.)

    • Die wichtigste Erkenntnis heute ist, zu wissen, dass Implementierungen unterschiedlich sind, und sich anzugewöhnen, die Referenzdokumentation für das, womit man arbeitet, heranzuziehen.
    • Zum Beispiel verwenden Emacs-Reguläre-Ausdrücke \s_- statt \w (oder irgendetwas, das ohne Referenzdokumentation auf dem Bildschirm erscheint) als Zeichenklasse, aber Emacs hat die beste Dokumentation und Auffindbarkeit.
    • Einige Utilities verlangen Escaping bei Klammern, andere nicht. Manchmal ist dieses Verhalten konfigurierbar und manchmal nicht.
    • Ich habe alle Phasen von Verwirrung, Frust und Verleugnung durchlaufen und akzeptiere es inzwischen einfach. Das Konzept ist überall gleich, aber die Ausprägung variiert.
  • Ich kann schon hören, wie schlechte Hiring-Manager "Wie matcht man in regulären Ausdrücken das Ende einer Zeichenkette?" zu ihrer Liste von "Ha! Du kennst den Trick nicht!"-Fragen hinzufügen.

  • Es ist seltsam, Perl bei regulären Ausdrücken aus der Liste wegzulassen.

    • In der perlre-Dokumentation wird $ so erklärt: Matcht das Ende der Zeichenkette (oder vor dem Newline-Zeichen am Ende der Zeichenkette; oder vor jedem Newline-Zeichen, wenn /m verwendet wird)
  • Raku (früher Perl 6) verwendet ^ und $ für den Anfang und das Ende einer Zeichenkette und führt ^^ und $$ für den Anfang und das Ende einer Zeile ein. Ein Multiline-Modus ist weder verfügbar noch nötig.

    • Einer der Vorteile einer vollständigen Neugestaltung/Neuimplementierung ist, dass man aus der Tatsache lernen kann, dass das frühere Verhalten Menschen überrascht hat.
  • Gibt es wirklich Leute, die denken, reguläre Ausdrücke seien standardisiert? In einen neuen Kontext zu wechseln bedeutet immer, neu lernen zu müssen.

  • Es gibt Verwirrung zwischen Strings und Zeilen. Ein String ist eine Folge von Zeichen, und eine Zeile kann zwei verschiedene Dinge sein. Wenn man das Newline-Zeichen als Zeilenabschluss betrachtet, ist eine Zeile eine Folge von Nicht-Newline-Zeichen einschließlich des Newline-Zeichens. Wenn es kein Newline gibt, ist es keine vollständige Zeile. So verwendet es POSIX. Wenn man das Newline-Zeichen als Zeilentrenner betrachtet, ist eine Zeile eine Folge von Nicht-Newline-Zeichen. In beiden Fällen endet der Inhalt der Zeile vor dem Newline-Zeichen, entweder weil es die Zeile beendet oder weil es sie von der nächsten Zeile trennt.

    • Die Bedeutung von ^ und $ basiert auf Zeilen – unabhängig davon, ob es ein Singleline- oder Multiline-Modus ist. Für stringbasierte Bedeutung – wenn man etwa bei Dateien an die gesamte Datei denkt – verwendet man \A und \Z oder das jeweilige Äquivalent.
  • Das hat in Ruby-basierten Apps zu einigen schwerwiegenden Bugs geführt. Ich verwende immer \A\z.