Return 来自 ruby 的替换数组
Return array of replacements from ruby
我想将字符串 foofoofoo
、映射 foo
到 bar
和 return 所有单独的替换作为一个数组 - ['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
块或类似的东西?
I want to take the string foofoofoo, map foo to bar, and return all individual replacements as an array - ['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
用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 宽度,所以我们将重新捕获 lookahead 的内容,并计算新的宽度那。
(免责声明:它仍然只匹配每个字符一次;如果您想考虑每个字符的多种可能性,就像您的 /f|o|fo/
情况一样,它会使事情变得更加复杂。)
编辑:稍微调整一下,我们甚至可以支持正确的 gsub-like 行为:
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"]
(免责声明:最后一个片段无法处理模式使用后视或先行检查匹配区域之外的情况。)
我想将字符串 foofoofoo
、映射 foo
到 bar
和 return 所有单独的替换作为一个数组 - ['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
块或类似的东西?
I want to take the string foofoofoo, map foo to bar, and return all individual replacements as an array - ['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
用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 宽度,所以我们将重新捕获 lookahead 的内容,并计算新的宽度那。
(免责声明:它仍然只匹配每个字符一次;如果您想考虑每个字符的多种可能性,就像您的 /f|o|fo/
情况一样,它会使事情变得更加复杂。)
编辑:稍微调整一下,我们甚至可以支持正确的 gsub-like 行为:
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"]
(免责声明:最后一个片段无法处理模式使用后视或先行检查匹配区域之外的情况。)