Perl Regex匹配未用引号括起来的字符串

时间:2013-10-05 04:13:40

标签: regex perl

我正在尝试编写正则表达式以匹配不在引号内(双或单)的字符串,但到目前为止我能做的最好的是循环遍历字符串的所有字符。必须有一个更简单,更优雅的解决方案。

示例:如果尝试将foo替换为bar,则字符串hello foo!将变为hello bar!,但字符串you said "my name is foo"将保持不变。

任何人都可以通过正则表达式来帮助实现上述目标吗?

3 个答案:

答案 0 :(得分:3)

一种方法,使用否定前瞻:

perl -lane 's/foo(?![^"]*"(?:[^"]*"[^"]*")*[^"]*$)/bar/g; print' input

这意味着如果前面的引号数不是奇数则替换。所以这个假设你在输入中有平衡的引号。

示例输入:

hello foo!
"foo" foo "foo"
foo "hello" foo
"foo" bar

示例输出:

hello bar!
"foo" bar "foo"
bar "hello" bar
"foo" bar

答案 1 :(得分:0)

更新:快速摘要:虽然您需要“平衡群组”来真正解决这个问题,但简短的回答是,如果您还需要单引号,则无法执行此操作。因为那些双重作为Apostrophes。所以无论如何,这都会让你感到沮丧:That's when foo said, "That's my line!"平衡得到了撇号的重击。您需要构建自定义解析引擎。

注意:如果这是针对HTML属性的......我已经编写了一个正确解析它们的正则表达式,我相信它可以在Perl中运行。但这也依赖于=符号和其他HTML结构之类的分隔符。但在90%的情况下,XML / HTML Parser是最佳选择(10%仍然是可能的)。

正如我在对你的问题的评论中所提到的,更多的例子会给出更具体的答案。这是您有限的例子的答案:

^([^"']*?)foo([^"']*)$

对于中间正则表达式编写器来说,外观很容易,但代码维护很复杂,而且通常不需要。此外,任何要求您在正则表达式中使用点.的内容通常都不如它有效。

将我的示例替换为$1bar$2,您将成为金牌。但同样,正如我的评论所说,这是基于您的基本示例,该示例假定您的整个字符串可以以引号开头并以引号结束。如果你有不同的例子,他们会帮助你。

加成

为了好玩,我将为另外两个选项回答你的问题。选项1是我上面的原始答案。

选项2 (如Floris所述):

Hi foo, I said "hello"

或者

"hello", said foo to his friend.

如果是这种情况,引用文本只会出现在搜索文本之前或之后(在这种情况下为foo),那么答案是这样的:

^(?:([^"']*?)foo(.*)|(.*?)foo([^"']*))$

选项3 (如下面的评论所示)

He said, "Hello", so then Foo told him, "Lawl, bye"

要做到这一点,我们必须计算foo之前和之后的报价数量,以确保它们是偶数,或者它们在.NET Regex中“关闭”称为“平衡”,两者都不是您可以选择在没有其他自定义功能的情况下使用选项。

答案 2 :(得分:0)

也需要这样做,所以我自己解决了......这个解决方案不依赖于平衡的引号,但如果它们成对出现,显然不会支持撇号。

#!/usr/bin/perl

my @test = ( 'hello foo!',
             '"my name is foo"',
             'foo test "test foo test" test foo test "test foo test" test foo',
             "foo test 'test foo test' test foo test 'test foo test' test foo",
             '"foo test foo"',
             'foo test " foo test' );

foreach ( @test )
{
  s!("[^"]*"|'[^']*')|foo!$1//'bar'!ge;
  print "$_\n";
}
相关问题