Regex, das mit der längeren Zeichenfolge in einem OR übereinstimmt

9

Motivation

Ich analysiere Adressen und muss die Adresse und das Land in getrennten Übereinstimmungen erhalten, aber die Länder können Aliase haben, z. B .:

%Vor%

und so weiter ...

Erklärung

Also, was ich tue, ist eine große Regex mit allen möglichen Ländernamen (zumindest die, die wahrscheinlicher erscheinen) getrennt durch den ODER-Operator, wie folgt zu erstellen:

%Vor%

Aber das Problem ist mit mehrsprachigen Ländernamen und ihren kürzeren Versionen, wie:

Republic of Moldova und Moldova

Als Beispiel verwenden wir die Zeichenfolge:

%Vor%

Was ich davon bekommen möchte:

%Vor%

Aber das ist was ich bekomme:

%Vor%

Regex

Da es mehrere Fälle gibt, hier ist, was ich bis jetzt benutze:

%Vor%

Da wir Fax, Telefon, Postleitzahl oder etwas anderes nach dem Namen des Landes haben - was mich nicht interessiert - benutze ich die letzte passende Gruppe, um sie zu entfernen:

%Vor%

Manchmal kommt der Name des Landes auch in Klammern, also habe ich \(? und \)? um die zweite Match-Gruppe und alle Länder gehen hinein:

%Vor%

Frage

Die Sache ist, wenn es einen Eintrag gibt, der eine Untermenge eines größeren ist, wird der kürzere über den längeren gewählt, und der Rest bleibt in der base_address string. Gibt es eine Möglichkeit, die Regex über die größtmögliche Übereinstimmung zu entscheiden, wenn zwei Werte mach?

Bearbeiten

  1. Ich benutze Python mit eingebautem re Modul
  2. Wie von m.buettner vorgeschlagen, wird durch das Ändern der ersten übereinstimmenden Gruppe von (.*) in (.*?) das aktuelle Problem zwar behoben, aber es wird auch ein neues erstellt. Betrachten Sie ein anderes Beispiel:

    'Department of Chemistry, Nationale Universität von Singapur, 4512436 Singapur'

Treffer:

%Vor%

Hier passt es jetzt zu früh.

    
alfetopito 18.05.2013, 00:00
quelle

2 Antworten

6

Ihr Problem ist Gier .

Das .* versucht zu Beginn so viel wie möglich zu erreichen. Das ist alles bis zum Ende der Saite. Aber dann schlägt der Rest deines Musters fehl. Die Engine rückt also zurück und verwirft das zuletzt mit .* übereinstimmende Zeichen und versucht den Rest des Musters erneut (was immer noch fehlschlägt). Die Engine wird diesen Prozess wiederholen (fehlgeschlagene Übereinstimmung, Zurückverfolgung / Verwerfen eines Zeichens, erneutes Versuchen), bis sie schließlich mit dem Rest des Musters übereinstimmen kann. Dies geschieht zum ersten Mal, wenn .* bis zu Moldova mit allem übereinstimmt (also verbraucht .* immer noch Republic of ). Und dann passt die Alternation (die immer noch nicht mit republic of moldova übereinstimmen kann) moldova und gibt das als Ergebnis zurück.

Die einfachste Lösung ist, die Wiederholung nicht gierig zu machen:

%Vor%

Beachten Sie, dass das Fragezeichen direkt nach einem Quantifizierer nicht "optional" bedeutet, sondern "ungegerecht". Dies kehrt das Verhalten einfach um: Die Engine versucht zunächst,% code_% vollständig wegzulassen, und während des Zurückverfolgens enthält sie nach jedem fehlgeschlagenen Versuch, den Rest des Musters abzugleichen, ein weiteres Zeichen.

BEARBEITEN:

Es gibt normalerweise bessere Alternativen zur Ungerechtigkeit. Wie Sie in einem Kommentar gesagt haben, bringt die ungarige Lösung ein weiteres Problem mit sich, dass Länder in früheren Teilen der Zeichenkette übereinstimmen könnten. Stattdessen können Sie lookarounds verwenden, um sicherzustellen, dass keine Wortzeichen (Buchstaben, Ziffern, Unterstriche) vorhanden sind ) vor oder nach dem Land. Das heißt, ein Länderwort wird nur dann gefunden, wenn es von Kommas oder einem Ende der Zeichenfolge umgeben ist:

%Vor%

Da die Blickwinkel nicht wirklich Teil des Spiels sind, stören sie nicht den Rest des Musters - sie überprüfen lediglich eine Bedingung an einer bestimmten Position im Spiel. Die zwei Lookarounds, die ich hinzugefügt habe, stellen sicher, dass:

  1. Es gibt kein Wortzeichen vor dem Pflichtfeld vor dem Land.
  2. Es gibt kein Wortzeichen nach dem Land, das durch nichts als Leerzeichen getrennt ist.

Beachten Sie, dass ich Leerzeichen in eine Zeichenklasse sowie die wörtlichen Klammern eingeschlossen habe (anstatt sie zu umgehen). Weder ist notwendig, aber ich bevorzuge diese Lesbarkeit, also sind sie nur ein Vorschlag.

EDIT 2:

Wie in einem Kommentar erwähnt, wie wäre es mit einer Regex-only-Lösung?

Sie könnten die Zeichenfolge in .* aufteilen, dann jedes Ergebnis abschneiden und diese mit Ihrer Länderliste vergleichen (möglicherweise mit Regex). Wenn eine Komponente Ihrer Adresse mit einem Ihrer Länder übereinstimmt, können Sie diese zurückgeben. Wenn es mehrere gibt, können Sie zumindest die Mehrdeutigkeit erkennen und richtig damit umgehen.

    
Martin Ender 18.05.2013, 00:18
quelle
0

Sortiere alle Alternativen in Regex, erzeuge Regex programmatisch nach sortierten (vom längsten bis kürzesten) Array von Namen. Dann machen Sie den ganzen Regex in der Atomgruppe (PCRE-Engine hat es, weiß nicht, ob RE Engine es auch hat). Wegen der atomaren Gruppe, Regex-Engine nie zurückzutreten, um andere Alternative in Atomgruppe zu versuchen, und so haben Sie alle Alternativen sortiert, Übereinstimmung wird immer die längste sein.

Tada.

    
ElSajko 25.07.2017 11:57
quelle

Tags und Links