从红宝石
替代的返回数组我想借此串foofoofoo
,地图foo
到bar
,并将所有个体替换为一个数组 - ['barfoofoo', 'foobarfoo', 'foofoobar']
从红宝石
这是最好的我:
require 'pp'
def replace(string, pattern, replacement)
results = []
string.length.times do |idx|
match_index = (Regexp.new(pattern) =~ string[idx..-1])
next unless match_index
match_index = idx + match_index
prefix = ''
if match_index > 0
prefix = string[0..match_index - 1]
end
suffix = ''
if match_index < string.length - pattern.length - 1
suffix = string[match_index + pattern.length..-1]
end
results << prefix + replacement + suffix
end
results.uniq
end
pp replace("foofoofoo", 'foo', 'bar')
这起作用(至少对于这个测试用例来说),但是看起来太冗长而且很拗口。我可以做的更好,也许通过使用string#gsub
与块或其他?
这是很容易与pre_match
($`
)和post_match
($'
)做:
def replace_matches(str, re, repl)
return enum_for(:replace_matches, str, re, repl) unless block_given?
str.scan(re) do
yield "#$`#{repl}#$'"
end
end
str = "foofoofoo"
# block usage
replace_matches(str, /foo/, "bar") { |x| puts x }
# enum usage
puts replace_matches(str, /foo/, "bar").to_a
编辑:如果你有重叠的比赛,那么它会变得更难,因为正则表达式并没有真正具备处理它的能力。所以,你可以做这样的:
def replace_matches(str, re, repl)
return enum_for(:replace_matches, str, re, repl) unless block_given?
re = /(?=(?<pattern>#{re}))/
str.scan(re) do
pattern_start = $~.begin(0)
pattern_end = pattern_start + $~[:pattern].length
yield str[0 ... pattern_start] + repl + str[pattern_end .. -1]
end
end
str = "oooo"
replace_matches(str, /oo/, "x") { |x| puts x }
在这里,我们滥用积极先行,这是0宽度,所以我们可以得到重叠的匹配。但是,我们还需要知道我们匹配了多少个字符,而我们现在无法像以前那样匹配0个宽度,因此我们将重新捕获该预测的内容,并计算新的宽度那。
(免责声明:它仍然将只匹配每个字符一次;如果你想在每一个字符考虑多种可能性,就像你/f|o|fo/
情况下,复杂的东西还多)
编辑:一个有点调整和我们甚至可以支持适当的GSUB类似的行为:
def replace_matches(str, re, repl)
return enum_for(:replace_matches, str, re, repl) unless block_given?
new_re = /(?=(?<pattern>#{re}))/
str.scan(new_re) do
pattern_start = $~.begin(0)
pattern_end = pattern_start + $~[:pattern].length
new_repl = str[pattern_start ... pattern_end].gsub(re, repl)
yield str[0 ... pattern_start] + new_repl + str[pattern_end .. -1]
end
end
str = "abcd"
replace_matches(str, /(?<first>\w)(?<second>\w)/, '\k<second>\k<first>').to_a
# => ["bacd", "acbd", "abdc"]
(免责声明:最后一个片段,该模式采用回顾后或先行到比赛区域外的检查无法处理的情况下)
如果我可以的话,我会再次为此编辑upvote :-) – Anand
我想借此串foofoofoo,地图FOO吧,并返回所有单个替换为一个数组 - 如果我们假定[ 'barfoofoo', 'foobarfoo', 'foofoobar']
输入总是正好是“foofoofoo”(三个“foo”),那么问题很简单,所以我们假设有一个或多个“foo”。
def possibilities(input)
n = input.length/3
n.times.map { |i|
(['bar'] + Array.new(n - 1, 'foo')).rotate(-i).join
}
end
possibilities "foo"
# ["bar"]
possibilities "foofoo"
# ["barfoo", "foobar"]
possibilities "foofoofoo"
# ["barfoofoo", "foobarfoo", "foofoobar"]
有一些解决方案会使用较少的内存,但是这种方法似乎很方便。
我不认为Ruby提供了这种开箱即用的功能。然而,这里是我的两分钱,这可能是更优雅:
def replace(str, pattern, replacement)
count = str.scan(pattern).count
fragments = str.split(pattern, -1)
count.times.map do |occurrence|
fragments[0..occurrence].join(pattern)
.concat(replacement)
.concat(fragments[(occurrence+1)..count].to_a.join(pattern))
end
end
这是针对所述问题的一个非常好的方法。在我的问题中看到我的评论 - 我实际上需要支持从任何索引开始的匹配,所以,例如,'replace 'oxo','oox']' – Anand
我不不认为Ruby提供这样的功能离子性。 –
@JaredBeck为了澄清,输入字符串作为示例给出 - 真正的问题是支持任意字符串,并用所提供的替换替换任何索引处的模式匹配。例如,'replace('foofof','f | o | fo','x')'应该产生'['xoofof','xofof','fxofof','fxxfof','foxfof', ]' –
Anand
请根据您的最新评论更新您的问题。另外,''f | o | fo''是一个字符串,而不是一个模式。如果你想要一个模式,你应该使用'/ f | o | fo /'。 –