【问题标题】:Is there a way, using regular expressions, to match a pattern for text outside of quotes?有没有办法使用正则表达式来匹配引号之外的文本模式?
【发布时间】:2009-04-09 20:28:39
【问题描述】:

如标题所述,有没有一种方法可以使用正则表达式来匹配出现在引号之外的文本的文本模式。理想情况下,给定以下示例,我希望能够匹配引号之外的逗号,而不是引号中的逗号。

这是一些文本,后跟“文本,带引号!”

这是一些文字,后面是“文字,带引号”,还有更多的“文字,带引号!”

此外,如果表达式能够像下面的示例中那样尊重嵌套引号,那就太好了。但是,如果这在技术上不适用于正则表达式,那么很高兴知道是否是这种情况。

程序员从办公桌上抬起头,“这可不好,”他惊呼道,“系统在说‘找不到文件!’”

我找到了一些表达式来匹配引号中的内容,但没有完全匹配引号之外的内容。

【问题讨论】:

  • 在“..1..”..2..“..3..”中,我们是否假设 2 不在引号内?
  • 我认为您无法使用正则表达式获得完整的解决方案,因为您必须跟踪开盘价和收盘价。
  • @Daniel / Mel - 这是我正在考虑的事情,现在我不需要它是完美的,但如果这是一个不能真正做好的情况,那么其他必须采取解决问题的途径。
  • 你需要什么语言的?
  • 必须是正则表达式吗?执行此搜索的状态机很简单(它有 2 个状态,如果您需要使用反斜杠进行引号转义,则有 4 个状态),并且可能更具可读性。

标签: regex


【解决方案1】:

最简单的方法是同时匹配逗号和带引号的字符串,然后过滤掉带引号的字符串。

/"[^"]*"|,/g

如果你真的不能让引号匹配,你可以这样做:

