如何用Ruby中的str.sub替换多个grep结果?

时间:2012-03-02 16:34:03

标签: ruby grep

我是红宝石初学者,我发现了一个问题,我想知道是否有更多的'红宝石方式' 解决它。

我的问题是: 我有一个字符串,像这样:

 str = "<div class=\"yui-u first\">\r\n\t\t\t\t\t<h1>Jonathan Doe</h1>\r\n
 \t\t\t\t\t<h2>Web   Designer, Director</h2>\r\n\t\t\t\t</div>"

 # now, i want to replace the substring in <h1> </h1> and <h2> and </h2> with 
 these two string:"fooo" and "barr".

这就是我所做的:

# first, i got the exactly matched substrings of str:
r = str.scan(/(?<=<h\d>).*?(?=<\/h\d>)/)
# then, i create a hash table to set the corresponding replace strings
h = {r[0] => 'fooo', r[1] => 'barr'}
# finally, using str.gsub to replace those matched strings
str.gsub!(/(?<=<h\d>).*?(?=<\/h\d>)/, h)
# or like this
str.gsub!(/(?<=<h\d>).*?(?=<\/h\d>)/) {|v| h[v]}

PS:<h1> </h1><h2> </h2>中的子字符串不是固定的,所以我有 首先获取这些字符串,以便我可以构建一个哈希表。但是我 真的不喜欢上面的代码(因为我写了两行几乎相同), 我认为必须有一种优雅的方式来做到这一点。我尝试过这样的事情:

str.gsub!(/(?<=<h\d>).*?(?=<\/h\d>)/) { ['fooo', 'barr'].each {|v| v}}

但这不起作用。因为这个块每天都会返回['fooo','barr']! 如果有办法让这个块(或其他东西?)一次返回一个元素(第一次返回'fooo',然后在第二次返回'barr'),我的问题将解决! 谢谢!

1 个答案:

答案 0 :(得分:1)

虽然您确实没有业务parsing HTML with a regexp,但像Nokogiri这样的库可以让您更容易直接修改DOM,但您所犯的错误在于假设迭代器每次替换只执行一次,并且该块只返回一个值。 each实际上将返回正在迭代的对象。

这是一种避免所有正则表达式疯狂的方法:

require 'rubygems'
gem 'nokogiri'
require 'nokogiri'

str = "<div class=\"yui-u first\">\r\n\t\t\t\t\t<h1>Jonathan Doe</h1>\r\n
 \t\t\t\t\t<h2>Web   Designer, Director</h2>\r\n\t\t\t\t</div>"

html = Nokogiri::HTML(str)

h1 = html.at_css('h1')
h1.content = 'foo'

h2 = html.at_css('h2')
h2.content = 'bar'

puts html.to_s

如果你想做多个替换,每个替换得到一个不同的值,简单的方法就是从堆栈中删除值:

subs = %w[ foo bar baz ]

string = "x x x"

string.gsub!(/x/) do |s|
  subs.shift
end

puts string.inspect
# => "foo bar baz"

请注意,此处会使用subs。一种更有效的方法是增加某种索引变量并使用该值,但这是一个微不足道的修改。