【问题标题】:Regular expression: dot with exclusion includes excluded char正则表达式:带排除的点包括排除的字符
【发布时间】:2021-08-27 13:54:04
【问题描述】:

下面的例子:

    $text = <<<END
some text

q 1
some next line

q 2 test
exptra line
END;

    $text   = trim($text);
    if (preg_match_all("/\nQ\s+\d+.[^\n]*\n/isU", $text, $match)) {
        print_r($match);
        echo "OK";
    } else {
        echo "FAIL";
    }

输出:

Array
(
    [0] => Array
        (
            [0] => 
q 1
some next line

            [1] => 
q 2 test

        )

)
OK

请注意,$text 示例中的新行仅包含一个 \n

出于某种原因.[^\n]* 包含换行符,即使它被声明为排除。仅当换行符是与这部分表达式匹配的唯一符号(\n 紧跟在 1 之后)和星号(*)时才会发生,这应该意味着包括无/可选在内的任意数量的字符都不会在此处播放正确。

由于我需要匹配这两种情况,在这种情况下可以做什么?

预期的输出,匹配以q N 开头但不包含以下行的两行:

Array
(
    [0] => Array
        (
            [0] => 
q 1 

            [1] => 
q 2 test

        )

)

上面的例子是简化的。匹配的字符串可以包含换行符,但不能在指定位置。实际上我用其他符号测试了.,它的工作原理是一样的。如果 dot 剩下的唯一字符是表示为 exclude 的字符 - 它仍然包含它:

    $text = <<<END
some text

q 1e
some next line

q 2 test
exptra line
END;

    $text   = trim($text);
    if (preg_match_all("/Q\s+\d+.[^e]*/iU", $text, $match)) {
        print_r($match);
        echo "OK";
    } else {
        echo "FAIL";
    }

输出:

Array
(
    [0] => Array
        (
            [0] => q 1e
            [1] => q 2 
        )

)

【问题讨论】:

  • . 匹配 \n 因为你有 s 标志。 [^\n]*matches 或更多非LF,因此它不限制. 这里。您希望在这里得到什么?
  • 试试preg_match_all('~^Q\h+\d.*~im', $text, $matches)。见the regex demo
  • 添加了预期的输出。来自s 修饰符文档A negative class such as [^a] always matches a newline character,因此对于这种情况,s 修饰符是否到位并不重要。 \n 声明为“排除”字符,不应像第二种情况一样包含在内。
  • s 标志修改了. 的行为,而不是[^a],您对这里的文档感到困惑。
  • 我的意思是 .[^a] 根据文档无论如何都包含换行符,无论 s 是否存在。

标签: php regex


【解决方案1】:

您的/Q\s+\d+.[^e]*/iU 正则表达式包含一个U (PCRE_UNGREEDY) 标志,用于交换量词的贪婪,并且等于/Q\s+?\d+?.[^e]*?/i 模式。 /Q\s+?\d+?.[^e]*?/i 匹配 qQ,然后是任何一个或多个但尽可能少的空白字符,然后是一个或多个但尽可能少的数字,然后是任何字符(如果 @987654328 则为换行符除外@ 标志被省略)然后 - 看看 - e之外的任何零个或多个字符尽可能少(即它不匹配任何文本,因为它确实如此不必)。

你可以使用

<?php

$text = <<<END
some text

q 1
some next line

q 2 test
exptra line
END;

if (preg_match_all('~^Q\h+\d.*~im', $text, $match)) {
    print_r($match);
    echo "OK";
} else {
    echo "FAIL";
}

请参阅PHP demo。输出:

Array
(
    [0] => Array
        (
            [0] => q 1
            [1] => q 2 test
        )

)
OK

图案细节

  • ^ - 行首(由于m 标志)
  • Q - Qq(由于 i 标志)
  • \h+ - 一个或多个水平空格
  • \d - 一个数字
  • .* - 除了换行符之外的任何零个或多个字符(因为我没有使用 s 标志)尽可能多(即行的其余部分)。

【讨论】:

  • 对不起,无法接受答案。上面的例子被简化了。匹配的字符串可以包含换行符,但不能在指定位置。实际上我用其他符号测试了.,它的工作原理是一样的。如果 dot 剩下的唯一字符是表示为 exclude 的字符 - 它仍然包含它。请参阅添加的示例。
  • @Index 如果您用简单的英语解释您的模式要求,为您提供 100% 有效的解决方案会容易得多。你的例子并没有明确你真正想要达到的目标。请查看我的模式详细信息并使用类似的样式编译一组需求。
  • @Index 我会解释为什么你会得到这些匹配,也许它会给你一个提示。
【解决方案2】:

修饰符s 切换. 的行为。因为您包含它,所以 . 将匹配 \n

一个小演示:

$patterns = [
  '(.*)', '(.*)s', '([^\\n]*)'
];

foreach ($patterns as $pattern) {
    preg_match($pattern, "foo\nbar", $match);
    echo $pattern, ': "', $match[0], "\"\n";
}

输出:

(.*): "foo"
(.*)s: "foo
bar"
([^\n]*): "foo"

修饰符m 更适合您的情况。它改变了^$ 的行为以匹配行(而不仅仅是字符串)开始/结束。

$pattern = '(
  ^q   # letter q or Q at line start (with modifier m)
  \\s+  # at least one whitespace 
  (?<number>\\d+) # string of digits - named group "number"
  (?<label>[^\\n]*) # any character except "\n" (without modifier s) - named group "label"
)xmi';

if (preg_match_all($pattern, $text, $match, PREG_SET_ORDER)) {
    print_r($match);
}

输出:

Array
(
    [0] => Array
        (
            [0] => q 1
            [number] => 1
            [1] => 1
            [label] => 
            [2] => 
        )

    [1] => Array
        (
            [0] => q 2 test
            [number] => 2
            [1] => 2
            [label] =>  test
            [2] =>  test
        )

)

修饰符x 启用“扩展语法”。它允许格式化和记录模式。

我建议对结果使用命名组。它使代码更具可读性和健壮性。 PREG_SET_ORDER 将每场比赛的数据放入一个集合中。

【讨论】:

    猜你喜欢
    • 2021-09-16
    • 2016-11-27
    • 2023-03-04
    • 2022-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-28
    相关资源
    最近更新 更多