PHP Conference Japan 2024

模式修饰符

下面列出了当前可能的 PCRE 修饰符。括号中的名称是指这些修饰符的内部 PCRE 名称。修饰符中会忽略空格和换行符,其他字符会导致错误。

i (PCRE_CASELESS)
如果设置了此修饰符,则模式中的字母将匹配大小写字母。
m (PCRE_MULTILINE)
默认情况下,PCRE 将主题字符串视为由单个“行”字符组成(即使它实际上包含多个换行符)。“行首”元字符 (^) 仅匹配字符串的开头,而“行尾”元字符 ($) 仅匹配字符串的结尾,或在终止换行符之前(除非设置了 D 修饰符)。这与 Perl 相同。 当设置此修饰符时,“行首”和“行尾”构造分别匹配主题字符串中任何换行符之后或之前,以及在开头和结尾。这相当于 Perl 的 /m 修饰符。如果主题字符串中没有“\n”字符,或者模式中没有 ^ 或 $ 的出现,则设置此修饰符无效。
s (PCRE_DOTALL)
如果设置了此修饰符,则模式中的点元字符将匹配所有字符,包括换行符。没有它,换行符将被排除在外。此修饰符等效于 Perl 的 /s 修饰符。诸如 [^a] 之类的否定类始终匹配换行符,而与此修饰符的设置无关。
x (PCRE_EXTENDED)
如果设置了此修饰符,则模式中的空格数据字符将被完全忽略,除非它们被转义或位于字符类中,并且字符在字符类之外未转义的 # 和下一个换行符之间(包括在内)也将被忽略。这相当于 Perl 的 /x 修饰符,并且可以使在复杂模式中包含注释成为可能。但是,请注意,这仅适用于数据字符。空格字符永远不能出现在模式中的特殊字符序列中,例如在引入条件子模式的序列 (?( 中。
A (PCRE_ANCHORED)
如果设置了此修饰符,则模式将强制为“锚定”,即它仅限于匹配正在搜索的字符串(“主题字符串”)的开头。此效果也可以通过模式本身中的适当构造来实现,这是在 Perl 中执行此操作的唯一方法。
D (PCRE_DOLLAR_ENDONLY)
如果设置了此修饰符,则模式中的美元元字符仅匹配主题字符串的结尾。如果没有此修饰符,如果美元是换行符,则它还会匹配最后一个字符之前的字符(但不会匹配其他换行符之前的字符)。如果设置了 m 修饰符,则会忽略此修饰符。Perl 中没有此修饰符的等效项。
S
当模式将被多次使用时,值得花费更多时间对其进行分析以加快匹配时间。如果设置了此修饰符,则将执行此额外分析。目前,研究模式仅对没有单个固定起始字符的非锚定模式有用。从 PHP 7.3.0 开始,此标志无效。
U (PCRE_UNGREEDY)
此修饰符反转量词的“贪婪性”,因此它们默认情况下不是贪婪的,但如果后面跟着 ? 则会变得贪婪。它与 Perl 不兼容。它也可以通过 (?U) 模式中的修饰符设置 或量词后面的问号(例如 .*?)来设置。

注意:

通常无法在非贪婪模式下匹配超过 pcre.backtrack_limit 个字符。

X (PCRE_EXTRA)
此修饰符启用 PCRE 的其他功能,这些功能与 Perl 不兼容。模式中任何后面跟着没有特殊含义的字母的反斜杠都会导致错误,从而为将来的扩展保留这些组合。默认情况下,与 Perl 一样,后面跟着没有特殊含义的字母的反斜杠被视为字面量。目前没有其他功能受此修饰符控制。
J (PCRE_INFO_JCHANGED)
(?J) 内部选项设置更改本地 PCRE_DUPNAMES 选项。允许子模式的重复名称。从 PHP 7.2.0 开始,也支持 J 作为修饰符。
u (PCRE_UTF8)
此修饰符启用 PCRE 的其他功能,这些功能与 Perl 不兼容。模式和主题字符串被视为 UTF-8。无效的主题将导致 preg_* 函数不匹配任何内容;无效的模式将触发级别为 E_WARNING 的错误。五字节和六字节 UTF-8 序列被视为无效。
n (PCRE_NO_AUTO_CAPTURE)
此修饰符使简单的 (xyz) 组成为非捕获组。只有像 (?<name>xyz) 这样的命名组才是捕获组。这只会影响哪些组是捕获组,仍然可以使用编号的子模式引用,并且 matches 数组仍将包含编号的结果。从 PHP 8.2.0 开始可用。
r (PCRE2_EXTRA_CASELESS_RESTRICT)
u (PCRE_UTF8) 和 i (PCRE_CASELESS) 生效时,此修饰符会阻止在 ASCII 和非 ASCII 字符之间进行匹配。 例如,preg_match('/\x{212A}/iu', "K") 匹配开尔文符号 (U+212A)。当使用 u 时 (preg_match('/\x{212A}/iur', "K")),它不匹配。 从 PHP 8.4.0 开始可用。

