模式修饰符

当前可能的 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
如果一个模式将被多次使用,则值得花费更多时间对它进行分析,以加快匹配所花费的时间。如果设置了此修饰符,则会执行此额外的分析。目前,学习模式仅对具有固定起始字符的非锚定模式有用。
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) 这样的命名组为捕获组。这仅影响哪些组为捕获组,仍然可以使用编号的子模式引用,并且匹配数组仍将包含编号的结果。从 PHP 8.2.0 起可用。

添加注释

用户提供的注释 12 条注释

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 的安全编程教程”的 5.9 节“字符编码” - 可以访问 http://www.tldp.org/ 和其他地方)

4. 如需用于测试 UTF-8 字符串有效性(并舍弃五/六个八位组序列)的 PHP 示例算法,请访问:http://hsivonen.iki.fi/php-utf8/

下面的脚本应该让您了解哪些有效哪些无效;

<?php
$examples
= array(
'Valid ASCII' => "a",
'Valid 2 Octet Sequence' => "\xc3\xb1",
'Invalid 2 Octet Sequence' => "\xc3\x28",
'Invalid Sequence Identifier' => "\xa0\xa1",
'Valid 3 Octet Sequence' => "\xe2\x82\xa1",
'Invalid 3 Octet Sequence (in 2nd Octet)' => "\xe2\x28\xa1",
'Invalid 3 Octet Sequence (in 3rd Octet)' => "\xe2\x82\x28",

'Valid 4 Octet Sequence' => "\xf0\x90\x8c\xbc",
'Invalid 4 Octet Sequence (in 2nd Octet)' => "\xf0\x28\x8c\xbc",
'Invalid 4 Octet Sequence (in 3rd Octet)' => "\xf0\x90\x28\xbc",
'Invalid 4 Octet Sequence (in 4th Octet)' => "\xf0\x28\x8c\x28",
'Valid 5 Octet Sequence (but not Unicode!)' => "\xf8\xa1\xa1\xa1\xa1",
'Valid 6 Octet Sequence (but not Unicode!)' => "\xfc\xa1\xa1\xa1\xa1\xa1",
);

echo
"++Invalid UTF-8 in pattern\n";
foreach (
$examples as $name => $str ) {
echo
"$name\n";
preg_match("/".$str."/u",'Testing');
}

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

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

if (
count($ar) == 0 ) {
echo
"Matched nothing!\n";
} else {
echo
"Matched {$ar[0]}\n";
}

}

echo
"++ preg_match_all() examples\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
"Matched nothing!\n";
} else {
echo
"Matched $num_utf8_chars character\n";
}

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

例如,我们希望查找日式圆圈数字 1-9(Unicode 代码为 0x2460-0x2468),为了通过十六进制代码实现,应该使用以下调用
preg_match('/[\x{2460}-\x{2468}]/u', $str);

此处 $str 是 haystack 字符串
\x{hex} - 是一个 UTF-8 十六进制字符代码
以及 /u 用于将类标识为 Unicode 字符串类。

希望它对您有帮助。
Hayley Watson
4 年前
从 7.3.0 开始,“S”修饰符没有任何效果;现在总是由 PCRE 引擎执行此分析。
arash dot dalir at gmail dot com
6 年前
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 如下:
警告:preg_match_all():在 test.php 第 3 行中未知的修饰符 'J'
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" }
}
*/
?>
phpman at crustynet dot org dot uk
13 年前
"u" 标志的说明有点误导。它表明仅在模式包含 UTF-8 字符时才需要它,但事实上,如果模式或主题包含 UTF-8,就需要它。如果没有它,我遇到了在得到一个 UTF-8 主题字符串时 preg_match_all 返回无效的多字节字符的问题。

如果你阅读 libpcre 的文档,它相当清楚

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

