2010-07-06 47 views

Trả lời

4

Khi tìm kiếm trong suối lớn dữ liệu, sử dụng reverse sẽ dứt khoát * dẫn đến vấn đề hiệu suất. Tôi sử dụng 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" 

Cùng mã phải làm việc với một chuỗi không có sự xuất hiện của sub_or_pattern:

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

* rpartition sử dụng rb_str_subseq() nội bộ. Tôi đã không kiểm tra xem hàm đó có trả về một bản sao của chuỗi không, nhưng tôi nghĩ nó giữ nguyên phần bộ nhớ được sử dụng bởi phần đó của chuỗi. reverse sử dụng rb_enc_cr_str_copy_for_substr(), điều này cho thấy các bản sao được thực hiện mọi lúc - mặc dù có thể trong tương lai một lớp String thông minh hơn có thể được thực hiện (có cờ reversed được đặt thành true và có tất cả các chức năng hoạt động ngược khi được đặt), tính đến bây giờ, nó không hiệu quả.

Hơn nữa, không thể hoàn nguyên mẫu đơn Regex.Câu hỏi này chỉ yêu cầu thay thế lần xuất hiện cuối cùng của chuỗi con, vì vậy, điều đó là ổn, nhưng độc giả cần một thứ gì đó mạnh mẽ hơn sẽ không được hưởng lợi từ câu trả lời được bình chọn nhiều nhất (theo văn bản này)

32

Làm thế nào về

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

Ví dụ:

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

rõ ràng và dễ hiểu câu trả lời. –

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

Nhưng có lẽ có một cách tốt hơn ...

Edit:

... mà Chris vui lòng cung cấp trong bình luận b elow.

Vì vậy, như * là một nhà điều hành tham lam, sau đây là đủ:

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

Edit2:

Ngoài ra còn có một giải pháp mà gọn gàng minh họa phân mảng song song trong Ruby:

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

Do khớp tham lam, chỉ cần 'str.sub (/(.*) abc /, '\ 1ABC')' là đủ. –

+0

Doh! Cảm ơn, tôi đã cập nhật câu trả lời. –

+0

Cảm ơn bạn rất nhiều. Tôi cũng nghĩ rằng vấn đề này có thể được giải quyết bằng biểu thức chính quy, nhưng không biết làm thế nào. Bạn làm được rồi. Cảm ơn một lần nữa! –

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

Đây là một giải pháp khả thi khác:

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

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

>> s 
=> "abc123ABC123" 
1

đơn giản và hiệu quả:

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

Điều này không trả lời được câu hỏi. – aross

1

I ' đã sử dụng phương pháp helper tiện dụng này khá một chút:

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

Nếu bạn muốn làm cho nó nhiều hơn Rails-y, mở rộng nó trên lớp string bản thân:

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

Sau đó, bạn chỉ có thể gọi nó là trực tiếp trên bất kỳ trường hợp Chuỗi nào, ví dụ: "fooBAR123BAR".gsub_last("BAR", "FOO") == "fooBAR123FOO"

0

Bạn có thể đạt được điều này với gsub và tham lam regexp .* như thế này:

'abc123abc123'.gsub(/(.*)abc/, '\1ABC') 
Các vấn đề liên quan