首页 > 解决方案 > 如何在 Ruby 字符串中替换正则表达式匹配之外的内容?

问题描述

给定如下示例输入:

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

但是,我不能用gsubor改变块内的这些部分gsub!。我想我需要一个 的变异版本split,类似于gsub的变异版本scan,但似乎没有这样的东西。

有没有一种简单的方法可以使这些方法中的任何一种都起作用?

标签: regexrubystring

解决方案


您可以匹配并捕获您需要保留的内容,并且只匹配您需要替换的内容。

利用

s.gsub(/('[^']*')|and/) { $1 || '&' }
s.gsub(/('[^']*')|and/) { |m| m == $~[1] ? $~[1] : '&' }

如果您需要匹配and整个单词,请使用\band\bin 模式而不是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子串。

推荐阅读