我有一个大文本文件。在这个文本文件中,我想用'菠菜'代替'pizza'一词,用'Spinach'代替'Pizza',用'旋转'代替'pizzing' - 除非这些词发生在花括号内的任何地方。因此{pizza}
,{giant.pizza}
和{hot-pizza-oven}
应保持不变。
到目前为止,我提出的最佳解决方案是逐行遍历文件,发出一个正则表达式,在{或之后}之前检测所有内容,并在每个字符串上使用正则表达式。但这变得非常复杂和笨拙,我想知道这个问题是否有适当的解决方案。
答案 0 :(得分:2)
这可以通过几个步骤完成。我会逐行遍历文件,并将每一行传递给此方法:
def spinachize line
# list of words to swap
swaps = {
'pizza' => 'spinach',
'Pizza' => 'Spinach',
'pizzing' => 'spinning'
}
# random placeholder for bracketed text
placeholder = 'fdjfafdlskdsfajkldfas'
# save all instances of bracketed text
bracketed_text = line.scan(/\{.*?\}/)
# remove bracketed text from line
line.gsub!(/\{.*?\}/, placeholder)
# replace all swaps
swaps.each do |original_text, new_text|
line.gsub!(original_text, new_text)
end
# re-insert bracketed text
line.gsub(placeholder){bracketed_text.shift}
end
上面的评论解释了我们的目标。以下是几个例子:
spinachize "Pizza is good, but more pizza is better"
=> "Spinach is good, but more spinach is better"
spinachize "Leave bracketed instances of {pizza} or {this.pizza} alone"
=> "Leave bracketed instances of {pizza} or {this.pizza} alone"
如您所见,您可以指定要交换的项目,或修改方法以从某个位置的数据库或平面文件中提取列表。占位符只需要是一些独特的东西,不会自然地出现在源文件中。
过程是这样的:从原始行中删除括号内的文本,并记住它以供日后使用。交换所有需要交换的文本,然后添加回括号内的文本。它不是单行,但它运行良好,易读且易于更新。
该方法的最后一行可能需要一些澄清。没有多少人知道“gsub”方法可以采用块而不是第二个参数。然后该块确定放置原始文本的内容。在这种情况下,每次调用块时,我都会删除保存的括号列表中的第一个项目,然后使用它。
答案 1 :(得分:1)
rules = {'pizza' => 'spinach','Pizza' => 'Spinach','pizzing' => 'spinning'}
regexp = /\{[^{}]*\}|#{rules.keys.join('|')}/m
puts(file.read.gsub(regexp) { |s| rules[s] || s })
这构造了一个正则表达式,它匹配括号中的字符串或要替换的字符串。然后我们通过一个块来运行它,该块用给定的值替换字符串,并保持括号内的字符串不变。使用/m
标志,正则表达式可以容忍括号内的换行符 - 如果不会发生,则可以将其取出。无论哪种方式,都不需要逐行迭代。
答案 2 :(得分:0)
str = "Pizza {pizza} with spinach is not pizzing."
swaps = {'{pizza}' =>'{pizza}',
'{Pizza}' =>'{Pizza}',
'{pizzing}'=> '{pizzing}'
'pizza' => 'spinach',
'Pizza' => 'Spinach',
'pizzing' => 'spinning'}
regex = Regexp.union(swaps.keys)
p str.gsub(regex, swaps) # => "Spinach {pizza} with spinach is not spinning."
答案 3 :(得分:0)
我会为文件的每一行调用以下方法。
<强>代码强>
def doit(line)
replace = {'pizza'=>'spinach', 'Pizza'=>'Spinach', 'pizzing'=>'spinning'}
r = /\{.*?\}/
arr= line.split(r).map { |str|
str.gsub(/\b(?:pizza|Pizza|pizzing)\b/, replace) }
line.scan(r).each_with_object(arr.shift) { |str,res|
res << str << arr.shift }
end
<强>实施例强>
doit("Pizza Primastrada's {pizza} is the best {pizzing} pizza in town.")
#=> "Spinach Primastrada's {pizza} is the best {pizzing} spinach in town."
doit("{Pizza Primastrada}'s pizza is the best pizzing {pizza} in town.")
#=> "{Pizza Primastrada}'s spinach is the best spinning {pizza} in town."
<强>解释强>
line = "Pizza Primastrada's {pizza} is the best {pizzing} pizza in town."
replace = {'pizza'=>'spinach', 'Pizza'=>'Spinach', 'pizzing'=>'spinning'}
r = /\{.*?\}/
a = line.split(r)
#=> ["Pizza Primastrada's ", " is the best ", " pizza in town."]
b = a.map { |str| str.gsub(/\b(?:pizza|Pizza|pizzing)\b/, replace) }
#=> ["Spinach Primastrada's ", " is the best ", " spinach in town."]
keepers = line.scan(r)
#=> ["{pizza}", "{pizzing}"]
keepers.each_with_object(b.shift) { |str,res| res << str << b.shift }
#=> "Spinach Primastrada's {pizza} is the best {pizzing} spinach in town."
嵌套大括号
如果您希望允许嵌套大括号,请将正则表达式更改为:
r = /\{[^{}]*?(?:\{.*?\})*?[^{}]*?\}/
doit("Pizza Primastrada's {{great {great} pizza} is the best pizza.")
#=> "Spinach Primastrada's {{great {great} pizza} is the best spinach."
你提到了字符串
{words,salad,#{1,2,3} pizza|}
在评论中。如果这是用单引号括起来的字符串的一部分,那不是问题。但是,如果用双引号括起来,#
将引发语法错误。如果英镑字符被转义(\#
),也没问题。