2017-01-30 22 views
3

Ich habe eine Reihe von Schlüsselwörtern. Jedes Schlüsselwort kann ein Leerzeichen ['one', 'one two'] enthalten. Ich erzeuge eine Regexp von diesen Kyewords wie diese /\b(?i:one|one\ two|three)\b/. Volles Beispiel unten:Regulärer Ausdruck gibt nur eine Übereinstimmung zurück

keywords = ['one', 'one two', 'three'] 
re = /\b(?i:#{ Regexp.union(keywords).source })\b/ 
text = 'Some word one and one two other word' 
text.downcase.scan(re) 

das Ergebnis dieses Codes ist

=> ["one", "one"] 

Wie Spiel des zweiten Schlüsselwort finden one two und bekommt wie dies zur Folge haben?

=> ["one", "one two"] 
+1

Ändern Sie die Reihenfolge der Änderungen vom längsten zum kürzesten. – revo

Antwort

4

Regexes bestrebt sind, zu entsprechen. Sobald sie eine Übereinstimmung gefunden haben, versuchen sie nicht, eine andere möglicherweise längere zu finden (mit einer wichtigen Ausnahme).

/\b(?i:one|one\ two|three)\b/ wird nie one two übereinstimmen, weil es immer zuerst one übereinstimmen wird. Sie würden /\b(?i:one two|one|three)\b/ benötigen, also versucht es zuerst one two. Die einfachste Möglichkeit, dies zu automatisieren, ist die Sortierung nach den längsten Keywords.

keywords = ['one', 'one two', 'three'] 
re = Regexp.union(keywords.sort { |a,b| b.length <=> a.length }).source 
re = /\b#{re}\b/i; 
text = 'Some word one and one two other word' 
puts text.scan(re) 

Bitte beachte, dass ich die ganze Regex gesetzt Groß- und Kleinschreibung zu sein, einfacher zu lesen als (?:...), und dass die Zeichenfolge downcasing redundant ist.


Die Ausnahme ist repetition wie +, * und Freunde. Sie sind gierig standardmäßig. .+ wird so viele Zeichen wie möglich übereinstimmen. Das ist gierig. Sie können es faul machen, um das erste, was es sieht, mit einem ? übereinstimmen. .+? wird ein einzelnes Zeichen entsprechen.

"A foot of fools".match(/(.*foo)/); # matches "A foot of foo" 
"A foot of fools".match(/(.*?foo)/); # matches "A foo" 
0

ich versuchte Ihr Beispiel indem das erste Element in die zweite Position des Arrays zu bewegen und es funktioniert (z.B. http://rubular.com/r/4F2Hc46wHT).

In der Tat sieht es aus wie das erste Keyword "überlappt" die zweite.

Diese Antwort ist möglicherweise nicht hilfreich, wenn Sie die Reihenfolge der Schlüsselwörter nicht ändern können.

4

Der Punkt ist, dass \bone\b Streichhölzer one in one two und da dieser Zweig erscheint vor one two Zweig, es „gewinnt“ (siehe Remember That The Regex Engine Is Eager).

Sie müssen das Keyword-Array in absteigender Reihenfolge sortieren, bevor Sie eine Regex erstellen. Es wird dann folgendermaßen aussehen

(?-mix:\b(?i:three|one\ two|one)\b) 

diese Weise wird die längere one two vor dem kürzeren one sein wird und angepasst bekommen.

Siehe Ruby demo:

keywords = ['one', 'one two', 'three'] 
keywords = keywords.dup.sort.reverse 
re = /\b(?i:#{ Regexp.union(keywords).source })\b/ 
text = 'Some word one and one two other word' 
puts text.downcase.scan(re) 
# => [ one, one two ] 
+1

Beachten Sie, dass dies funktioniert, weil "AB"> "A" 'egal, was" B "ist.[* Wenn die Strings unterschiedlich lang sind und die Strings im Vergleich zur kürzesten Länge gleich sind, gilt der längere String als größer als der kürzere. *] (Https://ruby-doc.org/core- 2.4.0/String.html # Methode-i-3C-3D-3E) – Schwern

Verwandte Themen