让我们来看一个文本,我们想匹配所有双引号之间的字符串;但在这些双引号之间,可能会有被引用的双引号。例如:
"He said \"Hello\" to me for the first time"
使用正则表达式,如何高效匹配此内容?
让我们来看一个文本,我们想匹配所有双引号之间的字符串;但在这些双引号之间,可能会有被引用的双引号。例如:
"He said \"Hello\" to me for the first time"
使用正则表达式,如何高效匹配此内容?
匹配此类输入的非常有效的解决方案是使用normal* (special normal*)*
模式;这个名称来自Jeffrey Friedl的优秀书籍《精通正则表达式》。
一般来说,这是一个有用的模式,用于匹配由常规条目(正常部分)和之间分隔符(特殊部分)组成的输入。
请注意,与所有正则表达式一样,只有在没有更好选择时才应使用它;例如,虽然您可以使用此模式解析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
只出现一次,因此没有量词,(special normal*)
中的normal
也只出现一次,因此没有量词,(special normal*)
部分恰好出现三次,因此使用{3}
。这样就得到了一个带有单词锚点的表达式:
\b\d{1,3}(\.\d{1,3}){3}\b
这种模式的灵活性使其成为您正则表达式工具箱中最有用的工具之一。虽然存在许多问题,如果存在库,则不应使用正则表达式,但在某些情况下,您必须使用正则表达式。一旦您练习了一段时间,它将成为您最好的朋友之一!
(special normal*)
部分),因此建议使用非捕获组。例如,对于引号字符串,请使用 "[^\\"]*(?:\\"[^\\"]*)*"
。实际上,在这种情况下,除非您在.NET中使用此模式,否则捕获几乎永远不会产生所需的结果,因为重复一个捕获组只会给出最后一个捕获(所有先前的重复都将被覆盖)。 (感谢@ohaal)