RegEx verstehen: Wieso matcht "a|b|c" auf "abc"
-
f705185@rppkn.com -
23. März 2021 um 21:03 -
Erledigt
-
-
Hi,
du suchst entweder nach dem Buchstaben a oder nach b oder nach c im Wort abc.
Was hast du denn erwartet und warum?
-
Wie gesagt, ich matche a, nicht .*a.* - offenbar ergänzt Autoit .* auf beiden Seiten Seite automatisch bzw. interpretiert die RegEx als Substring, weil auf "dabc" ebenso die Regex "a" matcht
Was ich erwarte und warum habe ich doch schon beschrieben - mir ist nicht klar, wie ich es anders formulieren soll. Ich versuche es dennoch nochmal
sollte False/0 sein
sollte True/1 sein
-
Was hast du denn erwartet und warum?
Nach meinem Verständnis dürfte das Pattern nur die 3 Strings "a","b" und "c" matchen, ...
Wie vermutlich Andy auch, ist mir nicht ganz klar, worauf Du hinaus willst.
Ein Pattern, welches nur "a", "b" und "c" matched, wäre z.B. :
AutoItLocal $sPattern = "\b(a)\b|\b(b)\b|\b(c)\b" ConsoleWrite('"a" = ' & StringRegExp("a", $sPattern) & @CRLF) ConsoleWrite('"b" = ' & StringRegExp("b", $sPattern) & @CRLF) ConsoleWrite('"c" = ' & StringRegExp("c", $sPattern) & @CRLF) ConsoleWrite('"abc" = ' & StringRegExp("abc", $sPattern) & @CRLF)
Konsole :
"a" = 1
"b" = 1
"c" = 1
"abc"= 0
-
Da haben sich unsere Beiträge wohl überschnitten :
-
Das ist für mich extrem unintuitiv. Warum muss ich das angeben, was ich nicht matchen will ("alles, was kein Wort trennt")?
Nach meinem Verständnis von RegEx sollte man explizit angeben, was man matcht und nicht, was man nicht matcht. Will sagen: Was ich in meinem Pattern nicht angebe, soll auch nicht da sein. \b vor und nach jedes Zeichen (bzw. in meinem Fall zu machtende Wort) zu setzen macht den Code extrem unleserlich und ich kann beim besten Willen nicht verstehen, was sich ein Sprachdesigner dabei gedacht hat. Schließlich ist es StringRegEx und nicht SubStringRegEx.
Immerhin geht "\A(a|b|c)\Z". Ist zwar hässlich und wird mich irgendwann zwingen \A und \Z nachzuschlagen, wenn ich den Code überarbeite, aber scheinbar muss das in Autoit dann so. Danke. -
Hi,
ich verstehe deine Interpretation und warum das aus deiner Sicht unlogisch erscheint. Allerdings haben reguläre Ausdrücke verschiedene Anwendungsfälle. Im Gegensatz zu deinem Beispiel möchte man häufig eben nicht nur prüfen, ob ein gesamter String einem Muster entspricht (Ja-Nein-Entscheidung), sondern aus einem String unbekannte Teile herausfiltern / suchen.
Kurzes Beispiel:
Aus "Wir treffen uns morgen um 13:35 Uhr am Bahnhof." soll die Uhrzeit herausgefiltert werden. Dann ergibt es natürlich Sinn, dass mein Pattern nur den wesentlichen Teil beinhalten muss, nämlich (ganz vereinfacht) "\d{2}:\d{2}". Alles andere würde hier zu Unleserlichkeit führen. Damit hast du genau dein gewünschtes Verhalten:
Nach meinem Verständnis von RegEx sollte man explizit angeben, was man matcht und nicht, was man nicht matcht.
Besonders bei den Flags 1-4 ergibt diese Substring-Suche deutlich mehr Sinn. Und wie du auch selbst bemerkt hast, ist deine gewünschte Funktionalität ebenfalls umsetzbar, man muss halt nur String-Anfang und -Ende explizit angeben:
Immerhin geht "\A(a|b|c)\Z". Ist zwar hässlich und wird mich irgendwann zwingen \A und \Z nachzuschlagen, wenn ich den Code überarbeite, aber scheinbar muss das in Autoit dann so. Danke.
Kleine Off-Topic-Anmerkung dazu: Dieses "hässliche" Verhalten ist nicht nur AutoIt-spezifisch, das hat sich unter anderem auch in PHP oder Linux bewährt.
Viele Grüße
Xenon
-
An dem was Xenon schreibt, ist vieles dran.
Eine weniger minimalistische Beschreibung Deiner Anforderung hätte auch nicht geschadet.
ich kann beim besten Willen nicht verstehen, was sich ein Sprachdesigner dabei gedacht hat. Schließlich ist es StringRegEx und nicht SubStringRegEx.
Es gibt keine begründbare Verpflichtung, das Problem ausschließlich mit einem Regulären Ausdruck anzugehen. Häufig sind die vorhandenen Stringfunktionen, ggf. gemischt mit kurzen, nachvollziehbaren RegEx-Elementen, nicht die schlechtere Wahl. Der Code wird damit zwar etwas länger, bleibt aber trotzdem verständlich.
-
Immerhin geht "\A(a|b|c)\Z". Ist zwar hässlich und wird mich irgendwann zwingen \A und \Z nachzuschlagen, wenn ich den Code überarbeite,
Wenn es eventuell intuitiver für dich ist dann würde auch ^ als Stringanfang und $ als Stringende gehen (solange du nicht irgendwo ein (?m) einbaust): ^[abc]$
aber scheinbar muss das in Autoit dann so. Danke.
Kommst du zufällig aus dem Java, C++, oder Python-Bereich?
Bei Java wird halt explizit zwischen .matches und .find unterschieden.
Auch C++ und Python verhält sich so (match() vs. search()).Diese haben da halt eine explizite Unterscheidung getroffen. Die meisten anderen regex-Implementierungen (PHP, C#, vim, sed...) hingegen verhalten sich hingegen genauso wie AutoIt, welches im Grunde nur die weit verbreitete PCRE-Engine anbietet.
Was nun intuitiv ist oder nicht hängt dann halt schlicht nur davon ab mit welchem Konzept du zuerst in Berührung gekommen bist.
Um die Frage zu klären was denn korrekt wäre sage ich nur dass bereits Kenneth Thompsons qed-Implementierung aus den 60ern sich bereits so verhalten hat wie AutoIt heute auch schon
Wenn dich das jedoch dermaßen stört kannst du dir jedoch einfach eine entsprechende Wrapper-Funktion erstellen:
AutoItConsoleWrite(RegExWholeString("a", "a|b|c")) Func RegExWholeString($sString, $sPattern) Return StringRegExp($sString, "\A(" & $sPattern & ")\z") EndFunc
Und um jetzt ganz gemein zu werden muss ich dir leider mitteilen, dass auch deine Lösung \A(a|b|c)\Z nicht in jedem Fall das macht was du erhoffst.
Nämlich dann wenn noch ein Zeilenumbruch am Ende des Strings steht. \Z stört das nicht und matcht trotzdem.
Um das Verhalten zu umgehen müsstest du ein \z statt einem \Z verwenden: StringRegExp("a" & @CRLF, "\Aa\z") -
Danke für die Erklärungen. Das mit \Z vs \z ist mir klar geworden aus der Hilfe, aber ein guter und wichtiger Hinweis. Mich wundert btw, dass es sich mit \A und \a nicht analog verhält.
Durch die Perspektive search vs. match wird es wesentlich verständlicher, warum das so gebaut ist. -
Mich wundert btw, dass es sich mit \A und \a nicht analog verhält.
Naja das eine Datei auf ein Zeilenumbruchzeichen endet kommt halt ziemlich häufig vor.
Z.B. bei Log-Dateien wo jede Info zeilenweise rausgeschrieben wird.
Daher macht es schon Sinn einen entsprechenden Shortcut für diesen Fall einzubauen (eben das \Z).
Das eine Datei hingegen mit einem Zeilenumbruch anfängt ist eher atypisch und daher hat man sicherlich auf einen derartigen Shortcut verzichtet da man stattdessen einfach nur ein \R? voranstellen müsste und man hat den selben Effekt.
Oder meintest du, dass es einer entsprechenden Logik folgend stattdessen \a anstatt \A heißen müsste?