2010-07-06 16 views

Respuesta

4

Durante la búsqueda en grandes flujos de datos, utilizando reverse hará definitivamente * provocar problemas de rendimiento. Yo uso 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" 

El mismo código debe trabajar con una cadena sin ocurrencias de sub_or_pattern:

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

* rpartition utiliza rb_str_subseq() internamente. No revisé si esa función devuelve una copia de la cadena, pero creo que conserva la porción de memoria utilizada por esa parte de la cadena. reverse usa rb_enc_cr_str_copy_for_substr(), lo que sugiere que las copias se realizan todo el tiempo, aunque tal vez en el futuro se pueda implementar una clase más inteligente String (con un indicador reversed establecido en verdadero y teniendo todas sus funciones funcionando al revés cuando esté configurado), a partir de ahora, es ineficiente.

Por otra parte, Regex patrones no se pueden revertir simplemente.La pregunta solo pide reemplazar la última ocurrencia de una subcadena, así que está bien, pero los lectores que necesiten algo más sólido no se beneficiarán de la respuesta más votada (al momento de escribir esto)

32

¿Qué tal

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

Por ejemplo:

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

clara y fácil de entender respuesta. –

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

Pero probablemente hay una manera mejor ...

Editar:

... lo cual Chris amablemente proporcionado en el comentario b elow.

Así que, como * es un operador codiciosos, el siguiente es suficiente:

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

Edit2:

También hay una solución que ilustra perfectamente asignación matriz paralela en Ruby:

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

Debido a la concordancia codiciosa, solo 'str.sub (/(.*) abc /, '\ 1ABC')' debería ser suficiente. –

+0

Doh! Gracias, actualicé la respuesta. –

+0

Muchas gracias. También pensé que este problema se puede resolver con expresiones regulares, pero no sé cómo. Lo hiciste. ¡Gracias de nuevo! –

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

Aquí hay otra posible solución:

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

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

>> s 
=> "abc123ABC123" 
1

simple y eficiente:

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

Esto no responde la pregunta. – aross

1

I ' he utilizado este método de ayuda a mano un poco:

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

Si desea hacerlo más carriles-y, extenderlo sobre la clase String en sí:

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

a continuación, puedes llamar directamente en cualquier instancia de String, por ejemplo, "fooBAR123BAR".gsub_last("BAR", "FOO") == "fooBAR123FOO"

0

Usted puede lograr esto con gsub y expresión regular codiciosos .* así:

'abc123abc123'.gsub(/(.*)abc/, '\1ABC') 
Cuestiones relacionadas