[英]How to replace the last occurrence of a substring in ruby?
I want to replace the last occurrence of a substring in Ruby.我想替换 Ruby 中最后一次出现的子字符串。 What's the easiest way?
最简单的方法是什么? For example, in abc123abc123 , I want to replace the last abc to ABC .
例如,在abc123abc123 中,我想将最后一个abc替换为ABC 。 How do I do that?
我怎么做?
How about怎么样
new_str = old_str.reverse.sub(pattern.reverse, replacement.reverse).reverse
For instance:例如:
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"
But probably there is a better way...但可能有更好的方法......
Edit:编辑:
...which Chris kindly provided in the comment below. ...克里斯在下面的评论中亲切地提供了这一点。
So, as *
is a greedy operator, the following is enough:因此,由于
*
是一个贪婪的运算符,因此以下内容就足够了:
"abc123abc123".gsub(/(.*)(abc)(.*)/, '\1ABC\3')
#=> "abc123ABC123"
Edit2:编辑2:
There is also a solution which neatly illustrates parallel array assignment in Ruby:还有一个解决方案可以巧妙地说明 Ruby 中的并行数组分配:
*a, b = "abc123abc123".split('abc', -1)
a.join('abc')+'ABC'+b
#=> "abc123ABC123"
Since Ruby 2.0 we can use \\K
which removes any text matched before it from the returned match.从 Ruby 2.0 开始,我们可以使用
\\K
从返回的匹配中删除在它之前匹配的任何文本。 Combine with a greedy operator and you get this:结合贪婪的运算符,你会得到这个:
'abc123abc123'.sub(/.*\Kabc/, 'ABC')
#=> "abc123ABC123"
This is about 1.4 times faster than using capturing groups as Hirurg103 suggested, but that speed comes at the cost of lowering readability by using a lesser-known pattern.这比 Hirurg103 建议的使用捕获组快大约 1.4 倍,但这种速度是以使用鲜为人知的模式降低可读性为代价的。
more info on \\K
: https://www.regular-expressions.info/keep.html关于
\\K
更多信息: https : //www.regular-expressions.info/keep.html
When searching in huge streams of data, using reverse
will definitively* lead to performance issues.在大量数据流中搜索时,使用
reverse
肯定会* 导致性能问题。 I use string.rpartition
*:我使用
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"
The same code must work with a string with no occurrences of sub_or_pattern
:相同的代码必须处理没有出现
sub_or_pattern
的字符串:
string = "hello_hello_hello"
# ...
# "hello_hello_hello"
* rpartition
uses rb_str_subseq()
internally. *
rpartition
内部使用rb_str_subseq()
。 I didn't check if that function returns a copy of the string, but I think it preserves the chunk of memory used by that part of the string.我没有检查该函数是否返回字符串的副本,但我认为它保留了该字符串部分使用的内存块。
reverse
uses rb_enc_cr_str_copy_for_substr()
, which suggests that copies are done all the time -- although maybe in the future a smarter String
class may be implemented (having a flag reversed
set to true, and having all of its functions operating backwards when that is set), as of now, it is inefficient. reverse
使用rb_enc_cr_str_copy_for_substr()
,这表明副本一直rb_enc_cr_str_copy_for_substr()
——尽管将来可能会实现一个更智能的String
类(将一个标志reversed
设置为 true,并且在设置时使其所有功能向后运行),截至目前,它是低效的。
Moreover, Regex
patterns can't be simply reversed.此外,
Regex
模式不能简单地反转。 The question only asks for replacing the last occurrence of a sub-string, so, that's OK, but readers in the need of something more robust won't benefit from the most voted answer (as of this writing)这个问题只要求替换最后一次出现的子字符串,所以,没关系,但是需要更强大的东西的读者不会从投票最多的答案中受益(在撰写本文时)
Here's another possible solution:这是另一种可能的解决方案:
>> s = "abc123abc123"
=> "abc123abc123"
>> s[s.rindex('abc')...(s.rindex('abc') + 'abc'.length)] = "ABC"
=> "ABC"
>> s
=> "abc123ABC123"
您可以使用String#sub和 greedy regexp .*
来实现这一点,如下所示:
'abc123abc123'.sub(/(.*)abc/, '\1ABC')
simple and efficient:简单高效:
s = "abc123abc123abc"
p = "123"
s.slice!(s.rindex(p), p.size)
s == "abc123abcabc"
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
I've used this handy helper method quite a bit:我经常使用这个方便的辅助方法:
def gsub_last(str, source, target)
return str unless str.include?(source)
top, middle, bottom = str.rpartition(source)
"#{top}#{target}#{bottom}"
end
If you want to make it more Rails-y, extend it on the String class itself:如果你想让它更加 Rails-y,在 String 类本身上扩展它:
class String
def gsub_last(source, target)
return self unless self.include?(source)
top, middle, bottom = self.rpartition(source)
"#{top}#{target}#{bottom}"
end
end
Then you can just call it directly on any String instance, eg "fooBAR123BAR".gsub_last("BAR", "FOO") == "fooBAR123FOO"
然后你可以直接在任何 String 实例上调用它,例如
"fooBAR123BAR".gsub_last("BAR", "FOO") == "fooBAR123FOO"
.gsub /abc(?=[^abc]*$)/, 'ABC'
匹配一个“abc”,然后断言((?=) 是正向先行)直到字符串末尾没有其他字符是“abc”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.