添加注释

用户贡献的注释 11 条注释

hfuecks at nospam dot org
19 年前
关于使用 /u 模式修饰符时 UTF-8 字符串的有效性,需要注意以下几点;

1. 如果模式本身包含无效的 UTF-8 字符,则会收到错误(如上述文档中所述 - “从 PHP 4.3.5 开始检查模式的 UTF-8 有效性”

2. 当主题字符串包含无效的 UTF-8 序列/代码点时,它基本上会导致 preg_* 函数“静默失败”,其中没有任何匹配,但没有指示字符串是无效的 UTF-8

3. PCRE 将五字节和六字节 UTF-8 字符序列视为有效(在模式和主题字符串中),但这些字符在 Unicode 中不受支持(参见“Linux 和 Unix HOWTO 的安全编程”的第 5.9 节“字符编码” - 可以在 http://www.tldp.org/ 和其他地方找到)

4. 有关在 PHP 中测试 UTF-8 字符串有效性(并丢弃五/六字节序列)的示例算法,请访问:http://hsivonen.iki.fi/php-utf8/

以下脚本应该可以让您了解哪些有效,哪些无效;

<?php
$examples
= array(
'有效的ASCII' => "a",
'有效的2字节序列' => "\xc3\xb1",
'无效的2字节序列' => "\xc3\x28",
'无效的序列标识符' => "\xa0\xa1",
'有效的3字节序列' => "\xe2\x82\xa1",
'无效的3字节序列(在第2字节)' => "\xe2\x28\xa1",
'无效的3字节序列(在第3字节)' => "\xe2\x82\x28",

'有效的4字节序列' => "\xf0\x90\x8c\xbc",
'无效的4字节序列(在第2字节)' => "\xf0\x28\x8c\xbc",
'无效的4字节序列(在第3字节)' => "\xf0\x90\x28\xbc",
'无效的4字节序列(在第4字节)' => "\xf0\x28\x8c\x28",
'有效的5字节序列(但不是Unicode!)' => "\xf8\xa1\xa1\xa1\xa1",
'有效的6字节序列(但不是Unicode!)' => "\xfc\xa1\xa1\xa1\xa1\xa1",
);

echo
"++模式中无效的UTF-8\n";
foreach (
$examples as $name => $str ) {
echo
"$name\n";
preg_match("/".$str."/u",'Testing');
}

echo
"++ preg_match() 示例\n";
foreach (
$examples as $name => $str ) {

preg_match("/\xf8\xa1\xa1\xa1\xa1/u", $str, $ar);
echo
"$name: ";

if (
count($ar) == 0 ) {
echo
"没有匹配到任何内容!\n";
} else {
echo
"匹配到 {$ar[0]}\n";
}

}

echo
"++ preg_match_all() 示例\n";
foreach (
$examples as $name => $str ) {
preg_match_all('/./u', $str, $ar);
echo
"$name: ";

$num_utf8_chars = count($ar[0]);
if (
$num_utf8_chars == 0 ) {
echo
"没有匹配到任何内容!\n";
} else {
echo
"匹配到 $num_utf8_chars 个字符\n";
}

}
?>
varrah NO_GARBAGE_OR_SPAM AT mail DOT ru
19 年前
花了几天的功夫,试图理解如何使用十六进制代码为Unicode字符创建模式。最后在阅读了几本没有提供任何实用的PHP有效示例的手册后,终于成功了。所以这里有一个例子。

例如,我们想搜索日文标准的带圈数字1-9(Unicode代码为0x2460-0x2468),为了通过十六进制代码实现这一点,应该使用以下调用
preg_match('/[\x{2460}-\x{2468}]/u', $str);

这里$str是一个目标字符串
\x{hex} - 是一个UTF-8十六进制字符代码
而/u用于标识该类为Unicode字符类。

希望它有用。
phpman at crustynet dot org dot uk
13年前
"u"标志的描述有点误导性。它暗示只有在模式包含UTF-8字符时才需要它,而实际上,如果模式或主题包含UTF-8,则都需要它。如果没有它,我在使用preg_match_all时遇到问题,当给定一个UTF-8主题字符串时,它会返回无效的多字节字符。

如果你阅读libpcre的文档,就会很清楚了

为了处理UTF-8字符串,你必须构建PCRE以在代码中包含UTF-8
支持,此外,你必须使用PCRE_UTF8选项标志调用pcre_compile(),
或者模式必须以
序列(*UTF8)开头。当其中任何一个为真时,模式
以及任何与之匹配的主题字符串都被视为
UTF-8字符串,而不是1字节字符的字符串。

[来自 http://www.pcre.org/pcre.txt]
arash dot dalir at gmail dot com
7年前
PCRE_INFO_JCHANGED修饰符显然在PHP版本<= 5.4(在PHP 5.5中未检查)中不被接受为全局选项(在结束分隔符之后),但在PHP 5.6(在PHP 7.X中也未检查)中允许。

以下模式在PHP 5.4中不起作用,但在PHP 5.6中起作用

<?php
//test.php
preg_match_all('/(?<dup_name>\d{1,4})\-(?<dup_name>\d{1,2})/J', '1234-23', $matches);
var_dump($matches);

/*
PHP 5.4中的输出:
Warning: preg_match_all(): Unknown modifier 'J' in test.php on line 3
NULL
--------------
PHP 5.6中的输出:
array(4) {
[0]=> array(1) { [0]=> string(7) "1234-23" }
["dup_name"]=> array(1) { [0]=> string(2) "23" }
[1]=> array(1) { [0]=> string(4) "1234" }
[2]=> array(1) { [0]=> string(2) "23" }
}
*/
?>

为了解决PHP 5.4中的此问题,可以使用(?J)模式修饰符,它指示模式(从该点开始)允许子模式的重复名称。

在PHP 5.4中有效的代码
<?php

preg_match_all
('/(?J)(?<dup_name>\d{1,4})\-(?<dup_name>\d{1,2})/', '1234-23', $matches);
var_dump($matches);

/*
PHP 5.4中的输出:
array(4) {
[0]=> array(1) { [0]=> string(7) "1234-23" }
["dup_name"]=> array(1) { [0]=> string(2) "23" }
[1]=> array(1) { [0]=> string(4) "1234" }
[2]=> array(1) { [0]=> string(2) "23" }
}
--------------
PHP 5.6中的输出(与/J相同):
array(4) {
[0]=> array(1) { [0]=> string(7) "1234-23" }
["dup_name"]=> array(1) { [0]=> string(2) "23" }
[1]=> array(1) { [0]=> string(4) "1234" }
[2]=> array(1) { [0]=> string(2) "23" }
}
*/
?>
Hayley Watson
4年前
从7.3.0开始,“S”修饰符无效;此分析现在始终由PCRE引擎执行。
Daniel Klein
12年前
如果_主题_包含utf-8序列,则应设置“u”修饰符,否则,诸如/./之类的模式可能会将utf-8序列匹配为两个到四个单独的ASCII字符。然而,这不是必需的,因为你可能需要将utf-8序列分解成单个字节。但是,大多数情况下,如果你正在使用utf-8字符串,则应使用“u”修饰符。

如果主题不包含任何utf-8序列(即仅包含0x00-0x7F范围内的字符),但模式包含,据我所知,设置“u”修饰符不会影响结果。
匿名用户
5年前
关于/i修饰符和POSIX字符类的警告
如果你在正则表达式中使用指示大小写的POSIX字符类,例如[:upper:]或[:lower:],并结合使用/i修饰符,那么在PHP < 7.3中,/i修饰符将优先,并有效地使这两个字符类都作为[:alpha:]工作,但在PHP >= 7.3中,字符类优先于/i修饰符。
Wirek
6年前
对于那些试图克服(或至少规避)在多行模式(/m)下在任何行的末尾($)正确匹配模式的问题的人,这里有一个提示。


<?php
// 不同的操作系统有不同的行结束符(也称为换行符):
// - Windows 使用 CR+LF (\r\n);
// - Linux 使用 LF (\n);
// - OSX 使用 CR (\r).
// 因此,单美元元字符断言 ($) 有时在多行修饰符 (/m) 模式下会失败 - 可能是 PHP 5.3.8 中的错误,或者只是一个“特性”(?)。
$str="ABC ABC\n\n123 123\r\ndef def\rnop nop\r\n890 890\nQRS QRS\r\r~-_ ~_-";
// C 3 p 0 _
$pat1='/\w$/mi'; // 在 JavaScript 中运行良好(Firefox 7.0.1+)
$pat2='/\w\r?$/mi';
$pat3='/\w\R?$/mi'; // 根据 php.net 和 pcre.org,效果令人失望
$pat4='/\w\v?$/mi';
$pat5='/(*ANYCRLF)\w$/mi'; // 非常好,但目前 php.net 上没有文档
$n=preg_match_all($pat1, $str, $m1);
$o=preg_match_all($pat2, $str, $m2);
$p=preg_match_all($pat3, $str, $m3);
$r=preg_match_all($pat4, $str, $m4);
$s=preg_match_all($pat5, $str, $m5);
echo
$str."\n1 !!! $pat1 ($n): ".print_r($m1[0], true)
.
"\n2 !!! $pat2 ($o): ".print_r($m2[0], true)
.
"\n3 !!! $pat3 ($p): ".print_r($m3[0], true)
.
"\n4 !!! $pat4 ($r): ".print_r($m4[0], true)
.
"\n5 !!! $pat5 ($s): ".print_r($m5[0], true);
// 请注意 $pat2 (\r)、$pat3 (\R)、$pat4 (\v) 中三个非常有用的转义序列以及 $pat5 ((*ANYCRLF)) 中修改的换行选项之间的差异 - 至少对于某些应用程序而言。

/* 上述代码产生以下输出:
ABC ABC

123 123
def def
nop nop
890 890
QRS QRS

~-_ ~-_
1 !!! /\w$/mi (3): Array
(
[0] => C
[1] => 0
[2] => _
)

2 !!! /\w\r?$/mi (5): Array
(
[0] => C
[1] => 3
[2] => p
[3] => 0
[4] => _
)

3 !!! /\w\R?$/mi (5): Array
(
[0] => C

[1] => 3
[2] => p
[3] => 0
[4] => _
)

4 !!! /\w\v?$/mi (5): Array
(
[0] => C

[1] => 3
[2] => p
[3] => 0
[4] => _
)

5 !!! /(*ANYCRLF)\w$/mi (7): Array
(
[0] => C
[1] => 3
[2] => f
[3] => p
[4] => 0
[5] => S
[6] => _
)
*/
?>
不幸的是,我没有权限访问安装了最新 PHP 版本的服务器 - 我的本地 PHP 版本是 5.3.8,我的公共主机上的 PHP 版本是 5.2.17。
Wirek
6年前
一个重要的补充(使用新的 $pat3_2 正确利用 \R,以及结果和注释)
请注意,像 \r、\R 和 \v 这样的转义序列在含义和应用上存在一些(有时乍一看很难理解的)细微差别 - 它们在所有情况下都不是完美的,但仍然非常有用。一些官方的 PCRE 控制选项及其更改也很有用 - 不幸的是,(*ANYCRLF)、(*ANY) 和 (*CRLF) 目前在 php.net 上没有文档(尽管它们似乎已经可用超过 10 年 5 个月了),但在维基百科(“Newline/linebreak options” 在 https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions)和官方 PCRE 库网站(“Newline convention” 在 http://www.pcre.org/original/doc/html/pcresyntax.html#SEC17)上都有很好的描述。根据 php.net 以及官方描述(“Newline sequences” 在 https://www.pcre.org/original/doc/html/pcrepattern.html#newlineseq),\R 的功能在(编译时选项的默认配置下)使用不当时似乎令人失望。

对于那些试图解决(或至少绕过)即使没有多行模式 (/m) 或元字符断言 ($ 或 ^) 也能正确匹配任何行末尾(或开头)的模式问题的用户,这里有一个提示。
<?php
// 不同的操作系统有不同的行结束符(也称为换行符):
// - Windows 使用 CR+LF (\r\n);
// - Linux 使用 LF (\n);
// - OSX 使用 CR (\r).
// 因此,单美元元字符断言 ($) 有时在多行修饰符 (/m) 模式下会失败 - 可能是 PHP 5.3.8 中的错误,或者只是 PCRE 编译时元字符断言 (^ 和 $) 的默认配置选项的“特性”(?)。
$str="ABC ABC\n\n123 123\r\ndef def\rnop nop\r\n890 890\nQRS QRS\r\r~-_ ~_-";
// C 3 p 0 _
$pat3='/\w\R?$/mi'; // 使用不当时,根据 php.net 和 pcre.org,效果令人失望
$pat3_2='/\w(?=\R)/i'; // 使用允许的前瞻断言(仅检测而不捕获)且不使用多行 (/m) 模式时,效果更好;请注意,如果使用字符串末尾的替代方案 ((?=\R|$)),它将按预期获取所有 7 个元素,但'/(*ANYCRLF)\w$/mi' 在使用中更直接
$p=preg_match_all($pat3, $str, $m3);
$r=preg_match_all($pat3_2, $str, $m4);
echo
$str."\n3 !!! $pat3 ($p): ".print_r($m3[0], true)
.
"\n3_2 !!! $pat3_2 ($r): ".print_r($m4[0], true);
// 请注意 $pat3 和 $pat3_2 (\R) 中两个非常有用的转义序列之间的差异 - 至少对于某些应用程序而言。

/* 上述代码产生以下输出:
ABC ABC

123 123
def def
nop nop
890 890
QRS QRS

~-_ ~-_
3 !!! /\w\R?$/mi (5): Array
(
[0] => C

[1] => 3
[2] => p
[3] => 0
[4] => _
)

3_2 !!! /\w(?=\R)/i (6): Array
(
[0] => C
[1] => 3
[2] => f
[3] => p
[4] => 0
[5] => S
)
*/
?>
不幸的是,我没有权限访问安装了最新 PHP 版本的服务器 - 我的本地 PHP 版本是 5.3.8,我的公共主机上的 PHP 版本是 5.2.17。
michal dot kocarek at brainbox dot cz
15 年前
如果您想知道“S”修饰符的含义,本段内容可能会有帮助

当设置“S”修饰符时,PHP 会在执行正则表达式之前调用 PCRE API 中的 pcre_study() 函数。该函数的结果会直接传递给 pcre_exec()。

有关 pcre_study() 和“学习模式”的更多信息,请查看 PCRE 手册 http://www.pcre.org/pcre.txt

PS:请注意,此处使用的函数名称“pcre_study”和“pcre_exec”指的是用 C 语言编写的 PCRE 库函数,而不是任何 PHP 函数。
ebarnard at marathonmultimedia dot com
17 年前
在使用 /x 修饰符添加注释时,请勿在注释中使用模式分隔符。它可能不会在注释区域被忽略。示例

<?php
$target
= 'some text';
if(
preg_match('/
e # Comments here
/x'
,$target)) {
print
"Target 1 hit.\n";
}
if(
preg_match('/
e # /Comments here with slash
/x'
,$target)) {
print
"Target 1 hit.\n";
}
?>

打印“Target 1 hit.”,但随后为第二个 preg_match() 生成 PHP 警告消息

警告:preg_match() [function.preg-match]:在 /ebarnard/x-modifier.php 的第 11 行中,未知修饰符“C”
To Top