给定如下示例输入:
s = "an example with 'one' word and 'two and three' words inside quotes"
我正在尝试迭代引号以外的部分以进行一些替换。例如,要将and
转换为&
但仅在引号之外才能获得:
an example with 'one' word & 'two and three' words inside quotes
如果我要更改引号内部,我可以简单地执行以下操作:
s.gsub(/'.*?'/){ |q| q.gsub(/and/, '&') }
获得:
an example with 'one' word and 'two & three' words inside quotes
我主要尝试了两件事来使这种策略适应报价之外。
首先,我试图在第一个gsub
中否定正则表达式(即/'.*?'/
(。我想如果有像/v
这样的后缀修饰符,我可以简单地做s.gsub(/'.*?'/v){ ... }
,不幸的是我找不到这样的东西。有一个负面的展望(即(?!pat)
(,但我认为这不是我需要的。
其次,我尝试将split
与gsub!
一起使用:
puts s.split(/'.*?'/){ |r| r.gsub!(/and/, '&') }
使用split
我可以遍历引号之外的部分:
s.split(/'.*?'/){ |r| puts r }
获得:
an example with
word and
words inside quotes
但是,我不能用gsub
或gsub!
来改变块内的这些部分。我想我需要split
的变异版本,类似于gsub
scan
的变异版本,但似乎没有这样的东西。
有没有一种简单的方法可以使这两种方法中的任何一种起作用?
您可以使用以下正则表达式执行所需的替换。
r = /G[^'n]*?(?:'[^'n]*'[^'n]*?)*?Kbandb/
启动引擎!
所需的 Ruby 代码如下。
str = "an and with 'one' word and 'two and three' words and end"
str.gsub(r, '&')
#=> "an & with 'one' word & 'two and three' words & end"
红宝石代码测试仪
Ruby 的正则表达式引擎执行以下操作。本质上,正则表达式断言"and"
自上一个匹配项以来跟随偶数个单引号,或者如果它是第一个匹配项,则从字符串开头跟随偶数个单引号。
G : asserts position at the end of the previous match
or the start of the string for the first match
[^'n]*? : match 0+ chars other than ' and n, lazily
(?: : begin capture group
'[^'n]*' : match ' then 0+ chars other than ' and n then '
[^'n]*? : match 0+ chars other than ' and n, lazily
) : end non-capture group
*? : execute non-capture group 0+ times, lazily
K : forget everything matched so far and reset start of match
bandb/ : match 'and'
您可以匹配并捕获需要保留的内容,而只需匹配需要替换的内容。
用
s.gsub(/('[^']*')|and/) { $1 || '&' }
s.gsub(/('[^']*')|and/) { |m| m == $~[1] ? $~[1] : '&' }
如果需要将and
匹配为一个完整的单词,请在模式中使用bandb
而不是and
。
这种方法非常方便,因为您可以根据需要添加任意数量的要跳过的特定模式。 例如,您还希望避免在双引号之间匹配整个单词and
:
s.gsub(/('[^']*'|"[^"]*")|bandb/) { $1 || '&' }
或者,您要确保它也跳过带有转义引号的引号之间的字符串:
s.gsub(/('[^'\]*(?:\.[^'\]*)*'|"[^"\]*(?:\.[^"\]*)*")|bandb/m) { $1 || '&' }
或者,如果它出现在圆形、方形、尖括号和大括号之外:
s.gsub(/(<[^<>]*>|{[^{}]*}|([^()]*)|[[^][]*])|bandb/m) { $1 || '&' }
匹配和捕获单引号之间的子字符串,只匹配您需要更改的内容。如果组 1 匹配,请将其放回$1
,否则,替换为&
。第二行中的替换块仅检查最后一个匹配项的组 1 值是否与当前匹配的值相同,如果是,则将其放回原处,否则用&
替换。
查看 Ruby 演示。
正则表达式详细信息
('[^']*')
- 捕获组 #1:'
、除'
以外的零个或多个字符,然后是一个'
字符|
- 或and
-and
子字符串。