转载

正则表达式基本知识(php)

这里的知识点基本上是《正则指引》的读书笔记,只是每个知识点的示例代码用php来实现。

1. 字符组

字符组(Character Class)就是一组字符,在正则表达式中,它表示“在同一个位置可能出现的各种字符”。写法:[ab]、[314]、[#.?]

字符组的基本用法

[...]

preg_match('/[0123456]/', '5');    // => 1 preg_match('/[abc123]/', '5');    // => 0

范围表示法(range)

[x-y] 表示 xy 整个范围内的字符。如, [0123456789] 表示为 [0-9][abcdefghijk] 表示为 [a-k]

为什么是 [0-9] ,而不是 [9-0]

因为 - 表示的范围一般是根据字符对应的码值(Code Point)来确定的。典型地有ACSⅡ编码。在ACSⅡ编码中,0~9的码值是48~57,a~z的码值是97~122,A~Z的码值是65~90。

preg_match('/[0-9]/', '5');    // => 1 preg_match('/[a-z]/', '5');    // => 0 preg_match('/[0-9a-fA-F]/', '0');    // 16进制

字符组简记法(shorthands)

提供比范围表示法更简洁的表示方法,如 /d 表示 [0-9]/w 表示 [0-9a-zA-z_]

php中支持的字符组简记:

  • /d 所有的数字,即 [0-9]

  • /D 所有的非数字,与 /d 互斥

  • /w 所有的单词字符(字符、数字、下划线),即 [0-9a-zA-Z_]

  • /W 所有的非单词字符,与 /W 互斥

  • /s 所有的空白字符,包括空格、制表符、回车符、换行符等空白字符

  • /S 所有的非空白字符,与 /s 互斥

preg_match('//d/', '8');    // => 1 preg_match('//d/', 'a');    // => 0 preg_match('//d[a-z]/', 'a');    // => 1  preg_match('//w/', 'a');    // => 1 preg_match('//w/', '6');    // => 1 preg_match('//w/', '_');    // => 1  preg_match('//s/', ' ');    // => 1 preg_match('//s/', "/t");    // => 1 preg_match('//s/', "/r");    // => 1

元字符与转义

在范围表示法中,字符组中的横线 - 不能匹配横线字符,而是用来表示范围,这类字符叫做 元字符 (meta-character)。元字符除了 - 还有开方括号 [ 、闭刚括号 ]^$ 等,它们都有特殊的意义。

当元字符想要表示普通字符的含义时(如 - 就只想表示横线字符),就需要转义处理(在元字符前加反斜线字符 / )。对于 - ,有一个例外情况,就是当它紧跟着字符组中的开括号 [ 时,它就表示普通横线字符,此时不用转义。

preg_match('/[0/-9]/', "-");    // => 1 preg_match('/[0/-9]/', "8");    // => 0 preg_match('/[0/-9]/', "0");    // => 1  preg_match('/[-09]/', "-");    // => 1  preg_match("/[0//-9]/", "-");    // => 1 preg_match("/[0/-9]/", "-");    // => 1

仔细看上面第一个表达式和最后两个表示式。这里要注意:

  • 在php中,字符串既可以用单引号标注也可以用双引号标注。两者的主要区别在于,双引号字符串可以插值,二单引号字符串不能;另外,双引号字符串会处理字符串转义,二单引号字符串不会

  • 正则表达式是以字符串的方式提供的。在php中,双引号字符串本身也有关于转义的规定(如 "//"'/r'"/t" 等),因此 "0//-9"'0/-9' 是等价的。

  • 那么最后一个表达式为什么也可以匹配呢?这是因为,尽管php的正则表达式用字符串文字给出,但它与常见的字符串不完全一样——如果某个转义序列可以有字符串识别,则对其进行转义处理;否则,将整个转义序列“原封不动”地保存下来。

因此,在正则表达式中转义要小心,在php中,使用单引号字符串来构建正则表达式会比双引号字符串更简单明了。

排除型字符组(Negated Character Class)

在方括号 […] 中列出希望匹配的所有字符叫做“普通字符组”。在开方括号 [ 之后紧跟一个脱字符 ^ ,写作 [^…] ,表示“在当前位置,匹配一个没有列出的字符”。例如, [^0-9] 匹配非数字字符。

preg_match('/[^0-9][0-9]/', "A8");    // => 1

排除型字符组中的紧跟着开方括号 [ 的脱字符 ^ 也是元字符,如果要匹配尖括号字符,需要进行转义处理。但是,不紧跟着开方括号 [^ 就是普通字符,不需要转义。

preg_match('/[^0-9]/', '0');    // => 0 preg_match('/[/^0-9]/', '0');    // => 1 preg_match('/[/^0-9]/', '^');    // => 1 preg_match('/[0-9^]/', '^');    // => 1

POSIX字符组

之前介绍的字符组,都属于Perl衍生出来的正则表达式流派(Flavor),这个流派叫做PCRE(Per Compatible Regular Expression)。正则表达式还有其他流派,比如POSIX(Portable Operating System Interface for unix),它是一系列规范,定义了UNIX操作系统应当支持的功能,其中也包括了正则表达式的规范。

常见的 [a-z] 形式的字符组,在POSIX规范中仍然获得支持,称作 POSIX方括号表达式 。POSIX方括号表达式中的 / 不是用来转义的,如 [/d] 就只能匹配 /d 两个字符。这里涉及到 ]- 这两个特殊字符,在POSIX规范中,紧接在开方括号 [ 之后的 ] 才表示闭方括号字符,紧挨在闭方括号 ] 之前的 - 才表示横线字符。

对于PCRE规范中的 /d/w/s 等字符组简记法,POSIX中有类似的东西,叫做 POSIX字符组 。在ASCⅡ语言环境(locale)中,常见的POSIX字符组及其含义如下:

POSIX字符组 说明 ACSⅡ字符组 等价的PCRE简记法
[:alnum:] 字母和数字 [0-9a-zA-Z]
[:alpha:] 字母 [a-zA-Z]
[:ASCⅡ] ASCⅡ字符 [/x00-/x7F]
[:blank:] 空格字符和制表字符 [ /t]
[:cntrl:] 控制字符 [/x00-/x1F/x7F]
[:digit:] 数字字符 [0-9] /d
[:graph:] 空白字符之外的字符 [/x21-/x7E]
[:lower:] 小写字母字符 [a-z]
[:print:] 类似[:graph:],但包括空白字符 [/x20-/x7E]
[:punct:] 标点符号 [][!"#$%&'()*+,./:;<=>?@/^_`{/ }~-]
[:space:] 空白字符 [ /t/r/n/v/f] /s
[:upper:] 大写字母 [A-Z]
[:word:] 字母字符 [A-Za-z0-9_] /w
[:xdigit:] 十六进制字符 [A-Fa-f0-9]

php中有专门处理POSIX正则的函数,但从5.3.0开始已经废弃了。这里只是了解一下相关知识。

2. 量词

这里首先介绍一下 ^$ 两个特殊字符,在上一章的 元字符与转义 一节提到过这两个特殊字符。

^ 放在正则表达式的开头,表示“定位到字符串的起始位置”; $ 用在正则表达式的末尾,表示“定位到字符串的结束位置”。

preg_match('//w/d/', '1a2b');   // => 1 preg_match('/^/w/d/', '1a2b');  // => 0 必须以字母开头 preg_match('//w/d$/', '1a2b');  // => 0 必须以数字结尾 preg_match('/^/w/d/', 'a2b');   // => 1 preg_match('//w/d$/', '1a2');   // => 1 preg_match('/^/w/d$/', '1a2');  // => 0 开头必须是字母,结尾必须是数字 preg_match('/^/w/d$/', 'a2');   // => 1

量词的一般形式

如果要匹配一个邮政编码(6位数字),目前能写出来的正则表达式是 ^/d/d/d/d/d/d$

preg_match('/^/d/d/d/d/d/d$/', '100010');   // => 1 preg_match('/^/d/d/d/d/d/d$/', '10001035'); // => 0 preg_match('/^/d/d/d/d/d/d$/', '10a010');   // => 0

/d 重复6次的写法很不科学,正则表达式肯定会有更方便的写法,也就是量词(quantifier)。量词的通用形式是 {m,n} (注意, , 后面不能有空格),它限定之前的元素能够出现的次数,m是下限,n是上限。其他常见的量词形式有:

量词 说明
{n} 之前的元素必须出现n次
{m,n} 之前的元素最少出现m次,最多出现n次
{m,} 之前的元素最少出现m次,出现次数无上限
{0,n} 之前的元素可以不出现,也可以出现,最多出现n次
preg_match('/^/d{6}$/', '100010');      // => 1  preg_match('/^/d{4,6}$/', '123');       // => 0 preg_match('/^/d{4,6}$/', '1234');      // => 1 preg_match('/^/d{4,6}$/', '123456');    // => 1 preg_match('/^/d{4,6}$/', '1234567');   // => 0

常用量词

正则表达式还有三个常用的量词,分别是 +?*

常用量词 {m,n}等价形式 说明
* {0,} 可能出现,也可能不出现,出现次数没有上限
+ {1,} 至少出现1次,出现次数没有上限
? {0,1} 出现0次或1次

这三种量词在实际中使用的非常多。

例如,匹配url的时候,有可能是 http ,也有可能是 https ,这个时候用 ? 就很方便:

preg_match('/^https?:////www/.baidu/.com/', 'http://www.baidu.com');    // => 1 preg_match('/^https?:////www/.baidu/.com/', 'https://www.baidu.com');   // => 1

在匹配html的tag(如 <head><div><img> 等)的时候,在 <> 中间会有字符,但是字符长度不确定。这种情况下就需要使用 + 了:

preg_match('/^<[^>]+>$/', '<html>');    // => 1 preg_match('/^<[^>]+>$/', '<a>');       // => 1 preg_match('/^<[^>]+>$/', '<>');        // => 0

如果说匹配一个双引号字符串,在双引号内,既可以出现字符也可以不出现字符。这种情况下就应该使用量词 *

preg_match('/^/"[^/"]*/"$/', '""');     // => 1 preg_match('/^/"[^/"]*/"$/', '"abcd"'); // => 1

点号

点号 . 是与量词搭配比较多得一个字符。一般情况下,点号 . 可以匹配除了换行符 /n 以外的任意字符。

preg_match('/^.$/', 'z');    // => 1 preg_match('/^.$/', '8');    // => 1 preg_match('/^.$/', "/n");    // => 0

如果要使 . 能匹配的字符包含换行符 /n ,可以使用自制通配符 /s/S/w/W/d/D

preg_match('/^[/s/S]$/', "/n");    // => 1

使 . 能匹配的字符包含换行符 /n 的另外一种方法是指定正则匹配时使用单行模式。在php中,可以使用模式修饰符和预定义常量两种方法来指定单行模式。关于正则匹配的模式,后面会详细介绍。

preg_match('/(?s)^.$/', "/n");    // => 1 模式修饰符 preg_match('/^.$/s', "/n");    // => 1 预定义常量

匹配优先量词、忽略优先量词

很多语言中,都可以使用 /*...*/ 来注释代码,如果是一个支持语法高亮的文本编辑器就要能够提取 /*...*/ 注释块。很easy的,我们可以写出如下正则表达式:

$str = '/*this is a comment*/ /*this is another comment*/'; preg_match('////*.*/*///', $str, $arr); echo $arr[0]; /*this is a comment*/ /*this is another comment*/

可以看到,这个正则表达式出了点小问题,它把两个注释块匹配出来了,如果两个注释块中间有代码,那么代码也会匹配出来。

这是因为,我们介绍的 *+? 都是 匹配优先量词 (greedy quantifier,也称贪婪量词)。匹配优先量词是指在拿不准是否要匹配的时候,优先尝试匹配。因此, $str 中间的 */ /* 都被 .* 匹配了。

正则表达式中利用 忽略优先量词 来解决上述问题。与 *+? 对应的忽略优先量词的形式是 *?+??? 。忽略优先量词在不确定是否要匹配时选择“不匹配”的状态。还是以提取注释块的代码为例:

preg_match('////*.*?/*///', $str, $arr); echo $arr[0]; /*this is a comment*/

这样就达到了提取注释块的效果。

匹配优先量词 忽略优先量词 限定次数
* *? 可能出现,可能不出现,出现次数没有上限
+ +? 至少出现1次,没有上限
? ?? 出现0次或1次
{m,n} {m,n}? 出现次数大于等于m,小于等于n
{m,} {m,}? 至少出现m次,没有上限
{0,n} {0,n}? 出现0次-n次

php中有指定非贪婪匹配模式的 模式修饰符预定义常量 ,与 忽略优先量词 是一样的效果:

// 默认贪婪匹配 preg_match('////*.*/*///', $str, $arr);    // => /*this is a comment*/ /*this is another comment*/  // 预定义常量 指定非贪婪匹配 preg_match('////*.*/*///U', $str, $arr);    // => /*this is a comment*/  // 模式修饰符 指定非贪婪匹配 preg_match('/(?U)///*.*/*///', $str, $arr);    // => /*this is a comment*/  // 同时使用 忽略优先量词 和 预定义常量 preg_match('////*.*?/*///U', $str, $arr);    // => /*this is a comment*/ /*this is another comment*/ // 同时使用 忽略优先量词 和 模式修饰符 preg_match('/(?U)///*.*?/*///', $str, $arr);    // => /*this is a comment*/ /*this is another comment*/

量词的转义

*+? 的转义比较简单,就是 /*/+/? 。主要是 {m,n} 形式的转义需要注意。

量词 转义 量词 转义 量词 转义
{n} //{n} * /* *? /*/?
{m,n} //{m,n} + //+ +? //+/?
{m,} //{m,} ? /? ?? /?/?
{0,n} //{0,n}

3.括号

分组

上一章介绍了量词,上一章的例子中量词都只能控制它前面的字符或字符组。那么量词能否控制连续的字符或字符组呢,如控制一个单词 hello 出现或者不出现。这就要用到正则表达式的 分组 功能(子表达式),使用圆括号 (...) 实现分组(子表达式)。

// 量词限定前面一个字符 preg_match('/^hello?, world$/', 'hello, world');    // => 1 preg_match('/^hello?, world$/', 'hell, world');    // => 1 preg_match('/^hello?, world$/', ', world');    // => 0  // 量词限定一个单词 preg_match('/^(hello)?, world$/', 'hello, world');    // => 1 preg_match('/^(hello)?, world$/', 'hell, world');    // => 0 preg_match('/^(hello)?, world$/', ', world');    // => 1

多选结构

多选结构(alternative)的形式是 (...|...) ,在括号内以竖线 | 分开多个子表达式,这些子表达式也叫多选分支(option)。

例如,匹配常见的11位手机号

// 匹配常见11位手机号 preg_match('/(13[0-9]|15[0-356]|18[025-9])/d{8}/', '18521510001', $arr);    // => 1

引用分组

正则表达式会保存每个 (...) 分组匹配的文本,这种功能叫捕获分组(capturing group)。在需要直接使用子表达式的时候非常有用

例如,提取 <a> 标签中的地址和描述文本

// 提取<a>标签中的地址和描述文本 preg_match('/<a/s+href="([^/'"/s]*)">(.*?)<//a>/', '<a href="github.com">visit github</a>', $arr); print_r($arr); /* Array (     [0] => <a href="github.com">github</a>     [1] => github.com     [2] => visit github ) */

正则表达式替换时也支持捕获分组。php中支持 /num$num 的形式替换,但是num不能大于10;另一种形式 ${num} 可以大于10。

例如,日期的替换

// 正则表达式替换 preg_replace('/(/d{4})-(/d{2})-(/d{2})/', '$1年$2月$3日', '2015-08-25');    // => 2015年08月25日 preg_replace('/(/d{4})-(/d{2})-(/d{2})/', '/1年/2月/3日', '2015-08-25');    // => 2015年08月25日

反向引用

在正则表达式内部引用之前的捕获分组匹配的文本叫反向引用(back-reference)。实测php支持 /num 形式的反向引用,不支持 $num 形式的反向引用

例如,利用反向引用匹配id相同的不同标签

// 匹配id为main的标签 preg_match('/<([/w]+)/s+id="main">.*?<///1>/', '<div id="main"><span>hello, world</span></div>', $arr); // => 1 print_r($arr); /* Array (  [0] => <div id="main"><span>hello, world</span></div>  [1] => div ) */ // 匹配id为main的标签 preg_match('/<([/w]+)/s+id="main">.*?<///1>/', '<p id="main">Hello, World</p>', $arr); // => 1 print_r($arr);                /* Array (  [0] => <p id="main">Hello, World</p>  [1] => p ) */ 

命名分组

数字编号的分组有时候不够直观,因此有些语言和工具的正则表达式提供了命名分组(named grouping)。

正则字符中 (?P<name>) 来命名, (?P=name) 来使用。 preg_replace 替换时不支持

// 捕获命名分组 preg_match('/<a/s+href="(?P<link>[^/'"/s]*)">(?P<description>.*?)<//a>/', '<a href="github.com">visit github</a>', $arr); print_r($arr);                /* Array (  [0] => <a href="github.com">visit github</a>  [link] => github.com  [1] => github.com  [description] => visit github  [2] => visit github ) */ // 反向引用命名分组 preg_match('/<(?P<tag>[/w]+)/s+id="main">.*?<//(?P=tag)>/', '<div id="main"><span>hello, world</span></div>', $arr); print_r($arr);                /* Array (  [0] => <div id="main"><span>hello, world</span></div>  [tag] => div  [1] => div ) */ 

非捕获分组

正则表达式默认会保存每个 (...) 匹配的文本,前面利用这个特性可以实现一些有用的功能。但是,有时候正则表达式比较复杂, (...) 会出现的比较多,而此时仅仅是想实现 分组 或者 多选 的功能,而不需要 捕获分组 ;同时,大量不需要的捕获分组可能会影响性能。

为了解决这种问题,正则表达式提供了 非捕获分组 (non-capturing group),它的形式是 (?:...)

// 捕获分组 preg_match('/(13[0-9]|15[0-356]|18[025-9])/d{8}/', '18521510001', $arr); // => 1 print_r($arr);                /* Array (  [0] => 18521510001  [1] => 185 ) */ // 非捕获分组 echo preg_match('/(?:13[0-9]|15[0-356]|18[025-9])/d{8}/', '18521510001', $arr); // => 1 print_r($arr);                /* Array (  [0] => 18521510001 ) */ 

4.断言

正则表达式中的有些结构不匹配真正的文本,只负责判断在某个位置左/右侧的文本是否符合要求,这种结构称为 断言 (assertion)。常见的断言有三类:单词边界、行起始/结束位置、环视。

单词边界

单词边界(word boundary),记为 /b 。它匹配的是单词边界(一边是单词字符,另一边不是单词字符)的位置,而不是字符。

// 单词边界 preg_match('//b/w+/b/', 'word', $arr);  // => 1 print_r($arr); /* Array (  [0] => word ) */ // 匹配所有单词边界 preg_match_all('//b/w+/b/', 'hello, world', $arr);  // => 2 print_r($arr); /* Array (  [0] => Array   (    [0] => hello    [1] => world   ) ) */ 

这类匹配位置而不匹配字符的元素叫做 锚点 (anchor),下一节要介绍的 ^$ 等也是锚点。

行起始/结束位置

^ :字符串的开始位置

$ :字符串的结束位置

$str = 'first line second line last line'; // 字符串起始位置 preg_match_all('/^/w+/b/', $str, $arr); // => 1 print_r($arr); /* Array (  [0] => Array   (    [0] => first   ) ) */ // 字符串结束位置 preg_match_all('//b/w+$/', $str, $arr); // => 1 print_r($arr); /* Array (  [0] => Array   (    [0] => line   ) ) */ 

如果指定了多行模式(Multiline Mode), ^$ 分别可以匹配行起始位置、行结束位置。关于模式,将在下一章详细介绍。指定多行模式的方式是使用模式修饰符 (?m) :在正则表达式之前加上 (?m) ;或者是预定义常量的方式: /.../m

// 多行模式  行起始位置 // preg_match_all('/^/w+/b/m', $str, $arr); 是一样的效果 preg_match_all('/(?m)^/w+/b/', $str, $arr); // => 3 print_r($arr); /* Array (  [0] => Array   (    [0] => first    [1] => second    [2] => last   ) ) */ //多行模式  行结束位置 // preg_match_all('//b/w+$/m', $str, $arr); 是一样的效果 preg_match_all('/(?m)/b/w+$/', $str, $arr); // => 3 print_r($arr); /* Array (  [0] => Array   (    [0] => line    [1] => line    [2] => line   ) ) */ 

/A 不论是普通模式还是多行模式,都匹配字符串起始位置

/Z/z 不论是普通模式还是多行模式,都匹配字符串结束位置

/Z/z 的区别在于最后字符串的末尾是 行终止符 的时候, /Z 匹配行终止符之前的位置, /z 则不管行终止符,只匹配“整个字符串的结束位置”。

这里用 行终止符 ,而不用 换行符 的原因是不同操作系统的行终止符不同(windows /r/n ,linux /n ,mac /n )。

正则表达式基本知识(php)

正则表达式基本知识(php)

// /A 始终匹配字符串起始位置 preg_match_all('//A/w+/b/', $str, $arr); // => 1 print_r($arr); /* Array (  [0] => Array   (    [0] => first   ) ) */ preg_match_all('/(?m)/A/w+/b/', $str, $arr); // => 1 print_r($arr); /* Array (  [0] => Array   (    [0] => first   ) ) */ // /Z始终匹配行结束位置 preg_match_all('//b/w+/Z/', $str, $arr); // => 1 print_r($arr); /* Array (  [0] => Array   (    [0] => line   ) ) */ preg_match_all('/(?m)/b/w+/Z/', $str, $arr); // => 1 print_r($arr); /* Array (  [0] => Array   (    [0] => line   ) ) */ // /Z /z的区别 $str = 'first line second line last line '; preg_match_all('/(?m)/b/w+/z/', $str, $arr); // => 0 print_r($arr); /* Array (  [0] => Array   (   ) ) */ preg_match_all('/(?m)/b/w+/Z/', $str, $arr); // => 1 print_r($arr); /* Array (  [0] => Array   (    [0] => line   ) ) */ 

环视

环视(look-around)用来“停在原地,四处张望”,它本身也不匹配任何字符,用来限定它旁边的文本满足某种条件。

名字 记法 含义
肯定顺序环视 (?=...) 向右看看,右边出现了环视中的内容才匹配
否定顺序环视 (?!...) 向右看看,右边不出现环视中的内容才匹配
肯定逆序环视 (?<=...) 向左看看,左边出现了环视中的内容才匹配
否定逆序环视 (?<!...) 向左看看,左边不出现环视中的内容才匹配

例如,要匹配html标签中的开标签( <a><div><body> 等),而不能匹配闭标签( </a></div></body> 等)和自闭和标签( <br/> 等),同时,也不能匹配一个空的标签 <>

直接使用 <[^//]+>/ 字符排除是不对的,因为有些标签的属性中可能会含有 / 字符。例如 <a href="github.com/nrk/predis"> 。这里只是在开尖括号 < 之后的第一个字符和闭尖括号 > 之前的第一个字符不能为 /

然而 <[^//][^>]*[^//]> 也是不行的,因为在 <> 之间会至少匹配两个字符,像 <a> 这样的标签是无法匹配到的。这里就要用到 环视 了。

&lt;(?!//).*+(?&lt;!//)&gt;

上面的正则表达式中有两个环视结构,一个在开尖括号 < 之后,表示在尖括号 < 后向右看看,右边的第一个字符不能为 / (正则表达式中进行了转义);另外一个在闭尖括号 > 之前,表示在闭尖括号 > 之前向左看看,左边挨着的字符不能为 /

上面的正则表达式已经解决了匹配html中开标签的主要问题,只是其中的 .*? 还需要优化一下。需要解决的问题是:

  • 有可能会有单引号 ' 或双引号 " ,它们都得成对出现

  • 单引号对或双引号对之内可以有 > 字符,但是它们的外面不能有 > 字符

利用正则表达式的选择结构,可以写出下面的表达式,用于完善上面的问题。

&lt;(?!//)(?:/'[^/']*/'|&quot;[^&quot;]*&quot;|[^/'&quot;&gt;])+(?&lt;!//)&gt;

5.匹配模式

前面的内容中已经出现介过了 单行模式多行模式非贪婪模式匹配模式 是指匹配时使用的规则。常用的匹配模式还有 不区分大小写模式注释模式

在开始介绍具体的模式之前,先介绍php中模式的两种具体实现 /.../{modifier}...(?{modifier})...

模式修饰符 /.../{modifier} ...(?{modifier})...
示例 /<tr>.*<//tr>/s <tr>(?s).*<//tr>
名称(php手册) 模式修饰符 模式内修饰符
名称(《正则指引》) 预定义常量 模式修饰符
作用范围 整个正则表达式 不在分组(子表达式)中时,对它后面的全部正则表达式起作用;如果在分组(子表达式)中,则对它分组中的剩余部分起作用。在没有分组,且放在整个正则表达式最前面的时候相当于 /.../{modifier}
支持程度 支持所有模式修饰符 支持部分模式修饰符
其他编程语言 可能不支持 一般都支持

不区分大小写模式

在html中是不区分大小写的,例如 <td><tD><Td><TD> 的作用是一样的。如果要从网页中提取 <td> ,不使用匹配模式的表达式应该是这样:

&lt;[tT][dD]&gt;

由于 <td> 标签只有两个字符,所以上面的写法还可以接受。但是如果标签是 <script> 呢?这里,就需要使用 不区分大小写模式/.../i...(?i)... 。上面的正则表达式可以进一步写为:

/<td>/i(?i)<td>

preg_match('/(?i)<td>/', '<tD>', $arr); print_r($arr); /* Array (     [0] => <tD> ) */

单行模式

形式: /.../s...(?s)...
作用:点号可以匹配换行符

多行模式

单行模式 没有关系。影响 ^$ 的匹配

形式: /.../x...(?m)...

注释模式

(?#...) 的方法可以在正则表达式中添加注释

// (?#...)注释 preg_match('/(?#this is comment)(?i)<td>/', '<tD>', $arr); print_r($arr); /* Array (     [0] => <tD> ) */

更彻底地,可是使用 /.../x 表示注释模式

// /.../x注释模式 $str = '/ (/d{4}) # year -    # dash (/d{2}) # month -    # dash (/d{2}) # day /x'; preg_match($str, '2015-10-01', $arr); print_r($arr); /* Array (  [0] => 2015-10-01  [1] => 2015  [2] => 10  [3] => 01 ) */ 

非贪婪

// 默认贪婪匹配 $str = '<tr><td>hello</td></tr><tr><td>world</td></tr>'; preg_match('/<tr>.*<//tr>/', $str, $arr); print_r($arr); /* Array (  [0] => <tr><td>hello</td></tr><tr><td>world</td></tr> ) */ // 非贪婪匹配模式 $str = '<tr><td>hello</td></tr><tr><td>world</td></tr>'; preg_match('/<tr>.*<//tr>/U', $str, $arr); print_r($arr); /* Array (  [0] => <tr><td>hello</td></tr> ) */ 

Unicode

要匹配中文等Unicode字符,最好是指定Unicode模式修饰符 /.../u 。如果不指定会有两个问题

  • GBK编码环境下,中文不能匹配

  • 无法利用 [/x{4e00}-/x{9fff}] 匹配中文

// 指定unicode模式 preg_match('/<td>[/x{4e00}-/x{9fff}]*<//td>/u', '<td>姚明</td>', $arr); print_r($arr); /* Array (     [0] => <td>姚明</td> ) */
正文到此结束
Loading...