php pcre 没有定界符的正则表达式
php pcre regular expressions without delimiter
背景/简介
通常,PHP 中的正则表达式,例如对于 preg_match()
,以分隔符开始和结束,例如 /
。我个人经常使用 @
代替。
除了分隔字符,还可以使用左括号和右括号,例如 ()
、{}
或 []
.
分隔符char需要被转义,当它应该被解释为常规字符时。例如。 preg_match('@^\w+\@\w+\.\w+$@', $mail)
需要将 '@'
转义为 '\@'
.
函数 preg_quote(string $str, ?string $delimiter)
接受 null
作为 $delimiter
,这表明可以用我们不必担心分隔符的方式来编写正则表达式。
使用 ()
似乎我们不必担心分隔符,因为 '('
和 ')'
已经需要转义了。
[]
和 {}
有点不同。孤儿 '['
会导致错误,而孤儿 ']'
、'{'
或 '}'
不会。
动机
对于包开发,我想提供用户可以指定正则表达式片段的方法,而不用担心分隔符的选择。
例如如果我在内部使用 '/'
作为分隔符,那么用户(调用代码)将需要在提供的正则表达式片段中转义 '/'
。如果我使用 '@'
,他们可以不转义 '/'
,但需要转义 '@'
。如果我使用 null
/ '()'
,他们将不需要转义任何东西 - 我认为。
这是一个假想的例子。请不要问->setFragment()
是干什么的,你只需要知道第二个参数接收一个正则表达式片段(即可以插入正则表达式的片段)即可。
// If regex like '/../':
$system->setFragment('email', '\w+@\w+\.\w+'); // nothing escaped.
$system->setFragment('dir', '\w+(\/\w+)*'); // '/' escaped.
// If regex like '@..@':
$system->setFragment('email', '\w+\@\w+\.\w+'); // '@' escaped.
$system->setFragment('dir', '\w+(/\w+)*'); // nothing escaped.
// If regex like '(..)':
$system->setFragment('email', '\w+@\w+\.\w+'); // nothing escaped.
$system->setFragment('dir', '\w+(/\w+)*'); // nothing escaped.
另一个例子,更类似于我实际在做的事情:
function buildMessageRegex(string $message, ?string $delimiter, array $regex_fragments = []): string {
$quoted_message = preg_quote($message, $delimiter);
$regex_body = strtr($quoted_message, $replacements);
return $delimiter !== null
? $delimiter . '^' . $regex_body . '$' . $delimiter
: '(^' . $regex_body . '$)';
}
// By using $delimiter === null, we don't have to escape '/' or '@'.
$regex = buildMessageRegex('Mail: %mail, Dir: %dir.', null, [
'%mail' => '\w+@\w+\.\w+',
'%dir' => '\w+(/\w+)*',
]);
问题
似乎 ()
是编写正则表达式的唯一方法,我不必担心分隔符,并且可以调用 preg_quote($str, null)
并将 null 作为分隔符。
这个假设是否正确?
如果是这样,我总是可以使用 ()
作为分隔符,而不需要在方法中提供分隔符选项。
还是我遗漏了什么?
范围
我不确定这个 problem/question 是否特定于 PHP,或者更普遍地适用于任何使用它的 PCRE(我假设是在 Perl 中?)。
我个人对 PHP 案例很感兴趣,但我认为值得一提的是在一个很好的答案中这如何适用于 PHP.
不幸的是,您认为 (
和 )
总是需要转义的逻辑是不正确的。它们通常不需要在 []
内进行转义,但如果 ()
是定界符,则需要进行转义。
例如:
preg_match('/[(]/', "foo(bar", $match);
有效,但是
preg_match('([(])', "foo(bar", $match);
收到“未找到结束匹配定界符 ')'”错误。
因此,如果您使用 ()
作为分隔符,调用者将需要转义 []
中的那些字符,这通常不需要。
比对您的问题的具体回答更实用的解决方案。
许多字符都可以用作模式分隔符,包括来自 ascii 范围的 non-printable 个字符:SOH、STX、ETX、EOT、ENQ、ACK ...
它们不太可能在字符串中找到,用户更不可能在键盘上键入它们(如果用户真的决定在模式中加入 SOH,他可能会使用转义符序列 \x01
看东西)。
因此,您可以通过这种方式合理地构建模式(例如使用 SOH):
$pattern = chr(1) . $body . chr(1) . $modifiers;
如果你寻找比SOH(开始标题U+0001)更有意义的东西,你最终可以选择控制字符RS(记录分隔符U+0030)或EOT(传输结束U+0004)。请注意,您不能使用 NUL (U+0000)。
显然,可以肯定的是,无论您选择什么分隔符,总有这两个很好的旧解决方案:转义它或删除它。
背景/简介
通常,PHP 中的正则表达式,例如对于 preg_match()
,以分隔符开始和结束,例如 /
。我个人经常使用 @
代替。
除了分隔字符,还可以使用左括号和右括号,例如 ()
、{}
或 []
.
分隔符char需要被转义,当它应该被解释为常规字符时。例如。 preg_match('@^\w+\@\w+\.\w+$@', $mail)
需要将 '@'
转义为 '\@'
.
函数 preg_quote(string $str, ?string $delimiter)
接受 null
作为 $delimiter
,这表明可以用我们不必担心分隔符的方式来编写正则表达式。
使用 ()
似乎我们不必担心分隔符,因为 '('
和 ')'
已经需要转义了。
[]
和 {}
有点不同。孤儿 '['
会导致错误,而孤儿 ']'
、'{'
或 '}'
不会。
动机
对于包开发,我想提供用户可以指定正则表达式片段的方法,而不用担心分隔符的选择。
例如如果我在内部使用 '/'
作为分隔符,那么用户(调用代码)将需要在提供的正则表达式片段中转义 '/'
。如果我使用 '@'
,他们可以不转义 '/'
,但需要转义 '@'
。如果我使用 null
/ '()'
,他们将不需要转义任何东西 - 我认为。
这是一个假想的例子。请不要问->setFragment()
是干什么的,你只需要知道第二个参数接收一个正则表达式片段(即可以插入正则表达式的片段)即可。
// If regex like '/../':
$system->setFragment('email', '\w+@\w+\.\w+'); // nothing escaped.
$system->setFragment('dir', '\w+(\/\w+)*'); // '/' escaped.
// If regex like '@..@':
$system->setFragment('email', '\w+\@\w+\.\w+'); // '@' escaped.
$system->setFragment('dir', '\w+(/\w+)*'); // nothing escaped.
// If regex like '(..)':
$system->setFragment('email', '\w+@\w+\.\w+'); // nothing escaped.
$system->setFragment('dir', '\w+(/\w+)*'); // nothing escaped.
另一个例子,更类似于我实际在做的事情:
function buildMessageRegex(string $message, ?string $delimiter, array $regex_fragments = []): string {
$quoted_message = preg_quote($message, $delimiter);
$regex_body = strtr($quoted_message, $replacements);
return $delimiter !== null
? $delimiter . '^' . $regex_body . '$' . $delimiter
: '(^' . $regex_body . '$)';
}
// By using $delimiter === null, we don't have to escape '/' or '@'.
$regex = buildMessageRegex('Mail: %mail, Dir: %dir.', null, [
'%mail' => '\w+@\w+\.\w+',
'%dir' => '\w+(/\w+)*',
]);
问题
似乎 ()
是编写正则表达式的唯一方法,我不必担心分隔符,并且可以调用 preg_quote($str, null)
并将 null 作为分隔符。
这个假设是否正确?
如果是这样,我总是可以使用 ()
作为分隔符,而不需要在方法中提供分隔符选项。
还是我遗漏了什么?
范围
我不确定这个 problem/question 是否特定于 PHP,或者更普遍地适用于任何使用它的 PCRE(我假设是在 Perl 中?)。
我个人对 PHP 案例很感兴趣,但我认为值得一提的是在一个很好的答案中这如何适用于 PHP.
不幸的是,您认为 (
和 )
总是需要转义的逻辑是不正确的。它们通常不需要在 []
内进行转义,但如果 ()
是定界符,则需要进行转义。
例如:
preg_match('/[(]/', "foo(bar", $match);
有效,但是
preg_match('([(])', "foo(bar", $match);
收到“未找到结束匹配定界符 ')'”错误。
因此,如果您使用 ()
作为分隔符,调用者将需要转义 []
中的那些字符,这通常不需要。
比对您的问题的具体回答更实用的解决方案。
许多字符都可以用作模式分隔符,包括来自 ascii 范围的 non-printable 个字符:SOH、STX、ETX、EOT、ENQ、ACK ...
它们不太可能在字符串中找到,用户更不可能在键盘上键入它们(如果用户真的决定在模式中加入 SOH,他可能会使用转义符序列 \x01
看东西)。
因此,您可以通过这种方式合理地构建模式(例如使用 SOH):
$pattern = chr(1) . $body . chr(1) . $modifiers;
如果你寻找比SOH(开始标题U+0001)更有意义的东西,你最终可以选择控制字符RS(记录分隔符U+0030)或EOT(传输结束U+0004)。请注意,您不能使用 NUL (U+0000)。
显然,可以肯定的是,无论您选择什么分隔符,总有这两个很好的旧解决方案:转义它或删除它。