2024 日本 PHP 研討會

模式修飾符

目前可能的 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)
這個修飾詞會開啟與 Perl 不相容的 PCRE 附加功能。在樣式中,任何反斜線後接沒有特殊意義的字母都會導致錯誤,從而保留這些組合以供將來擴展。預設情況下,與 Perl 一樣,反斜線後接沒有特殊意義的字母會被視為字面量。目前沒有其他功能受此修飾詞控制。
J (PCRE_INFO_JCHANGED)
(?J) 內部選項設定會更改局部 PCRE_DUPNAMES 選項。允許子樣式使用重複名稱。從 PHP 7.2.0 開始,也支援 J 作為修飾詞。
u (PCRE_UTF8)
這個修飾詞會開啟與 Perl 不相容的 PCRE 附加功能。樣式和目標字串會被視為 UTF-8 編碼。無效的目標字串將導致 preg_* 函數不匹配任何內容;無效的樣式將觸發 E_WARNING 等級的錯誤。五個和六個八位元組的 UTF-8 序列被視為無效。
n (PCRE_NO_AUTO_CAPTURE)
此修飾詞使簡單的 (xyz) 群組不進行擷取。只有像 (?<name>xyz) 這樣的命名群組才會擷取。這只會影響哪些群組進行擷取,仍然可以使用編號的子樣式參考,且匹配陣列仍將包含編號的結果。從 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 則註釋

27
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(
'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";
}

}
?>
13
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 是 haystack 字串
\x{hex} - 是 UTF-8 十六進位字元碼
/u 用於將該類別識別為 Unicode 字元類別。

希望這會有所幫助。
11
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]
7
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 中的輸出:
警告: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" }
}
*/
?>
4
Hayley Watson
4 年前
從 7.3.0 版本開始,'S' 修飾符不再生效;此分析現在一律由 PCRE 引擎執行。
10
Daniel Klein
12 年前
如果目標字串包含 UTF-8 序列,則應設定 'u' 修飾符,否則像 /./ 這樣的模式可能會將一個 UTF-8 序列匹配為兩個到四個單獨的 ASCII 字元。然而,這並非必要條件,因為您可能需要將 UTF-8 序列分解成單個位元組。不過,大多數情況下,如果您正在處理 UTF-8 字串,則應該使用 'u' 修飾符。

如果目標字串不包含任何 UTF-8 序列(即僅包含 0x00-0x7F 範圍內的字元),但模式包含 UTF-8 序列,據我所知,設定 'u' 修飾符不會對結果產生任何影響。
2
匿名
5 年前
關於 /i 修飾符和 POSIX 字元類別的警告
如果您在正規表達式中使用指示大小寫的 POSIX 字元類別,例如 [:upper:] 或 [:lower:],並結合 /i 修飾符,那麼在 PHP < 7.3 中,/i 修飾符將優先,並有效地使這兩個字元類別都像 [:alpha:] 一樣工作,但在 PHP >= 7.3 中,字元類別會覆蓋 /i 修飾符。
2
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.
1
Wirek
6 年前
重要的補充說明(使用新的 $pat3_2 正確運用 \R,及其結果和註釋)
需要注意的是,像 \r、\R 和 \v 這樣的跳脫序列在意義和應用上有一些細微差別(乍看之下有時難以掌握)——它們沒有一個在所有情況下都是完美的,但它們仍然非常有用。一些官方的 PCRE 控制選項及其變化也很方便——可惜的是,目前 php.net 上並沒有 (*ANYCRLF)、(*ANY) 和 (*CRLF) 的文件(雖然它們似乎已經可以使用超過 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): 陣列
(
[0] => C

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

3_2 !!! /\w(?=\R)/i (6): 陣列
(
[0] => C
[1] => 3
[2] => f
[3] => p
[4] => 0
[5] => S
)
*/
?>
很遺憾,我無法使用最新版 PHP 的伺服器 - 我的本地 PHP 版本是 5.3.8,而我的公開主機的 PHP 版本是 5.2.17。
2
michal dot kocarek at brainbox dot cz
15 年前
如果您想知道「S」修飾符的含義,這段說明可能會有幫助

設定「S」修飾符時,PHP 會在執行正規表示式之前從 PCRE API 呼叫 pcre_study() 函式。函式的結果會直接傳遞給 pcre_exec()。

有關 pcre_study() 和「研究模式」的更多資訊,請參閱 http://www.pcre.org/pcre.txt 上的 PCRE 手冊

附註:這裡使用的函式名稱「pcre_study」和「pcre_exec」是指以 C 語言編寫的 PCRE 函式庫函式,而不是任何 PHP 函式。
2
ebarnard at marathonmultimedia dot com
17 年前
使用 /x 修飾符新增註釋時,請勿在註釋中使用模式分隔符號。在註釋區域中,它可能不會被忽略。範例

<?php
$target
= 'some text';
if(
preg_match('/
e # 這裡是註解
/x'
,$target)) {
print
"目標 1 命中。\n";
}
if(
preg_match('/
e # /包含斜線的註解
/x'
,$target)) {
print
"目標 1 命中。\n";
}
?>

會印出「目標 1 命中。」,但接著第二個 preg_match() 會產生 PHP 警告訊息。

警告:preg_match() [function.preg-match]: 在 /ebarnard/x-modifier.php 的第 11 行發現未知的修飾詞 'C'
To Top