让我们有一个文本,我们希望在双引号之间匹配所有字符串;但在这些双引号中,可以引用双引号。例如:
"He said \"Hello\" to me for the first time"
使用正则表达式,您如何有效地匹配它?
答案 0 :(得分:14)
匹配此类输入的一种非常有效的解决方案是使用normal* (special normal*)*
模式;这个名字引自Jeffrey Friedl的优秀着作,Mastering Regular Expressions。
这是一种模式,通常用于匹配由常规条目(正常部分)和中间的分隔符(特殊部分)组成的输入。
请注意,就像所有正则表达式一样,它应该在没有更好的选择时使用;虽然可以使用这种模式来解析CSV数据,例如,如果你使用Java,你最好使用OpenCSV。
另请注意,虽然模式名称中的量词是星号(即零或更多),但您可以根据需要改变它们。
让我们再看一遍上面的例子;请注意,此文本示例可能位于您输入的任何位置:
"He said \"Hello\" to me for the first time"
无论你怎么努力,没有多少“点加上贪婪/懒惰量词”魔法会帮助你解决它。相反,将引号之间的输入分类为正常和特殊:
[^\\"]
; \\"
。将其替换为normal* (special normal*)*
模式,这给出了以下正则表达式:
[^\\"]*(\\"[^\\"]*)*
添加双引号以匹配全文给出了最终的正则表达式:
"[^\\"]*(\\"[^\\"]*)*"
您会注意到这也会匹配空的引用字符串。
在这里,我们将不得不在量词上使用变体,因为:
为简单起见,我们还假设只允许使用小写的ASCII字母。
示例输入:
the-word-to-match
让我们再次分解为正常和特殊:
[a-z]
; -
该模式的规范形式将是:
[a-z]*(-[a-z]*)*
但正如我们所说:
*
应该变为+
; *
应变为+
。我们最终得到:
[a-z]+(-[a-z]+)*
在其周围添加单词锚以获得最终结果:
\b[a-z]+(-[a-z]+)*\b
上述示例仅限于将*
替换为+
,但当然您可以根据需要添加任意数量的变体。一个超经典的例子是IP地址:
\d{1,3}
),\.
),normal
只出现一次,因此没有量词,normal
内的(special normal*)
也只出现一次,因此没有量词,(special normal*)
部分恰好出现了三次,因此{3}
。给出了expresison(用词锚装饰):
\b\d{1,3}(\.\d{1,3}){3}\b
此模式的灵活性使其成为正则表达式工具箱中最有用的工具之一。虽然存在许多问题,如果存在库,则不应使用正则表达式,在某些情况下,必须使用正则表达式。一旦你练习了一下,这将成为你最好的朋友之一!
(special normal*)
部分);因此,建议您使用非捕获组。例如,对引用的字符串使用"[^\\"]*(?:\\"[^\\"]*)*"
。事实上,如果你想要它,在这种情况下捕获几乎永远不会产生预期的结果,因为重复捕获组只会给你最后捕获(所有先前的重复将被覆盖),除非你在.NET中使用这种模式。 (感谢@ohaal)