Regex 使用正则表达式,如何有效地在双引号和嵌入双引号之间匹配字符串?

Regex 使用正则表达式,如何有效地在双引号和嵌入双引号之间匹配字符串?,regex,Regex,让我们有一个文本,其中我们希望匹配双引号之间的所有字符串;但在这些双引号中,可以引用双引号。例如: "He said \"Hello\" to me for the first time" 使用正则表达式,如何有效地匹配这些输入?匹配这些输入的一个非常有效的解决方案是使用normal*(special normal*)*模式;这个名字引自杰弗里·弗里德尔的优秀著作 通常,这是一种有用的模式,用于将由常规条目(正常部分)组成的输入与中间的分隔符(特殊部分)进行匹配 注意,就像所有的东西regex

让我们有一个文本,其中我们希望匹配双引号之间的所有字符串;但在这些双引号中,可以引用双引号。例如:

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

使用正则表达式,如何有效地匹配这些输入?

匹配这些输入的一个非常有效的解决方案是使用
normal*(special normal*)*
模式;这个名字引自杰弗里·弗里德尔的优秀著作

通常,这是一种有用的模式,用于将由常规条目(正常部分)组成的输入与中间的分隔符(特殊部分)进行匹配

注意,就像所有的东西regex一样,它应该在没有更好的选择时使用;例如,虽然可以使用此模式解析CSV数据,但如果使用Java,则最好使用OpenCSV

还请注意,虽然模式名称中的量词是星(即零或更多),但您可以根据需要更改它们

嵌入双引号的字符串 让我们再举一次上面的例子;请考虑此文本示例可能在您输入的任何地方:

"He said \"Hello\" to me for the first time"
不管你怎么努力,再多的“点加贪婪/懒惰量词”魔法也帮不了你解决这个问题。相反,将引号之间的输入分类为普通和特殊:

  • 正常情况下不是反斜杠或双引号:
    [^\\\”]
  • 特殊是反斜杠后跟双引号的顺序:
    \\“
将其替换为
normal*(special normal*)*
模式,将得到以下正则表达式:

[^\\"]*(\\"[^\\"]*)*
在前后添加双引号以匹配全文将生成最终正则表达式:

"[^\\"]*(\\"[^\\"]*)*"
您将注意到,这也将匹配带引号的空字符串

带破折号分隔符的单词 在这里,我们必须对量词使用变体,因为:

  • 我们不想要空话
  • 我们不想要以破折号开头的词
  • 当破折号出现时,它必须在另一个破折号之前至少有一个字母(如果有的话)
为了简单起见,我们还假设只允许使用小写ASCII字母

样本输入:

the-word-to-match
让我们再次分解为正常和特殊:

  • 普通:小写ASCII字母:
    [a-z]
  • 特殊:破折号:
    -
模式的标准形式为:

[a-z]*(-[a-z]*)*
但正如我们所说:

  • 我们不希望单词以破折号开头:第一个
    *
    应该变成
    +
  • 当发现破折号时,破折号后面至少应该有一个字母:第二个
    *
    应该变成
    +
我们最终得到:

[a-z]+(-[a-z]+)*
在其周围添加单词锚以获得最终结果:

\b[a-z]+(-[a-z]+)*\b
其他操作员变体 上面的示例仅限于将
*
替换为
+
,但当然,您可以根据自己的需要进行多种变体。一个非常经典的例子是IP地址:

  • 正常值最多为三位数(
    \d{1,3}
  • 特殊的是点:(
    \.
  • 第一个
    normal
    只出现一次,因此没有量词
  • (特殊正常*)
    中的
    normal
    也只出现一次,因此没有量词
  • 最后,
    (特殊正常*)
    部分正好出现三次,因此
    {3}
它给出了expresison(用单词锚修饰):

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

提示
  • 很可能您不需要(或不想)捕获重复部分(特殊正常*)部分);因此,建议您使用非捕获组。例如,对带引号的字符串使用
    “[^\\”]*(?:\\“[^\\”]*)*”
    。事实上,如果您需要,在这种情况下,捕获几乎永远不会得到所需的结果,因为重复一个捕获组只会给您最后一次捕获(所有以前的重复都将被覆盖),除非您在.NET中使用此模式。(谢谢@ohaal)

你不应该问一个问题,然后把它作为一个答案发布吗?我担心这不是一个真正的问题…这被称为“展开循环”(或者弗里德尔在他的书中称之为“展开循环”)。我认为你应该把这个(或其中的主要部分)转化为一个答案,因为这肯定不是一个问题。一个好的问题可能是“用嵌入引号匹配字符串的有效方法是什么?“或者类似的。好吧,我会回答这个问题。。。这是我第一次尝试这样的入口!现在呢?还有什么需要修改/改进的吗?@fge:为什么?你值得为此赢得声誉。当有足够多的人修改它时,它会自动变成CW。如果你真的想发疯,这个模式也可以应用到支持它们的正则表达式风格的递归循环中。看,使用非捕获组不是更有效吗?(参考
“[^\\”]*(\\“[^\\”]*)*”
)@ohaal,这是正确的(应该在提示部分或其他地方提及);但是,我想让事情相对简单一些。另外,我相信如果你不实际使用,一些正则表达式引擎会优化捕获组。@fge我随意更改了一些提示,因为我认为措辞有点漏洞百出。@fge只有极少数能优化它们,因为他们不知道你是否会使用它们稍后从引擎外部访问它们。此外,仅仅在模式周围添加捕获与此特定技术无关,因此我认为更重要的是关注模式内部的优化。
\b\d{1,3}(\.\d{1,3}){3}\b