用值替换捕获,然后返回整个匹配的模式

时间:2012-07-03 13:17:52

标签: ruby regex

我有一些包含JavaScript变量定义的文本(页面的HTML):

var FOREGROUND_COLOR = '#ffffff';    
var BACKGROUND_COLOR = '#aaaaaa';
var BORDER_COLOR = '#000000';

我希望使用像/var BACKGROUND_COLOR = '(#.*?)';/这样的ruby regexp模式匹配字符串内容,并用替换值(#bbbbbb)替换捕获,但是然后返回整个原始模式替换值,以便新内容为

var FOREGROUND_COLOR = '#ffffff';    
var BACKGROUND_COLOR = '#bbbbbb';
var BORDER_COLOR = '#000000';

主要约束是模式在外部文件中定义,替换值来自数据库,内容(带有javascript的HTML页面和我想要更改的其他值)在运行时不受我的控制。因此,当定义模式时,我不知道替换,并且当定义替换时,我不知道模式。因此,我需要假设模式和值都给出了。

简单的ruby gsub不起作用:

contents.gsub(pattern, replacement)
=> 
var FOREGROUND_COLOR = '#ffffff';    
#bbbbbb                               // this is the problem with simple gsub
var BORDER_COLOR = '#000000';

但看起来我可以使用反向引用(\1)来使用块语法。但我宁愿不必更改我的模式,因为它们已经很复杂(这是一个简化的案例,regexp是处理模式变化所必需的)。我可以破解并使其发挥作用,但这看起来并不明智。

如何在Ruby中有效地完成这项工作?

其他澄清(为什么答案尚未被接受) 我事先知道了这个模式,但它并不总是以var SOMETHING = '#COLOR_VALUE';的形式出现 - 它可能是CSS规则,或URL或其他任意模式。如果我有值X, Y and Z和模式/The letters of the day are (.*?), Cookie Monster/以及包含

的内容
<p>Welcome to Sesame Street!</p>
<p>The letters of the day are Q, J and L, Cookie Monster.  Do you like them?</p>

我的操作后内容应为

<p>Welcome to Sesame Street!</p>
<p>The letters of the day are X, Y and Z, Cookie Monster.  Do you like them?</p>

我不认为7月11日之前的答案提供了一个通用的解决方案,尽管他们可能会回答我提出的问题。谢谢。

2 个答案:

答案 0 :(得分:1)

脚本:

contents = 'var FOREGROUND_COLOR = \'#ffffff\';     
var BACKGROUND_COLOR = \'#aaaaaa\'; 
var BORDER_COLOR = \'#000000\';'

pattern = '(?<=(var BACKGROUND_COLOR = \'))(#[a-zA-Z\d]+)(?=(\';))'

replacement = '#bbbbbb'

contents = contents.sub(/#{pattern}/m, replacement)
puts contents;
puts "\n"
puts $1+$2+$3;

输出:

var FOREGROUND_COLOR = '#ffffff';     
var BACKGROUND_COLOR = '#bbbbbb'; 
var BORDER_COLOR = '#000000';

var BACKGROUND_COLOR = '#aaaaaa';

测试代码here

答案 1 :(得分:1)

这是一个允许您为每个正则表达式存储多个替换值的解决方案:

JS = %Q{
  var FOREGROUND_COLOR = '#ffffff';    
  var BACKGROUND_COLOR = '#aaaaaa';
  var BORDER_COLOR = '#000000';
}

# String regexes from DB mapped to array of replacement values
# First array entry [0] matched the first regex capture \1
FIND_REPLACE = {
  "var BACKGROUND_COLOR = '(#.*?)';" => ['#bbbbbb']
}

def replace_all( str, find_replace )
  str.dup.tap do |result|
    find_replace.each do |re,replacements|
      result.gsub! Regexp.new(re) do
        matches = $~
        matches[0].tap do |result|
          replacements.each.with_index do |replacement,i|
            found = matches[i+1]
            puts "Replacing #{found} with #{replacement}"
            result[found] = replacement
          end
        end
      end
    end
  end
end

puts replace_all( JS, FIND_REPLACE )

#=> Replacing #aaaaaa with #bbbbbb
#=> 
#=>   var FOREGROUND_COLOR = '#ffffff';    
#=>   var BACKGROUND_COLOR = '#bbbbbb';
#=>   var BORDER_COLOR = '#000000';

这不是一个理想的答案,因为如果先前替换此传递的结果创建的内容与稍后匹配的文本相同,则行result[found] = replacement可能做错了。例如:

# Should output "dogs = emus"
puts replace_all "cats = dogs", "(\\w+) = (\\w+)" => ["dogs","emus"]
#=> Replacing cats with dogs
#=> Replacing dogs with emus
#=> emus = dogs

# Should output "foo = bar"
puts replace_all "foo = foo", "foo = (\\w+)" => ["bar"]
#=> Replacing foo with bar
#=> bar = foo