Ruby-Ersatz für mehrere Zeichenfolgen


75
str = "Hello☺ World☹"

Die erwartete Ausgabe ist:

"Hello:) World:("

Ich kann dies tun: str.gsub("☺", ":)").gsub("☹", ":(")

Gibt es eine andere Möglichkeit, dies in einem einzigen Funktionsaufruf zu tun? Etwas wie:

str.gsub(['s1', 's2'], ['r1', 'r2'])

1
Gibt es einen Grund, warum Sie das in einem Anruf tun möchten? Ich würde es vorziehen, bei Ihrer ersten Lösung zu bleiben.
Simon Perepelitsa

2
@Semyon: Das Mapping-Tabellenpaar ist groß oder kann zur Laufzeit konfiguriert werden.
Mu ist zu kurz

1
Wenn Sie am Ende eine riesige Zuordnungstabelle haben, sehen Sie im Grunde genommen eine Vorlagensprache. In diesem Fall können Sie es in ein DSL konvertieren und dafür einen Interpreter (oder Compiler) schreiben.
Swanand

Ich hatte erwartet String#tr, den Trick zu machen, aber der Ersatz durch mehrere Zeichen bedeutet, dass ich das nicht verwenden kann.
Andrew Grimm

Antworten:


123

Akzeptiert seit Ruby 1.9.2 String#gsubHash als zweiten Parameter zum Ersetzen durch übereinstimmende Schlüssel. Sie können einen regulären Ausdruck verwenden, um die zu ersetzende Teilzeichenfolge abzugleichen und einen Hash für zu ersetzende Werte zu übergeben.

So was:

'hello'.gsub(/[eo]/, 'e' => 3, 'o' => '*')    #=> "h3ll*"
'(0) 123-123.123'.gsub(/[()-,. ]/, '')    #=> "0123123123"

In Ruby 1.8.7 würden Sie dasselbe mit einem Block erreichen:

dict = { 'e' => 3, 'o' => '*' }
'hello'.gsub /[eo]/ do |match|
   dict[match.to_s]
 end #=> "h3ll*"

woah! Ich hatte keine Ahnung! tolles Zeug!
Kikito

Cool, wusste nichts davon. Eine Art schönere Version von Perl tr.
Marnen Laibow-Koser

Beachten Sie, dass dies nicht mit dem Aufruf von str.gsub (Schlüssel, Wert) für jedes Element des Hash identisch ist. Wenn etwas mit dem regulären Ausdruck übereinstimmt, aber keinen Eintrag im Hash hat, wird es gelöscht.
Sprachprofi

3
@NarenSisodiya, eigentlich sollte es sein: '(0) 123-123.123'.gsub (/ [() \ - ,.] /,' ') Sie müssen das Escape-Zeichen zu' - 'hinzufügen.
jpbalarini

Ja, diese Zeile dort ist falsch: '(0) 123-123.123'.gsub (/ [() -,.] /,' ') Sie müssen entweder dem Armaturenbrett entkommen oder es nach vorne bewegen.
Greg Blass

40

Richten Sie eine Zuordnungstabelle ein:

map = {'☺' => ':)', '☹' => ':(' }

Erstellen Sie dann einen regulären Ausdruck:

re = Regexp.new(map.keys.map { |x| Regexp.escape(x) }.join('|'))

Und schließlich gsub:

s = str.gsub(re, map)

Wenn Sie in 1.8 Land stecken, dann:

s = str.gsub(re) { |m| map[m] }

Sie benötigen das Regexp.escapedort, falls etwas, das Sie ersetzen möchten, innerhalb eines regulären Ausdrucks eine besondere Bedeutung hat. Oder dank steenslag können Sie Folgendes verwenden:

re = Regexp.union(map.keys)

und das Angebot wird für Sie erledigt.


@steenslag: Das ist eine schöne Modifikation.
Mu ist zu kurz

String # gsub akzeptiert Strings als Musterparameter: "Das Muster ist normalerweise ein Regexp. Wenn es als String angegeben wird, werden alle darin enthaltenen Metazeichen für reguläre Ausdrücke wörtlich interpretiert, z. B. '\\ d' entspricht einem Spiel, gefolgt von 'd'. anstelle einer Ziffer. ".
Andrew Grimm

@ Andrew: Ja, aber wir müssen mehrere Zeichenfolgen ersetzen, daher der reguläre Ausdruck.
Mu ist zu kurz

Was ist, wenn die Schlüssel der Karte Regex-Ausdrücke sind? Der Ersatz scheint nicht zu funktionieren
content01

@ content01: Aus dem Kopf, ich denke, Sie müssten in diesem Fall eins nach dem anderen gehen:map.each { |re, v| ... }
mu ist zu kurz

37

Sie könnten so etwas tun:

replacements = [ ["☺", ":)"], ["☹", ":("] ]
replacements.each {|replacement| str.gsub!(replacement[0], replacement[1])}

Es mag eine effizientere Lösung geben, aber dies macht den Code zumindest ein bisschen sauberer


2
Ist es nicht anzunehmen replacements.each?
DanneManne

4
Dies ist nur komplizierter und langsamer.
Texasbruce

1
Der Rückgabewert für eachist die Sammlung, für die aufgerufen wurde. stackoverflow.com/questions/11596879/…
Nathan Manousos

1
Damit es das Ergebnis replacements.reduce(str){|str,replacement| str.gsub(replacement[0],replacement[1])}
zurückgibt

4
@artm können Sie auch tun replacements.inject(str) { |str, (k,v)| str.gsub(k,v) }und vermeiden, dass Sie tun müssen [0]und [1].
Ben Lings

19

Spät zur Party, aber wenn Sie bestimmte Zeichen durch eines ersetzen möchten, können Sie einen regulären Ausdruck verwenden

string_to_replace.gsub(/_|,| /, '-')

In diesem Beispiel ersetzt gsub die Unterstriche (_), Kommas (,) oder () durch einen Bindestrich (-).


4
das wäre noch besser so:string_to_replace.gsub(/[_- ]/, '-')
Automatico

5

Eine andere einfache und dennoch leicht zu lesende Methode ist die folgende:

str = '12 ene 2013'
map = {'ene' => 'jan', 'abr'=>'apr', 'dic'=>'dec'}
map.each {|k,v| str.sub!(k,v)}
puts str # '12 jan 2013'

5

Sie können tr auch verwenden, um mehrere Zeichen in einer Zeichenfolge gleichzeitig zu ersetzen.

Ersetzen Sie z. B. "h" durch "m" und "l" durch "t".

"hello".tr("hl", "mt")
 => "metto"

sieht einfach, ordentlich und schneller aus (allerdings kein großer Unterschied) als gsub

puts Benchmark.measure {"hello".tr("hl", "mt") }
  0.000000   0.000000   0.000000 (  0.000007)

puts Benchmark.measure{"hello".gsub(/[hl]/, 'h' => 'm', 'l' => 't') }
  0.000000   0.000000   0.000000 (  0.000021)

1

Nach Narens Antwort oben würde ich mitgehen

tr = {'a' => '1', 'b' => '2', 'z' => '26'}
mystring.gsub(/[#{tr.keys}]/, tr)

So 'zebraazzeebra'.gsub(/[#{tr.keys}]/, tr)kehrt "26e2r112626ee2r1"

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.