协助正则表达式

时间:2013-03-19 12:07:23

标签: php regex

我感到羞愧,但我仍然不清楚一些正面的方面。 我需要解析包含许多@"I'm a string"格式的字符串文字的文本文件。 我编写了简单的模式/@"([^"]*)"/si。它工作正常,preg_match_all返回一个集合。但是,如果字符串文字包含@"I'm plain string. I'm \"qouted\" string "之类的转义引号,它显然无法正常工作。感谢任何线索。

2 个答案:

答案 0 :(得分:2)

这是Freidl经典“展开循环”的一个用例:( EDIT 固定分组用于捕获)

/"((?:[^"\\]|\\.)*)"/

这将匹配引用的字符串,并将反斜杠转义的引号考虑在内。

用于匹配字段(包括@)的完整正则表达式将是:

/@"((?:[^"\\]|\\.)*)"/

但要小心!我经常看到人们抱怨这种模式在PHP中不起作用,这是因为在字符串中使用反斜杠会略微融化思维。

上述模式中的反斜杠表示需要传递到PCRE的文字反斜杠。这意味着在PHP字符串中使用它们时需要进行双重转义:

$expr = '/@"((?:[^"\\\\]|\\\\.)*)"/';

preg_match_all($expr, $subject, $matches);

print_r($matches[1]); // this will show the content of all the matched fields

See it working

它是如何运作的?

......我听到你问。那么,让我们看看我是否能够以一种真正有意义的方式解释这一点。让我们启用x模式,这样我们就可以将它分开:

/
  @             # literal @
  "             # literal "
    (           # start capture group, we want everything between the quotes
      (?:       # start non-capturing group (a group we can safely repeat)
        [^"\\]  # match any character that's not a " or a \
        |       # ...or...
        \\.     # a literal \ followed by any character
      )*        # close non-capturing group and allow zero or more occurrences
    )           # close the capture group
  "             # literal "
/x

这些非常重要的一点是:

  • [^"\\]|\\. - 意味着每个反斜杠都是“平衡的” - 每个反斜杠都必须转义一个字符,并且不会多次考虑任何字符。
  • *重复组中包含上述内容意味着上述模式可以无限次发生,并且允许空字符串(如果您不想允许空字符串,请更改{{ 1}}到*)。这是“展开循环”的“循环”部分。

但是输出字符串仍然包含逃避引号的反斜杠?

确实如此,这只是一个匹配程序,它不会修改匹配。但由于结果是字符串的内容,因此简单的+将是安全的并产生正确的结果。

然而,在做这种事情时,我经常发现我也想处理其他转义序列 - 在这种情况下,我通常会对结果执行类似的操作:

str_replace('\\"', '"', $result)

这给出了与PHP中双引号字符串类似的行为,其中 preg_replace_callback('/\\./', function($match) { switch ($match[0][1]) { // inspect the escaped character case 'r': return "\r"; case 'n': return "\n"; case 't': return "\t"; case '\\': return '\\'; case '"': return '"'; default: // if it's not a valid escape sequence, treat the \ as literal return $match[0]; } }, $result); 替换为制表符,\t替换为换行符,依此类推。

如果我还想允许单引号字符串怎么办?

很长一段时间以来,这一直困扰着我。我总是有一种傻笑的感觉,这可以通过反向引用更有效地处理,但是许多尝试都未能产生任何可行的结果。

我这样做:

\n

正如您所看到的,这基本上只是两次使用基本相同的模式,具有OR关系。这也使PHP端的字符串提取变得非常复杂:

/(?:"((?:[^"\\]|\\.)*)")|(?:'((?:[^'\\]|\\.)*)')/

答案 1 :(得分:0)

你需要使用负面的lookbehind - 匹配所有内容,直到找到一个前面没有反斜杠的引号。这是在java:

public static void main(String[] args) {
    final String[] strings = new String[]{"@\"I'm a string\"", "@\"I'm plain string. I'm \\\"qouted\\\" \""};

    final Pattern p = Pattern.compile("@\"(.*)\"(?<!\\\\)");
    System.out.println(p.pattern());

    for (final String string : strings) {
        final Matcher matcher = p.matcher(string);
        while (matcher.find()) {
            System.out.println(matcher.group(1));
        }
    }
}

输出:

I'm a string
I'm plain string. I'm \"qouted\" 

模式(没有所有Java转义)是:@"(.*)"(?<!\\)