使用正则expression式,如何有效地匹配双引号与embedded双引号之间的string?

让我们有一个我们想要匹配双引号之间的所有string的文本; 但在这些双引号内,可以引用双引号。 例:

"He said \"Hello\" to me for the first time" 

使用正则expression式,你如何有效地匹配这个?

匹配这种input的非常有效的解决scheme是使用normal* (special normal*)*模式; 这个名字是杰弗里·弗里德(Jeffrey Friedl) 精通正则expression式 ( Mastering Regular Expressions)的优秀着作引用的。

一般情况下,将由常规条目(正常部分)组成的input与中间的分隔符(特殊部分)匹配是有用的模式。

请注意,像所有的东西正则expression式,应该使用时没有更好的select; 而可以使用这种模式parsingCSV数据,例如,如果使用Java,则最好使用OpenCSV。

还要注意的是,尽pipe模式名称中的量词是星号(即0或更多),但您可以根据自己的需要更改它们。

embedded双引号的string

让我们再次拿上面的例子; 并请考虑这个文本示例可能在您input的任何地方:

 "He said \"Hello\" to me for the first time" 

不pipe你多努力,没有数量的“点加贪婪/懒惰的量词”的魔法将帮助你解决它。 相反,将报价之间的input分为正常和特殊:

  • 正常是什么,但反斜杠或双引号: [^\\"] ;
  • 特殊的是一个反斜杠的后面跟着一个双引号\\"

把这个代入normal* (special normal*)*模式,这给出了以下正则expression式:

 [^\\"]*(\\"[^\\"]*)* 

添加双引号以匹配全文给出最终的正则expression式:

 "[^\\"]*(\\"[^\\"]*)*" 

你会注意到这也会匹配空的引用string。

用破折号分隔符的单词

这里我们将不得不在量词上使用一个变体,因为:

  • 我们不想空洞的话,
  • 我们不想用短划线开始的话,
  • 当短划线出现时,如果有的话,它必须至less有一个字母在另一个短划线之前。

为了简单起见,我们还将假设只允许使用小写的ASCII字母。

示例input:

 the-word-to-match 

让我们再分解成正常和特殊的:

  • 正常:小写,ASCII字母: [az] ;
  • 特别的:破折号: -

该模式的规范forms将是:

 [az]*(-[az]*)* 

但正如我们所说:

  • 我们不想用短划线开头的词:第一个*应该变成+ ;
  • 当发现破折号后,至less应该有一个字母:第二个*应该成为+

我们结束了:

 [az]+(-[az]+)* 

在它周围添加单词锚点以获得最终结果:

 \b[az]+(-[az]+)*\b 

其他操作员变化

上面的例子限制了用*replace* ,但是当然你可以有任意多的变化。 一个超经典的例子是一个IP地址:

  • 正常最多三位数字( \d{1,3} ),
  • 特别是点( \. ),
  • 第一个normal只出现一次,因此没有量词,
  • (special normal*)内的(special normal*)也只出现一次,因此没有量词,
  • 最后(special normal*)部分正好出现三次,因此{3}

这给了expresison(装饰字锚):

 \b\d{1,3}(\.\d{1,3}){3}\b 

结论

这种模式的灵活性使其成为您的正则expression式工具箱中最有用的工具之一。 虽然存在许多问题,如果存在库,则不应使用正则expression式,但在某些情况下,必须使用正则expression式。 一旦你有了这个练习,这将成为你最好的朋友之一!

提示

  • 你不需要(或者想要)捕获重复的部分(special normal*)部分)。 因此build议您使用非捕获组。 例如,对于带引号的string,使用"[^\\"]*(?:\\"[^\\"]*)*" 。事实上,如果您想要,捕获几乎不会导致期望的结果这种情况下,因为重复一个捕获组将只会给你最后一次捕获(所有以前的重复将被覆盖),除非你在.NET中使用这种模式(谢谢@ ohaal)