• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 模式修饰符

    下面列出了当前可用的 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 修饰符,使被编译模式中可以包含注释。 注意:这仅用于数据字符。 空白字符 还是不能在模式的特殊字符序列中出现,比如序列 (?( 引入了一个条件子组(译注: 这种语法定义的 特殊字符序列中如果出现空白字符会导致编译错误。 比如(?(就会导致错误)。
    e(PREG_REPLACE_EVAL)
    Warning

    This feature wasDEPRECATEDin PHP 5.5.0, andREMOVEDas of PHP 7.0.0.

    如果设置了这个被弃用的修饰符, preg_replace() 在进行了对替换字符串的 后向引用替换之后, 将替换后的字符串作为php 代码评估执行(eval 函数方式),并使用执行结果 作为实际参与替换的字符串。单引号、双引号、反斜线(\)和 NULL 字符在 后向引用替换时会被用反斜线转义. Caution

    The addslashes() function is run on each matched backreference before the substitution takes place. As such, when the backreference is used as a quoted string, escaped characters will be converted to literals. However, characters which are escaped, which would normally not be converted, will retain their slashes. This makes use of this modifier very complicated.

    Caution

    请确保$replacement参数由合法 php 代码字符串组成,否则 php 将会 在preg_replace() 调用的行上产生一个解释错误。

    Caution

    不建议使用此修饰符,它很容易产生安全漏洞:

    <?php
    $html = $_POST['html'];
    // uppercase headings
    $html = preg_replace(
        '(<h([1-6])>(.*?)</h\1>)e',
        '"<h$1>" . strtoupper("$2") . "</h$1>"',
        $html
    );

    以上示例代码能够被这样的字符串利用:<h1>{${eval($_GET[php_code])}}</h1>。 这能让攻击者执行他们想要的 PHP 代码,几乎完全渗透进服务器。

    为了阻止此类远程代码执行攻击,可以使用 preg_replace_callback() 替代:

    <?php
    $html = $_POST['html'];
    // uppercase headings
    $html = preg_replace_callback(
        '(<h([1-6])>(.*?)</h\1>)',
        function ($m) {
            return "<h$m[1]>" . strtoupper($m[2]) . "</h$m[1]>";
        },
        $html
    );

    Note:

    仅 preg_replace() 使用此修饰符,其他 PCRE 函数忽略此修饰符。


    A(PCRE_ANCHORED)
    如果设置了这个修饰符,模式被强制为"锚定"模式,也就是说约束匹配使其仅从 目标字符串的开始位置搜索。这个效果同样可以使用适当的模式构造出来,并且 这也是 perl 种实现这种模式的唯一途径。
    D(PCRE_DOLLAR_ENDONLY)
    如果这个修饰符被设置,模式中的元字符美元符号仅仅匹配目标字符串的末尾。如果这个修饰符 没有设置,当字符串以一个换行符结尾时, 美元符号还会匹配该换行符(但不会匹配之前的任何换行符)。 如果设置了修饰符m,这个修饰符被忽略. 在 perl 中没有与此修饰符等同的修饰符。
    S
    当一个模式需要多次使用的时候,为了得到匹配速度的提升,值得花费一些时间 对其进行一些额外的分析。如果设置了这个修饰符,这个额外的分析就会执行。当前, 这种对一个模式的分析仅仅适用于非锚定模式的匹配(即没有单独的固定开始字符)。
    U(PCRE_UNGREEDY)
    这个修饰符逆转了量词的"贪婪"模式。 使量词默认为非贪婪的,通过量词后紧跟?的方式可以使其成为贪婪的。这和 perl 是不兼容的。 它同样可以使用 模式内修饰符设置 (?U)进行设置, 或者在量词后以问号标记其非贪婪(比如.*?)。

    Note:

    在非贪婪模式,通常不能匹配超过 pcre.backtrack_limit 的字符。


    X(PCRE_EXTRA)
    这个修饰符打开了 PCRE 与 perl 不兼容的附件功能。模式中的任意反斜线后就 ingen 一个 没有特殊含义的字符都会导致一个错误,以此保留这些字符以保证向后兼容性。 默认情况下,在 perl 中,反斜线紧跟一个没有特殊含义的字符被认为是该字符的原文。 当前没有其他特性由这个修饰符控制。
    J(PCRE_INFO_JCHANGED)
    内部选项设置(?J)修改本地的PCRE_DUPNAMES选项。允许子组重名, (译注:只能通过内部选项设置,外部的 /J 设置会产生错误。)
    u(PCRE_UTF8)
    此修正符打开一个与 perl 不兼容的附加功能。 模式和目标字符串都被认为是 utf-8 的。 无效的目标字符串会导致 preg_* 函数什么都匹配不到; 无效的模式字符串会导致 E_WARNING 级别的错误。 PHP 5.3.4 后,5字节和6字节的 UTF-8 字符序列被考虑为无效(resp. PCRE 7.3 2007-08-28)。 以前就被认为是无效的 UTF-8。


    Regarding the validity of a UTF-8 string when using the /u pattern modifier, some things to be aware of;
    1. If the pattern itself contains an invalid UTF-8 character, you get an error (as mentioned in the docs above - "UTF-8 validity of the pattern is checked since PHP 4.3.5"
    2. When the subject string contains invalid UTF-8 sequences / codepoints, it basically result in a "quiet death" for the preg_* functions, where nothing is matched but without indication that the string is invalid UTF-8
    3. PCRE regards five and six octet UTF-8 character sequences as valid (both in patterns and the subject string) but these are not supported in Unicode ( see section 5.9 "Character Encoding" of the "Secure Programming for Linux and Unix HOWTO" )
    4. For an example algorithm in PHP which tests the validity of a UTF-8 string (and discards five / six octet sequences) head to: http://hsivonen.iki.fi/php-utf8/
    The following script should give you an idea of what works and what doesn't;
    <?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";
      }
      
    }
    ?>
    
    The description of the "u" flag is a bit misleading. It suggests that it is only required if the pattern contains UTF-8 characters, when in fact it is required if either the pattern or the subject contain UTF-8. Without it, I was having problems with preg_match_all returning invalid multibyte characters when given a UTF-8 subject string.
    It's fairly clear if you read the documentation for libpcre:
        In order process UTF-8 strings, you must build PCRE to include UTF-8
        support in the code, and, in addition, you must call pcre_compile()
        with the PCRE_UTF8 option flag, or the pattern must start with the
        sequence (*UTF8). When either of these is the case, both the pattern
        and any subject strings that are matched against it are treated as
        UTF-8 strings instead of strings of 1-byte characters.
    [from http://www.pcre.org/pcre.txt]
    If the _subject_ contains utf-8 sequences the 'u' modifier should be set, otherwise a pattern such as /./ could match a utf-8 sequence as two to four individual ASCII characters. It is not a requirement, however, as you may have a need to break apart utf-8 sequences into single bytes. Most of the time, though, if you're working with utf-8 strings you should use the 'u' modifier.
    If the subject doesn't contain any utf-8 sequences (i.e. characters in the range 0x00-0x7F only) but the pattern does, as far as I can work out, setting the 'u' modifier would have no effect on the result.
    An important addendum (with new $pat3_2 utilising \R properly, its results and comments):
    Note that there are (sometimes difficult to grasp at first glance) nuances of meaning and application of escape sequences like \r, \R and \v - none of them is perfect in all situations, but they are quite useful nevertheless. Some official PCRE control options and their changes come in handy too - unfortunately neither (*ANYCRLF), (*ANY) nor (*CRLF) is documented here on php.net at the moment (although they seem to be available for over 10 years and 5 months now), but they are described on Wikipedia ("Newline/linebreak options" at https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions) and official PCRE library site ("Newline convention" at http://www.pcre.org/original/doc/html/pcresyntax.html#SEC17) pretty well. The functionality of \R appears somehow disappointing (with default configuration of compile time option) according to php.net as well as official description ("Newline sequences" at https://www.pcre.org/original/doc/html/pcrepattern.html#newlineseq) when used improperly.
    A hint for those of you who are trying to fight off (or work around at least) the problem of matching a pattern correctly at the end (or at the beginning) of any line even without the multiple lines mode (/m) or meta-character assertions ($ or ^).
    <?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"(?) of default configuration option for meta-character assertions (^ and $) at compile time of 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';  // Somehow disappointing according to php.net and pcre.org when used improperly
    $pat3_2='/\w(?=\R)/i';  // Much better with allowed lookahead assertion (just to detect without capture) without multiline (/m) mode; note that with alternative for end of string ((?=\R|$)) it would grab all 7 elements as expected, but '/(*ANYCRLF)\w$/mi' is more straightforward in use anyway
    $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);
    // Note the difference between the two very helpful escape sequences in $pat3 and $pat3_2 (\R) - 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
    ~-_ ~-_
    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
    )
     */
    ?>
    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.
    the PCRE_INFO_JCHANGED modifier is apparently not accepted as a global option (after the closing delimiter) in PHP versions <= 5.4 (not checked in PHP 5.5) but allowed in PHP 5.6 (also not checked in PHP 7.X)
    The following pattern doesn't work in PHP 5.4, but it works in 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);
    /*
    output in PHP 5.4:
    Warning: preg_match_all(): Unknown modifier 'J' in test.php on line 3
    NULL
    --------------
    output 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" } 
    }
    */
    ?>
    in order to resolve this issue in PHP 5.4, one can use the (?J) pattern modifier, which indicates the pattern (from that point forward) allows duplicate names for subpatterns.
    code which works in PHP 5.4:
    <?php
    preg_match_all('/(?J)(?<dup_name>\d{1,4})\-(?<dup_name>\d{1,2})/', '1234-23', $matches);
    var_dump($matches);
    /*
    output in 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" } 
    }
    --------------
    output in PHP 5.6 (the same as with /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" } 
    }
    */
    ?>
    
    In case you're wondering, what is the meaning of "S" modifier, this paragraph might be useful:
    When "S" modifier is set, PHP calls the pcre_study() function from the PCRE API before executing the regexp. Result from the function is passed directly to pcre_exec().
    For more information about pcre_study() and "Studying the pattern" check the PCRE manual on http://www.pcre.org/pcre.txt
    PS: Note that function names "pcre_study" and "pcre_exec" used here refer to PCRE library functions written in C language and not to any PHP functions.
    Spent a few days, trying to understand how to create a pattern for Unicode chars, using the hex codes. Finally made it, after reading several manuals, that weren't giving any practical PHP-valid examples. So here's one of them:
    For example we would like to search for Japanese-standard circled numbers 1-9 (Unicode codes are 0x2460-0x2468) in order to make it through the hex-codes the following call should be used:
    preg_match('/[\x{2460}-\x{2468}]/u', $str);
    Here $str is a haystack string
    \x{hex} - is an UTF-8 hex char-code
    and /u is used for identifying the class as a class of Unicode chars.
    Hope, it'll be useful.
    When adding comments with the /x modifier, don't use the pattern delimiter in the comments. It may not be ignored in the comments area. Example:
    <?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";
    }
    ?>
    prints "Target 1 hit." but then generates a PHP warning message for the second preg_match():
    Warning: preg_match() [function.preg-match]: Unknown modifier 'C' in /ebarnard/x-modifier.php on line 11
    A warning about the /i modifier and POSIX character classes:
    If you're using POSIX character classes in your regex that indicate case such as [:upper:] or [:lower:] in combination with the /i modifier, then in PHP < 7.3 the /i modifier will take precedence and effectively make both those character classes work as [:alpha:], but in PHP >= 7.3 the character classes overrule the /i modifier.
    A hint for those of you who are trying to fight off (or work around at least) the problem of matching a pattern correctly at the end ($) of any line in multiple lines mode (/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.
    The PCRE_INFO_JCHANGED modifier works in version 5.6.31 but not in 5.6.16, which generates an 'unknown modifier J' warning.

    上篇:性能