【问题标题】:How can I capture an escaped " but not an unescaped one?我怎样才能捕获一个逃脱的“但不是一个未逃脱的?
【发布时间】:2010-02-01 06:18:55
【问题描述】:

假设需要被正则表达式捕获的部分由以下字符串中的PORTION指示

,"PORTION","","a",["some_string"]  

PORTION 的例子是

  • \"abc123
  • abc123\"
  • \"abc123\"
  • abc\"123\"
  • abc123

所以字符串实际上看起来像

  • ,"\"abc123","","a",["some_string"]
  • ,"abc123\" ","","a",["some_string"]
  • "\"abc123\"","","a",["some_string"]
  • "abc\"123\"","","a",["some_string"]
  • "abc123","","a",["some_string"]

PORTION 用双引号括起来。 PORTION 中的双引号用反斜杠转义。我目前的模式是

my $pattern = '(.?([\\"]|[^"][^,][^"])*)';

上面例子的结果如下

  • \"abc123","","a"
  • abc123
  • \"abc12
  • abc\"123\""
  • abc123"

该模式尝试匹配序列前面不是“,”的所有内容
并且还允许捕获 \"
但它没有按预期工作。 我怎样才能让它发挥作用?

【问题讨论】:

    标签: regex perl


    【解决方案1】:

    你把它弄得太复杂了;没有规则说您必须在一个单一的正则表达式中完成所有解析。由于您的字符串看起来像逗号分隔的序列,因此首先对其进行解析:

    my @fields = split /(?<!\\),/, $string;   # use comma as a delimiter (except when escaped)
    

    ...然后相应地解析您的第一个字段:

    shift @fields unless $fields[0];     # pull off the potentially null first field
    $fields[0] =~ s/^"//g;               # remove the leading "
    $fields[0] =~ s/(?<!\\)"$//g;        # remove the trailing " that isn't preceded by a \
    

    您可以通过将上述代码包装在 for 循环或 map() 中来解析所有字段。

    请注意,此代码不考虑诸如\\, 之类的事件(逗号在这里是有效的分隔符,即使它会错误地通过正则表达式)。因此,最好为您的格式使用适当的解析器(无论它是什么)。你可能想看看Text::CSV

    【讨论】:

    • 如果其中一个字段包含逗号,则会中断。假设这是真的可能是安全的,但也可能不是。
    • @eaolson:OP 没有指定这是否有效。正如我所指出的,解析转义字符的复杂性需要比简单的正则表达式提供的更复杂的解析器。需要从字符串的开头开始并单独解释每个字符。考虑一长串后跟分号的 backslaxhes - 它是否被转义?必须从序列的开头开始计数才能知道。
    • 使用 CSV 解析器会考虑引号。
    • +1 温馨提醒,并非所有解析都需要在一个单一的正则表达式中完成
    • 当我在上面的评论中说“分号”时,我的意思是“双引号”:)
    【解决方案2】:

    只需使用Text::CSV

    【讨论】:

      【解决方案3】:

      您的问题需要臭名昭著的零宽度否定后视断言

      ...它可以让您匹配一个跟随barfoo

      文档在这里:http://perldoc.perl.org/perlre.html#Extended-Patterns

      你想要在你的正则表达式中这样的东西:

      "(.+?)(?<!\\)"
      

      匹配一个双引号,尽可能少的任何字符,然后是另一个前面没有反斜杠的双引号(我认为是通过加倍转义)。第一组括号按您的意图捕获,第二个括号没有捕获。

      编辑:同时使用http://www.internetofficer.com/seo-tool/regex-tester/进行测试 它似乎工作正常。

      编辑: 正如 outis 指出的那样,此表达式将无法正确匹配 PORTION,其中右引号之前的最后一个字符是转义的反斜杠。如果您预计文本中没有反斜杠,您应该没问题。

      【讨论】:

        【解决方案4】:

        不要忘记允许转义的反斜杠和转义的引号。使用 RE 来匹配平衡的任何东西都会很快变得丑陋:

        /(?<=")((?:[^"\\]+|\\+[^"\\]|(?:\\\\)+|(?<!\\)\\(?:\\\\)*")*)(?=")/
        

        按照 Ether 的建议,帮自己一个忙并使用解析器。

        【讨论】:

        • 转义的反斜杠并不难处理:(?&gt;[^"\\]+|\\.)*。您也可以将其写为(?&gt;[^"\\]+|\\["\\])*,但点不会导致任何问题,并且它会处理您可能想要支持的任何其他反斜杠转义序列。
        【解决方案5】:

        如果您的数据以逗号分隔并且没有嵌入逗号,则只需在“,”上拆分并获取相应的字段

        while(<>){
            chomp;
            @s = split /,/;
            if ($s[0] eq ""){
                print "$s[1]\n";
            }else{
                print $s[0]."\n";
            }
        }
        

        输出

        $ perl perl.pl file
        "\"abc123"
        "abc123\" "
        "\"abc123\""
        "abc\"123\""
        "abc123"
        

        【讨论】:

          【解决方案6】:

          如果你需要考虑outis提到的转义反斜杠,你可以使用这个:

          m/"((\\\\|\\"|[^"])+)"/

          (我似乎无法对 outis 的回答发表评论,但 outis 解决方案不适用于此:

          "abc\\\"123"

          会产生

          abc\\\

          )

          输入:

          ,"\"abc123","","a",["some_string"] ,"abc123\" ","","a",["some_string"] "\"abc123\"","","a",["some_string"] "abc\"123\"","","a",["some_string"] "abc123","","a",["some_string"] "ab\\c123","","a",["some_string"] "abc123\\","","a",["some_string"] "abc123\\\"","","a",["some_string"] "abc\\\"123\"","","a",["some_string"] "abc123\\\\"","","a",["some_string"]

          输出:

          \"abc123 abc123\" \"abc123\" abc\"123\" abc123 ab\\c123 abc123\\ abc123\\\" abc\\\"123\" abc123\\\\\"

          【讨论】:

            猜你喜欢
            • 2011-01-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-05-28
            • 1970-01-01
            • 2023-02-24
            • 2021-01-27
            相关资源
            最近更新 更多