我想在Ruby中替换最后一次出现的子字符串。最简单的方法是什么? 例如,在abc123abc123中,我想将最后一个abc替换为ABC。应该如何实现?
我想在Ruby中替换最后一次出现的子字符串。最简单的方法是什么? 例如,在abc123abc123中,我想将最后一个abc替换为ABC。应该如何实现?
怎么样?
new_str = old_str.reverse.sub(pattern.reverse, replacement.reverse).reverse
例如: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"
"abc123abc123".gsub(/(.*(abc.*)*)(abc)(.*)/, '\1ABC\4')
#=> "abc123ABC123"
但可能有更好的方法...
编辑:
...Chris在下面的评论中友情提供了。
因此,由于*
是贪婪运算符,所以下面的内容足够了:
"abc123abc123".gsub(/(.*)(abc)(.*)/, '\1ABC\3')
#=> "abc123ABC123"
编辑2:
还有一个解决方案,它很好地演示了Ruby中的并行数组赋值:
*a, b = "abc123abc123".split('abc', -1)
a.join('abc')+'ABC'+b
#=> "abc123ABC123"
str.sub(/(.*)abc/, '\1ABC')
即可。 - Chris Johnsenclass String def sub_last(str_match, str_sub) self.sub(/(.*)#{Regexp.quote(str_match)}/, '\1' + str_sub) end end
- coconup自从 Ruby 2.0 版本以后,我们可以使用 \K
命令,该命令会将其前面匹配到的任何文本从返回的匹配结果中移除。结合贪婪操作符使用,您可以这样做:
'abc123abc123'.sub(/.*\Kabc/, 'ABC')
#=> "abc123ABC123"
这比使用Hirurg103建议的捕获组要快大约1.4倍,但是以使用较不常见的模式为代价降低了可读性。
>> s = "abc123abc123"
=> "abc123abc123"
>> s[s.rindex('abc')...(s.rindex('abc') + 'abc'.length)] = "ABC"
=> "ABC"
>> s
=> "abc123ABC123"
当在大量数据流中搜索时,使用reverse
一定会导致性能问题。我使用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"
同一段代码必须适用于不包含sub_or_pattern
的字符串:
string = "hello_hello_hello"
# ...
# "hello_hello_hello"
*rpartition
内部使用rb_str_subseq()
。我没有检查该函数是否返回字符串的副本,但我认为它会保留字符串的这部分所使用的内存块。reverse
使用rb_enc_cr_str_copy_for_substr()
,这表明复制操作一直在进行——尽管也许将来可能会实现更智能的String
类(设置为true的标志reversed
,并且当设置为真时,所有函数都会反向运行),但目前它是低效的。
此外,Regex
模式不能简单地反转。问题只要求替换子字符串的最后一个出现位置,因此这没问题,但需要更健壮的读者将无法从最受欢迎的答案中受益(截至本文撰写之时)。
简单而高效:
s = "abc123abc123abc"
p = "123"
s.slice!(s.rindex(p), p.size)
s == "abc123abcabc"
def gsub_last(str, source, target)
return str unless str.include?(source)
top, middle, bottom = str.rpartition(source)
"#{top}#{target}#{bottom}"
end
class String
def gsub_last(source, target)
return self unless self.include?(source)
top, middle, bottom = self.rpartition(source)
"#{top}#{target}#{bottom}"
end
end
"fooBAR123BAR".gsub_last("BAR", "FOO") == "fooBAR123FOO"
。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
.gsub /abc(?=[^abc]*$)/, 'ABC'
匹配 "abc",然后断言 ((?=) 是正向先行断言),直到字符串结尾没有其他字符是 "abc"。