[引用 http://www.pcre.org/pcre.txt]
丹尼尔·克莱因
12 年前
如果 _subject_ 包含 utf-8 序列,则应设置“u”修饰符,否则诸如 /./ 的模式可能将 utf-8 序列匹配为两个到四个独立的 ASCII 字符。不过,这并不是一个要求,因为您可能需要将 utf-8 序列分解为单个字节。然而,大多数情况下,如果您正在处理 utf-8 字符串,则应使用“u”修饰符。

如果 subject 不包含任何 utf-8 序列(即仅包含 0x00-0x7F 范围内的字符),但模式包含,据我所知,设置“u”修饰符将不会对结果产生任何影响。
Wirek
6 年前
一个重要的补充(其中新的 $pat3_2 正确地使用了 \R,以及它的结果和注释)
请注意,有(有时乍一看很难理解)关于转义序列的含义和应用的细微差别,如 \r、\R 和 \v - 在各种情况下,它们都不是完美的,但它们仍然非常有用。一些官方 PCRE 控制选项及其变更也很方便 - 遗憾的是,这里在 php.net 上目前没有关于 (*ANYCRLF)、(*ANY) 或 (*CRLF) 的记录(虽然它们似乎已经发布了超过 10 年 5 个月),但它们在维基百科(https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions 上的“Newline/linebreak options”)和官方 PCRE 库网站(http://www.pcre.org/original/doc/html/pcresyntax.html#SEC17 上的“Newline convention”)上描述得很好。根据 php.net 以及官方描述(https://www.pcre.org/original/doc/html/pcrepattern.html#newlineseq 上的“Newline sequences”),使用不当的情况下,\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。
匿名的
5 年前
关于 /i 修饰符和 POSIX 字符类的警告
如果你在 regex 中使用了 POSIX 字符类来表示大小写,例如 [:upper:] 或 [:lower:],并结合了 /i 修饰符使用,那么在 PHP < 7.3 中,/i 修饰符将优先,并有效地使这两个字符类都像 [:alpha:] 一样工作,但在 PHP >= 7.3 中,字符类将覆盖 /i 修饰符。
Wirek
6 年前
对于那些试图对抗(或至少在多行模式(/m)下绕过)在任何行的结尾 ($) 正确匹配模式的问题的人,这里提供一个提示。
<?php
// Various OS-es have various end line (a.k.a line break) chars:
// - Windows uses CR+LF (\r\n);
// - Linux LF (\n);
// - OSX CR (\r).
// And that's why single dollar meta assertion ($) sometimes fails with multiline modifier (/m) mode - possible bug in PHP 5.3.8 or just a "feature"(?).
$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'; // This works excellent in JavaScript (Firefox 7.0.1+)
$pat2='/\w\r?$/mi';
$pat3='/\w\R?$/mi'; // Somehow disappointing according to php.net and pcre.org
$pat4='/\w\v?$/mi';
$pat5='/(*ANYCRLF)\w$/mi'; // Excellent but undocumented on php.net at the moment
$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);
// Note the difference among the three very helpful escape sequences in $pat2 (\r), $pat3 (\R), $pat4 (\v) and altered newline option in $pat5 ((*ANYCRLF)) - for some applications at least.

/* The code above results in the following output:
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] => _
)
*/
?>
Unfortunately, I haven't got any access to a server with the latest PHP version - my local PHP is 5.3.8 and my public host's PHP is version 5.2.17.
michal dot kocarek at brainbox dot cz
15 年前
如果你想知道“S”修饰符的含义,那么下面的段落可能会有帮助

当“S”修饰符被设置时,PHP 在执行 regexp 之前会从 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 # 注释放在这里
/x'
,$target)) {
print
"Target 1 hit.\n";
}
if(
preg_match('/
e # 在这里使用正斜杠的注释
/x'
,$target)) {
print
"Target 1 hit.\n";
}
?>

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

警告:preg_match() [function.preg-match]:在 /ebarnard/x-modifier.php 第 11 行未知的修饰符“C”
damian dot driscoll at gmail dot com
6 年前
PCRE_INFO_JCHANGED 修饰符在 5.6.31 版本中可用,但不能在 5.6.16 中用,后者会生成“未知的修饰符 J”警告。
To Top