2010-07-06 22 views

Antwort

4

Wenn in riesigen Datenströme suchen, reverse wird endgültig * führen zu Leistungsproblemen mit. Ich benutze string.rpartition *:

sub_or_pattern = "!" 
replacement = "?" 
string = "hello!hello!hello" 

array_of_pieces = string.rpartition sub_or_pattern 
(array_of_pieces[(array_of_pieces.find_index sub_or_pattern)] = replacement) rescue nil 
p array_of_pieces.join 
# "hello!hello?hello" 

Der gleiche Code muss ohne Vorkommen von sub_or_pattern mit einem String arbeiten:

string = "hello_hello_hello" 
# ... 
# "hello_hello_hello" 

* rpartition verwendet rb_str_subseq() intern. Ich habe nicht überprüft, ob diese Funktion eine Kopie der Zeichenkette zurückgibt, aber ich denke, sie behält den Teil des Speichers, der von diesem Teil der Zeichenkette verwendet wird, bei. reverse verwendet rb_enc_cr_str_copy_for_substr(), was darauf hindeutet, dass Kopien die ganze Zeit gemacht werden - obwohl vielleicht in Zukunft eine intelligentere String Klasse implementiert werden könnte (mit einem Flag reversed auf True gesetzt, und alle Funktionen rückwärts laufen, wenn das gesetzt ist), ab sofort ist es ineffizient.

Darüber hinaus Regex Muster können nicht einfach umgekehrt werden.Die Frage fragt nur nach dem Ersetzen des letzten Vorkommens einer Teilzeichenfolge, also, das ist in Ordnung, aber Leser, die etwas robusteres benötigen, werden nicht von der am meisten gewählten Antwort profitieren (zum jetzigen Zeitpunkt)

32

Wie wäre es

new_str = old_str.reverse.sub(pattern.reverse, replacement.reverse).reverse 

Zum Beispiel:

irb(main):001:0> old_str = "abc123abc123" 
=> "abc123abc123" 
irb(main):002:0> pattern="abc" 
=> "abc" 
irb(main):003:0> replacement="ABC" 
=> "ABC" 
irb(main):004:0> new_str = old_str.reverse.sub(pattern.reverse, replacement.reverse).reverse 
=> "abc123ABC123" 
+0

Klare und einfache Antwort zu verstehen. –

19
"abc123abc123".gsub(/(.*(abc.*)*)(abc)(.*)/, '\1ABC\4') 
#=> "abc123ABC123" 

Aber wahrscheinlich gibt es eine bessere Art und Weise ...

Edit:

... die Chris freundlicherweise in dem Kommentar b zur Verfügung gestellt elow.

So, wie * ein gieriger Operator ist, ist die folgende genug:

"abc123abc123".gsub(/(.*)(abc)(.*)/, '\1ABC\3') 
#=> "abc123ABC123" 

EDIT2:

Es ist auch eine Lösung, die ordentlich parallel Array Zuordnung in Ruby zeigt:

*a, b = "abc123abc123".split('abc', -1) 
a.join('abc')+'ABC'+b 
#=> "abc123ABC123" 
+12

Wegen der gierigen Übereinstimmung sollte nur 'str (/(.*) abc /, '\ 1ABC') ausreichen. –

+0

Doh! Danke, ich habe die Antwort aktualisiert. –

+0

Vielen Dank. Ich dachte auch, dass dieses Problem durch den regulären Ausdruck gelöst werden kann, aber ich weiß nicht wie. Du hast es geschafft. Danke noch einmal! –

1
string = "abc123abc123" 
pattern = /abc/ 
replacement = "ABC" 

matches = string.scan(pattern).length 
index = 0 
string.gsub(pattern) do |match| 
    index += 1 
    index == matches ? replacement : match 
end 
#=> abc123ABC123 
4

Hier ist eine andere mögliche Lösung:

>> s = "abc123abc123" 
=> "abc123abc123" 

>> s[s.rindex('abc')...(s.rindex('abc') + 'abc'.length)] = "ABC" 
=> "ABC" 

>> s 
=> "abc123ABC123" 
1

einfach und effizient:

s = "abc123abc123abc" 
p = "123" 
s.slice!(s.rindex(p), p.size) 
s == "abc123abcabc" 
+1

Dies beantwortet die Frage nicht. – aross

1

I '

def gsub_last(str, source, target) 
    return str unless str.include?(source) 
    top, middle, bottom = str.rpartition(source) 
    "#{top}#{target}#{bottom}" 
end 

Wenn Sie es mehr Rails-y, erweitern es auf der String-Klasse selbst machen wollen:

class String 
    def gsub_last(source, target) 
    return self unless self.include?(source) 
    top, middle, bottom = self.rpartition(source) 
    "#{top}#{target}#{bottom}" 
    end 
end 

Dann können Sie einfach es direkt anrufen dieses handliche Helfer-Methode ziemlich viel ve verwendet B. "fooBAR123BAR".gsub_last("BAR", "FOO") == "fooBAR123FOO"

0

Sie können dies erreichen mit gsub und gierig regexp .* wie folgt aus:

'abc123abc123'.gsub(/(.*)abc/, '\1ABC') 
Verwandte Themen