/,(?=[^"]*(?:"[^"]*"[^"]*)*\Z)/g

这可能会变慢,因为对于每个逗号,它必须查看剩余的字符并计算引号的数量。 \Z 匹配字符串的结尾。类似于$,但永远不会匹配行尾。

如果您不介意额外的捕获组,则可以这样做:

/\G((?:[^"]*"[^"]*")*?[^"]*?)(,)/g

这只会扫描字符串一次。它从字符串的开头计算引号。 \G 将匹配上次匹配结束的位置。


最后一个模式可能需要一个例子。

Input String: 'This is, some text, followed by "text, in quotes!" and more ,-as'
Matches:
1. ['This is', ',']
2. [' some text', ',']
3. [' and followed by "text, in quotes!" and more ', ',']

它匹配逗号前的字符串以及逗号。

【讨论】:

    【解决方案2】:

    由于存在大量对正则表达式引擎的黑客攻击,这可以通过现代正则表达式完成,但让我成为发布“不要使用正则表达式执行此操作”答案的人。

    不是正则表达式的工作。这是一个成熟的解析器的工作。作为一个你不能用(经典)正则表达式做的事情的例子,考虑一下:

    ()(())(()())
    

    没有(经典)正则表达式可以确定这些括号是否正确匹配,但是没有正则表达式这样做是微不足道的:

    /* C code */
    
    char string[] = "()(())(()())";
    int parens = 0;
    for(char *tmp = string; tmp; tmp++)
    {
      if(*tmp == '(') parens++;
      if(*tmp == ')') parens--;
    }
    if(parens > 0)
    {
      printf("%s too many open parenthesis.\n", parens);
    }
    else if(parens < 0)
    {
      printf("%s too many closing parenthesis.\n", -parens);
    }
    else
    {
      printf("Parenthesis match!\n");
    }
    
    # Perl code
    
    my $string = "()(())(()())";
    my $parens = 0;
    for(split(//, $string)) {
      $parens++ if $_ eq "(";
      $parens-- if $_ eq ")";
    }
    die "Too many open parenthesis.\n" if $parens > 0;
    die "Too many closing parenthesis.\n" if $parens < 0;
    print "Parenthesis match!";
    

    看看编写一些非正则表达式代码来为您完成这项工作是多么简单?

    编辑:好的,从冒险乐园回来。 :) 试试这个(用 Perl 写的,如果你不了解 Perl,评论帮助你理解我在做什么):

    # split $string into a list, split on the double quote character
    my @temp = split(/"/, $string);
    
    # iterate through a list of the number of elements in our list
    for(0 .. $#temp) {
    
      # skip odd-numbered elements - only process $list[0], $list[2], etc.
      # the reason is that, if we split on "s, every other element is a string
      next if $_ & 1;
    
      if($temp[$_] =~ /regex/) {
        # do stuff
      }
    
    }
    

    另一种方法:

    my $bool = 0;
    my $str;
    my $match;
    
    # loop through the characters of a string
    for(split(//, $string)) {
    
      if($_ eq '"') {
        $bool = !$bool;
        if($bool) {
    
          # regex time!
          $match += $str =~ /regex/;
    
          $str = "";
        }
      }
    
      if(!$bool) {
    
        # add the current character to our test string
        $str .= $_;
      }
    }
    
    # get trailing string match
    $match += $str =~ /regex/;
    

    (我给出两个是因为,在另一种语言中,一种解决方案可能比另一种更容易实施,而不仅仅是因为 There's More Than One Way To Do It™。)

    当然,随着您的问题变得越来越复杂,构建一个成熟的解析器会带来一些好处,但那是另一回事。目前,这就足够了。

    【讨论】:

    • 看起来这是我要去的方向,但是能够使用正则表达式(假设它很快)做到这一点会很好,因为我仍在尝试做一个正则表达式对于引号之外的内容。
    • 我接受了这个 12 岁的建议,这是值得的。一直在用正则表达式把我的头撞到墙上,但是在这上面扔一些算法和数据结构 101 会产生更好的结果。
    【解决方案3】:

    如前所述,regexp cannot match any nested pattern,因为它不是Context-free language

    因此,如果您有任何嵌套引号,则不会使用正则表达式来解决此问题。
    (除了 .Net 正则表达式引擎的“balancing group”功能 - 正如 cmets 中的 Daniel L 所提到的那样 - 但我在这里没有对正则表达式风格做出任何假设)

    除非您添加进一步的规范,例如必须对引号中的引号进行转义。

    在这种情况下,如下:

    text before string "string with \escape quote \" still
    within quote" text outside quote "within quote \" still inside" outside "
    inside" final outside text
    

    将成功匹配:

    (?ms)((?:\\(?=")|[^"])+)(?:"((?:[^"]|(?<=\\)")+)(?<!\\)")?
    
    • group1:引用文本之前的文本
    • group2:双引号内的文本,即使其中存在\"

    【讨论】:

    • .net 正则表达式引擎不支持匹配平衡引号、括号等内容吗?
    • PCRE 支持递归正则表达式,但它们确实令人困惑,并且可能仍处于试验阶段。我认为最简单的是 (?R),它是整个模式的递归匹配。因此,匹配的括号将是 /((?:[^()]|(?R))*)/ 与 Perl 正则表达式。但这看起来比平时更可怕。
    【解决方案4】:

    这是一个得到匹配的表达式,但它并不完美,因为它得到的第一个匹配是整个字符串,删除了最后的 "。

    [^"].*(,).*[^"]
    

    我一直在使用我的Free RegEx tester 来看看有什么用。

    测试结果

    Group Match Collection # 1
    Match # 1
    Value: This is some text, followed by "text, in quotes!
    Captures: 1
    
    Match # 2
    Value: ,
    Captures: 1
    

    【讨论】:

      【解决方案5】:

      你最好自己构建一个简单的解析器(伪代码):

      quoted := False
      FOR char IN string DO
          IF char = '"'
              quoted := !quoted
          ELSE
              IF char = "," AND !quoted
                  // not quoted comma found
              ENDIF
          ENDIF
      ENDFOR
      

      【讨论】:

      • 那里有漂亮的 Pascal 代码。 =P 不是想成为一个混蛋,但我从未见过有人在伪代码中使用 := 进行赋值。
      【解决方案6】:

      这实际上取决于您是否允许嵌套引号。

      理论上,使用嵌套引号你不能这样做(常规语言不能算)

      在实践中,如果可以限制深度,您可能会做到。随着复杂性的增加,它会变得越来越难看。这通常是人们对正则表达式感到悲痛的原因(试图匹配通常不是正则的东西)。

      请注意,一些“正则表达式”库/语言添加了非常规功能。

      如果这种事情变得足够复杂,你真的必须为它编写/生成一个解析器。

      【讨论】:

      • 特别是对于我的问题,需要尊重嵌套引号,但我可以预见其他开发人员可能需要这样做的情况,他们不需要尊重嵌套引号,因此信息可能对他们有用。
      【解决方案7】:

      您需要在描述中添加更多内容。你想要任何一组可能的带引号的字符串和不带引号的字符串吗...

      Lorem ipsum "dolor sit" amet, "consectetur adipiscing" elit.

      ...或者只是您要求的模式?我认为这非常接近......

      (?<outside>.*?)(?<inside>(?=\"))
      

      但它确实捕获了 "。

      【讨论】:

        【解决方案8】:

        也许您可以分两步完成?
        首先你替换引用的文本:

        ("[^"]*")
        

        然后你从剩下的字符串中提取你想要的东西

        【讨论】:

          【解决方案9】:
          ,(?=(?:[^"]*"[^"]*")*[^"]*\z)
          

          正则表达式可能无法计数,但它们可以确定某物的数量是奇数还是偶数。找到逗号后,前瞻断言,如果前面有任何引号,则它们的数量是偶数,这意味着逗号不是在一组引号内。

          如果需要,可以对其进行调整以处理转义的引号,尽管最初的问题没有提到这一点。此外,如果您的正则表达式支持它们,我会添加原子组或所有格量词以保持回溯检查。

          【讨论】:

            猜你喜欢
            • 2019-02-11
            • 1970-01-01
            • 1970-01-01
            • 2014-12-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多