匹配一个字符的正则表达式,但是当该字符被引号包含时不匹配。

17
我需要在一个字符串中匹配冒号(“:”),但不应匹配被引号(“或'字符)包围的冒号。 因此,以下字符串应该有两个匹配项。
something:'firstValue':'secondValue'
something:"firstValue":'secondValue'

但这应该只有1个匹配

something:'no:match'

1
@Jaco:1)用什么语言?2)先在['"]上分割字符串不是更容易,这样你就可以检查数组中所有奇数项。 - Huppie
你最好使用解析器。 - Gumbo
@Gumbo...我猜这就是他想要实现的。我的建议是:逐字节阅读,并在引号内使用标志。 - Scoregraphic
虽然我必须同意其他人的观点,即使用正则表达式比简单扫描更难实现。 - DigitalRoss
显示剩余2条评论
5个回答

7
如果正则表达式实现支持环视断言,请尝试以下方法:
:(?:(?<=["']:)|(?=["']))

这将匹配任何在双引号或单引号之前或之后的冒号。因此,只考虑像您提到的结构。

如果您构建一个逐字节读取输入并记住引号何时打开的小解析器,那么会更好。


1
这个工作得很好,但在退化情况下失败了,例如:'no match:'。 - Daniel Brückner
2
我同意Gumbo的观点 - 最好建立一个小型解析器。 - Jaco Pretorius
2
请在regex101上查看正则表达式的实际应用:https://regex101.com/r/bo4Es1/2。 我同意@DanielBrückner的观点,这个正则表达式可以工作,但在提到的退化情况下会失败。 - arthur.sw

3
正则表达式是无状态的。跟踪您是否在引号内部是状态信息。因此,仅使用单个正则表达式处理这些内容是不可能正确处理的。(请注意,一些“正则表达式”实现添加了扩展,这可能使其成为可能;我这里只谈论“真正”的正则表达式。)
使用两个正则表达式可以实现,但前提是您愿意修改原始字符串或使用副本。在 Perl 中:
$string =~ s/['"][^'"]*['"]//g;
my $match_count = $string =~ /:/g;

第一个步骤会找到由引号开头,后面跟着任意数量的非引号字符,并以第二个引号结束的所有序列,并从字符串中删除所有这样的序列。这将消除在引号内的冒号。(something:"firstValue":'secondValue' 变成 something::something:'no:match' 变成 something:
第二个步骤只是简单地计算剩余冒号的数量,这些冒号一开始就不在引号内。
然而,仅仅计算非引用冒号似乎在大多数情况下并不是一个特别有用的事情,因此我怀疑你真正的目标是将字符串分割成以冒号为字段分隔符的字段。在这种情况下,基于正则表达式的解决方案是不适用的,因为它会破坏引号内的任何数据。在这种情况下,您需要使用一个真正的解析器(大多数CSV解析器允许您指定分隔符,并且非常适合此任务),或者在最坏的情况下,逐个字符地遍历字符串并手动拆分它。
如果您告诉我们您使用的语言,我相信有人可以为该语言建议一个好的解析器库。

我正在使用C#,但我认为我可以用正则表达式(这是语言无关的)来完成它...不过我认为最好还是不用正则表达式解析它。 - Jaco Pretorius
1
这就是问题所在;正则表达式并不是语言/库无关的;那些无关的部分也做不到这一点。 - reinierpost

1

哎呀,错过了重点。忘掉其他的吧。这很难做到,因为正则表达式不擅长计算平衡字符(但是例如.NET实现有一个扩展可以做到,但有点复杂)。

您可以使用否定字符组来实现此目的。

[^'"]:[^'"]

你可以进一步将引号包装在非捕获组中。

(?:[^'"]):(?:[^'"])

或者你可以使用断言。

(?<!['"]):(?!['"])

0

你可以尝试捕获引号内的字符串

/(?<q>'|")([\w ]+)(\k<q>)/m

第一个模式定义了允许的引号类型,第二个模式获取所有的单词数字和空格。 这个解决方案非常好的一点是,它只获取开头和结尾引号匹配的字符串。

在regex101.com上试试吧


我认为这个正则表达式没有按要求执行:在引号内部的冒号不应该被分割。 - not2savvy
在问题中并不完全清楚,但我的正则表达式可以捕获所有的冒号。如果问题的作者也想捕获没有引号的部分,或许可以对其进行修改。 - Radon8472

0
我想出了以下有点令人担忧的结构:
(?<=^('[^']*')*("[^"]*")*[^'"]*):
它使用回顾断言来确保您从行开头到当前冒号匹配偶数引号。它允许在双引号内嵌入单引号,反之亦然。例如:
'a":b':c::"':"(在位置6、8和9处匹配)
编辑
Gumbo是正确的,在回顾断言中使用*是不允许的。

这个表达式只有在字符串以单引号开头时才会匹配,因为它使用了断言 (?<=^('[^...)。 - Daniel Brückner
@Daniel - ('[^']') 匹配单引号之间的零个或多个实例,因此它不必以引号开头。话虽如此,我的也坏了,看看我的编辑。 - heijp06
1
一般来说,回顾断言不允许使用诸如 * 之类的无限量词。 - Gumbo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接