[英]How to substitude outside of regexp matches in a Ruby string?
给定如下示例输入:
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
,但似乎没有这样的东西。
有没有一种简单的方法可以使这些方法中的任何一种都起作用?
您可以匹配并捕获您需要保留的内容,并且只匹配您需要替换的内容。
利用
s.gsub(/('[^']*')|and/) { $1 || '&' }
s.gsub(/('[^']*')|and/) { |m| m == $~[1] ? $~[1] : '&' }
如果您需要将and
作为一个完整的单词进行匹配,请在模式中使用\band\b
而不是and
。
这种方法非常方便,因为您可以添加想要跳过的任意数量的特定模式。 例如,您还想避免在双引号and
匹配整个单词:
s.gsub(/('[^']*'|"[^"]*")|\band\b/) { $1 || '&' }
或者,您想确保它也在使用转义引号的引号之间跳过字符串:
s.gsub(/('[^'\\]*(?:\\.[^'\\]*)*'|"[^"\\]*(?:\\.[^"\\]*)*")|\band\b/m) { $1 || '&' }
或者,如果它出现在圆形、方形、尖括号和大括号之外:
s.gsub(/(<[^<>]*>|\{[^{}]*\}|\([^()]*\)|\[[^\]\[]*\])|\band\b/m) { $1 || '&' }
匹配和捕获单引号之间的子字符串,只匹配您需要更改的内容。 如果第 1 组匹配,则将其放回$1
,否则,替换为&
。 第二行中的替换块只是检查最后一个匹配的 Group 1 值是否与当前匹配的值相同,如果是,则将其放回原处,否则,替换为&
。
请参阅Ruby 演示。
正则表达式详细信息
('[^']*')
- 捕获组 #1: '
,除'
之外的零个或多个字符,然后是一个'
字符|
- 或者and
- and
substring。您可以使用以下正则表达式执行所需的替换。
r = /\G[^'\n]*?(?:'[^'\n]*'[^'\n]*?)*?\K\band\b/
所需的 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
\band\b/ : match 'and'
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.