2016-07-01 5 views
3

erstellen Ich habe eine Liste von Zeichenfolgen und müssen den regulären Ausdruck von ihnen erstellen, mit Regexp#union. Ich brauche das resultierende Muster case insensitive.Wie man einen regulären Ausdruck ohne Berücksichtigung der Groß-/Kleinschreibung mit Regexp.union

Die #union Methode selbst akzeptiert keine Optionen/Modifikatoren, also zur Zeit sehe ich zwei Möglichkeiten:

strings = %w|one two three| 

Regexp.new(Regexp.union(strings).to_s, true) 

und/oder:

Regexp.union(*strings.map { |s| /#{s}/i }) 

Beide Varianten ein bisschen komisch aussehen.

Gibt es eine Möglichkeit, einen regulären Ausdruck ohne Berücksichtigung der Groß-/Kleinschreibung zu erstellen, indem Regexp.union verwendet wird?

+0

Beachten Sie, dass Ihre erste Option 'Regexp.new (Regexp.union (strings) .to_s, true)' '/ (? - mix: eins | zwei | drei)/i' zurückgibt, was wahrscheinlich nicht das ist, was Sie wollen, weil die Wörter immer noch Groß- und Kleinschreibung übereinstimmen ('-i'). – Stefan

Antwort

5

Die einfache Ausgangspunkt ist:

words = %w[one two three] 
/#{ Regexp.union(words).source }/i # => /one|two|three/i 

Sie wahrscheinlich sicherstellen möchten, sind Sie nur Worte so zwicken es passend:

/\b#{ Regexp.union(words).source }\b/i # => /\bone|two|three\b/i 

Für Sauberkeit und Klarheit Ich bevorzuge die Verwendung einer nicht einfangenden Gruppe:

/\b(?:#{ Regexp.union(words).source })\b/i # => /\b(?:one|two|three)\b/i 

Verwenden Sie ist wichtig.Wenn Sie ein Regexp-Objekt erstellen, hat es eine Vorstellung von den Fahnen (i, m, x), die auf das Objekt anwenden und diejenigen, erhalten in den String interpoliert:

"#{ /foo/i }" # => "(?i-mx:foo)" 
"#{ /foo/ix }" # => "(?ix-m:foo)" 
"#{ /foo/ixm }" # => "(?mix:foo)" 

oder

(/foo/i).to_s # => "(?i-mx:foo)" 
(/foo/ix).to_s # => "(?ix-m:foo)" 
(/foo/ixm).to_s # => "(?mix:foo)" 

Das ist Fein, wenn das erzeugte Muster alleine steht, aber wenn es in eine Zeichenkette interpoliert wird, um andere Teile des Musters zu definieren, beeinflussen die Flags jeden Unterausdruck:

/\b(?:#{ Regexp.union(words) })\b/i # => /\b(?:(?-mix:one|two|three))\b/i 

Dig in die Regexp-Dokumentation und Sie werden sehen, dass ?-mix "ignore-case" innerhalb (?-mix:one|two|three) deaktiviert, obwohl das Gesamtmuster mit i gekennzeichnet ist, was zu einem Muster führt, das nicht das tut, was Sie wollen und wirklich ist schwer zu debuggen: Statt

'foo ONE bar'[/\b(?:#{ Regexp.union(words) })\b/i] # => nil 

, entfernt source die Flagge inneren Ausdruck, das Muster zu machen tun, was man erwarten würde:

/\b(?:#{ Regexp.union(words).source })\b/i # => /\b(?:one|two|three)\b/i 

und

'foo ONE bar'[/\b(?:#{ Regexp.union(words).source })\b/i] # => "ONE" 

Sie können bauen Sie Ihre Muster Regexp.new und vorbei in den Flaggen mit:

regexp = Regexp.new('(?:one|two|three)', Regexp::EXTENDED | Regexp::IGNORECASE) # => /(?:one|two|three)/ix 

aber als der Ausdruck wird immer komplexer wird es unhandlich. Das Erstellen eines Musters mit der String-Interpolation bleibt einfacher zu verstehen.

+0

Das ist interessant. Mir ist nicht aufgefallen, dass "Twosome" = ~/\ b # {Regexp.union (Wörter) .source} \ b/i # => 0' ist. Daher muss '# {Regexp.union (words) .source}' in eine Gruppe eingefügt werden. Ich wusste auch nicht über "Quelle". –

+0

'Quelle', natürlich, danke! Tolle Erklärung, übrigens. – mudasobwa

+2

Ich benutze viele Muster zum Parsen von Text, und "source" war der Kern, um einfache Muster zu komplexeren zu kombinieren. –

0

Sie haben das Offensichtliche übersehen.

strings = %w|one two three| 

r = Regexp.union(strings.flat_map do |word| 
    len = word.size 
    (2**len).times.map { |n| 
    len.times.map { |i| n[i]==1 ? word[i].upcase : word[i] } } 
end.map(&:join)) 

"'The Three Little Pigs' should be read by every building contractor" =~ r 
    #=> 5  
+0

Das würde funktionieren, ja; noch mehr: mit richtig gepatcht 'String # upcase' ist sogar mit _utf8_ strings arbeiten, wie mit kyrillischen (während Standard-Ruby regexps immer noch diese Funktionalität fehlt.) – mudasobwa

+0

Dies ist eine absurde Antwort. Ich würde es ablehnen, wenn ich könnte. Angenommen, 'r' ist die Regex, die für 'strings = [" eins "," zwei "," dreihundertsiebzig "]' 'berechnet wurde. Sicher, es funktioniert ('' Es gab hHReNDred seventy cats "= ~ r # => 11'), aber' r.to_s.count ('|') # => 2097167'! –

+1

Ich weiß, ich weiß;) Ich werde es nicht in prod verwenden, nur zu Hause. – mudasobwa

Verwandte Themen