So passen Sie alle Vorkommen eines regulären Ausdrucks an

586

Gibt es eine schnelle Möglichkeit, jede Übereinstimmung eines regulären Ausdrucks in Ruby zu finden? Ich habe das Regex-Objekt in der Ruby STL durchsucht und vergeblich bei Google gesucht.

Chris Bunch
quelle
3
Ich habe gelesen, wie kann ich einen String nach allen Regex-Mustern durchsuchen und war schrecklich verwirrt ...
Hugoagogo

Antworten:

821

Verwenden scansollte den Trick tun:

string.scan(/regex/)
Jean
quelle
9
Aber was ist mit diesem Fall? "pass zu mir!". scan (/.../) = ["mat", "ch" "me!" ], aber alle Vorkommen von /.../ wären ["mat", "atc", "tch", "ch", ...]
Michael Dickens
13
Nicht wäre es nicht. /.../ ist ein normaler gieriger regulärer Ausdruck. Übereinstimmende Inhalte werden nicht zurückverfolgt. Sie könnten versuchen, einen faulen regulären Ausdruck zu verwenden, aber selbst das wird wahrscheinlich nicht ausreichen. Schauen Sie sich das Regexp-Dokument ruby-doc.org/core-1.9.3/Regexp.html an, um Ihr Regexp korrekt auszudrücken :)
Jean
49
Dies scheint wie eine Ruby WTF ... warum ist dies auf String anstelle von Regexp mit den anderen Regexp-Sachen? Es wird nicht einmal irgendwo in den Dokumenten für Regexp
Anentropic
9
Ich denke, das liegt daran, dass es für String definiert und aufgerufen wird, nicht für Regex ... Aber es macht tatsächlich Sinn. Sie können einen regulären Ausdruck schreiben, um alle Übereinstimmungen mit Regex # match zu erfassen und über erfasste Gruppen zu iterieren. Hier schreiben Sie eine Teilübereinstimmungsfunktion und möchten, dass sie mehrmals auf eine bestimmte Zeichenfolge angewendet wird. Dies liegt nicht in der Verantwortung von Regexp. Ich schlage vor, Sie überprüfen die Implementierung des Scans für ein besseres Verständnis: ruby-doc.org/core-1.9.3/String.html#method-i-scan
Jean
9
@ MichaelDickens: In diesem Fall können Sie verwenden /(?=(...))/.
Konrad Borowski
67

Verwenden Sie die String- scanMethode, um alle übereinstimmenden Zeichenfolgen zu finden .

str = "A 54mpl3 string w1th 7 numb3rs scatter36 ar0und"
str.scan(/\d+/)
#=> ["54", "3", "1", "7", "3", "36", "0"]

Wenn Sie möchten, MatchDatawelchen Typ das von der Regexp- matchMethode zurückgegebene Objekt hat , verwenden Sie:

str.to_enum(:scan, /\d+/).map { Regexp.last_match }
#=> [#<MatchData "54">, #<MatchData "3">, #<MatchData "1">, #<MatchData "7">, #<MatchData "3">, #<MatchData "36">, #<MatchData "0">]

Der Vorteil der Verwendung MatchDatabesteht darin, dass Sie Methoden wie die offsetfolgenden verwenden können :

match_datas = str.to_enum(:scan, /\d+/).map { Regexp.last_match }
match_datas[0].offset(0)
#=> [2, 4]
match_datas[1].offset(0)
#=> [7, 8]

Sehen Sie sich diese Fragen an, wenn Sie mehr wissen möchten:

Lesen Sie weiter über spezielle Variablen $&, $', $1, $2in Ruby wird auch hilfreich sein.

Sudo Bangbang
quelle
12

Wenn Sie einen regulären Ausdruck mit Gruppen haben:

str="A 54mpl3 string w1th 7 numbers scatter3r ar0und"
re=/(\d+)[m-t]/

Sie können die String- scanMethode verwenden, um übereinstimmende Gruppen zu finden:

str.scan re
#> [["54"], ["1"], ["3"]]

So finden Sie das passende Muster:

str.to_enum(:scan,re).map {$&}
#> ["54m", "1t", "3r"]
MVP
quelle
str.scan(/\d+[m-t]/) # => ["54m", "1t", "3r"]ist idiomatischer alsstr.to_enum(:scan,re).map {$&}
der Blechmann
Vielleicht hast du falsch verstanden. Der reguläre Ausdruck des Beispiels eines Benutzers, auf den ich geantwortet habe, war: /(\d+)[m-t]/Nicht /\d+[m-t]/schreiben: re = /(\d+)[m-t]/; str.scan(re)ist derselbe, str.scan(/(\d+)[mt]/)aber ich erhalte #> [["" 54 "], [" 1 "], [" 3 "]]und nicht "54m", "1t", "3r"]Die Frage war: Wenn ich einen regulären Ausdruck mit einer Gruppe habe und alle Muster erfassen möchte, ohne den regulären zu ändern Ausdruck (Verlassen der Gruppe), wie kann ich das machen? In diesem Sinne war eine mögliche Lösung, wenn auch etwas kryptisch und schwer zu lesen ,:str.to_enum(:scan,re).map {$&}
MVP
-1

Sie können verwenden string.scan(your_regex).flatten. Wenn Ihre Regex Gruppen enthält, wird sie in einem einzelnen einfachen Array zurückgegeben.

string = "A 54mpl3 string w1th 7 numbers scatter3r ar0und"
your_regex = /(\d+)[m-t]/
string.scan(your_regex).flatten
=> ["54", "1", "3"]

Regex kann auch eine benannte Gruppe sein.

string = 'group_photo.jpg'
regex = /\A(?<name>.*)\.(?<ext>.*)\z/
string.scan(regex).flatten

Sie können auch verwenden gsub, es ist nur eine weitere Möglichkeit, wenn Sie MatchData möchten.

str.gsub(/\d/).map{ Regexp.last_match }
Datt
quelle
Entfernen Sie die Gruppierung aus your_regex = /(\d+)[m-t]/und Sie müssen sie nicht verwenden flatten. Ihr letztes Beispiel verwendet, last_matchwas in diesem Fall wahrscheinlich sicher ist, aber global ist und möglicherweise überschrieben werden könnte, wenn vor dem Aufruf ein regulärer Ausdruck gefunden wurdelast_match . Stattdessen ist es wahrscheinlich sicherer zu verwenden string.match(regex).captures # => ["group_photo", "jpg"]oder string.scan(/\d+/) # => ["54", "3", "1", "7", "3", "0"]wie in anderen Antworten gezeigt, je nach Muster und Anforderungen.
Der Blechmann