Ruby正则表达式 - 如何替换字符串中第n个匹配项

6
在我的应用程序中,我需要能够查找所有数字子字符串,然后扫描每个子字符串,找到与给定范围匹配的第一个子字符串(例如介于5和15之间),并将该实例替换为另一个字符串“X”。
我的测试字符串为 s = "1 foo 100 bar 10 gee 1" 我的初始模式是任何一个或多个数字的字符串,例如,re = Regexp.new(/\d+/) matches = s.scan(re) 返回 ["1", "100", "10", "1"] 如果我想要替换第N个匹配项,并且仅替换第N个匹配项,如何操作?
例如,如果我想要替换第三个匹配项“10”(matches[2]),我不能只说s[matches[2]] = "X",因为那样会进行两个替换。
期望结果为: "1 foo 100 bar X gee 1" 谢谢!
2个回答

10

String#gsub有一个采用块的形式。它为每个匹配项提供,然后用块的结果替换匹配项。因此:

first = true
"1 foo 100 bar 10 gee 1 12".gsub(/\d+/) do |digits|
  number = digits.to_i
  if number >= 5 && number <= 15 && first
    # do the replacement
    first = false
    'X'
  else
    # don't replace; i.e. replace with itself
    digits
  end
end
# => "1 foo 100 bar X gee 1 12" 

我必须承认,从未想过用其他匹配项替换它们自己...聪明!谢谢! - jpw
2
我更喜欢使用next而不是返回原始值。 - bwest

2
另一种方法是使用字符类构建数字范围(如果不太复杂)。
>> s = "1 foo 100 bar 10 gee 1"
=> "1 foo 100 bar 10 gee 1"
>> s.sub(/(?<!\d)([5-9]|1[0-5])(?!\d)/, 'X')
=> "1 foo 100 bar X gee 1"
  • 负向先行断言确保数字序列的一部分不匹配
    • 如果数字不能成为单词的一部分,例如abc12ef8foo,则可以使用\b代替lookarounds
  • ([5-9]|1[0-5])将匹配从5到15的数字


最初,标题让我认为您想要替换第N次出现-例如:N = 2表示替换任何数字序列的第二次出现。 对于此操作,可以使用以下内容:

# here the number inside {} will be N-1
>> s.sub(/(\d+.*?){1}\K\d+/, 'X')
=> "1 foo X bar 10 gee 1"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接