正则表达式
PHP 支持两种不同类型的正则表达式:
POSIX 和 Perl 兼容的。
POSIX 正则表达式比 Perl 兼容的功能弱,并且有时速度慢,但是易于阅读。
正则表达式的三种作用:
- 匹配,用于从字符串提取信息
- 用新文本取代匹配的文本;
- 把字符串拆分成小块的数组。
基础:
在正则表达式中有特殊含义的字符:
- 在正则表达式开头的
^
符号表示它必须匹配字符串的开头
更准确的说是把正则表达式定位在字符串开头
ereg('^cow','Dave was a cowhand');//返回 false
ereg('^cow','cowbunga');//返回 true
- 在正则表达式末尾出现的
$
表示他必须匹配字符串的末尾
也就是说,把正则表达式定位在字符串的末尾
ereg('cow$','Dave was a cowhand'); // 返回 false
ereg('cow$','Don't have a cow'); // 返回 true
- 在正则表达式中的句点
.
匹配任意单个字符:
ereg('c.t','cat');//true
ereg('c.t','c t');//true
ereg('c.t','ct');//false
如果想要匹配特殊字符中的某一个字符(称为元字符),需要使用反斜杠对它进行转义
php
ereg('\$5\.00','Your bill is $5.00 exactly');//返回 true
ereg('$5.00','Your bill is $5.00 exactly');//返回 false
正则表达式默认不区分大小写,所以 cow 和 COW 是不匹配的,如果想要执行 一个不区分大小写的 POSIX 风格的匹配,可以使用
eregi()
函数。
正则表达式中三种基本的抽象模式
- 字符集:可以通过用中括号
[]
包含可接受的字符来建立自己的字符类。
ereg('c[aeiou]t','I cut my hand');//true
ereg('c[aeiou]t','This crusty cat');//true
ereg('c[aeiou]t','What cart?');//false
ereg('c[aeiou]t','14ct gold');//false
执行原理分析:如果正则表达式引擎发现一个 c,然后匹配下一个字符是否是中括号中的字符中的一个(这里即为 a 或 e 或 i 或 o 或 u 中的一个),
- 如果不是,则匹配失败,引擎继续去查找字符串中的另一个 c;
- 如果是,引擎继续检查下一个字符是否是 t;
- 如果是,引擎达到匹配的末尾并返回 true;
- 如果不是,则引擎去查找另一个 c;
- 查找不到,直接退出;
- 找到,继续按照上面的匹配规则匹配;
- 如果没找到,直接返回 false.
- 可以在字符类开头使用 ^
来否定该类:
ereg('c[^aeiou]t','I cut my hand');//false
ereg('c[^aeiou]t','Reboot chthon');//true
ereg('c[^aeiou]t','14ct gold');//false
在这里正则表达式引擎查找一个 c,然后是一个非元音字符,接着是一个 t
- 可以用连字符
-
定义一个字符范围
ereg('[0-9]%','we are 25% complete');//true
ereg('[0123456789]%',we are 25% complete);//true
ereg('[a-z]t','2th');//false
ereg('[a-z]t','cet');//true
ereg('[a-z]t','PIT');//false
ereg('[a-zA-Z]!','stop!');//true
- 可选择性集合
可以使用竖线|
字符在正则表达式中指定可供选择的部分。
ereg('cat|dog','the cat rubbed my legs'); //返回 true
ereg('cat|dog','the dog rubbed my legs'); //返回 true
ereg('cat|dog','the rabbit rubbed my legs'); //返回 false
如果想要一行只有 cat 或 dog,需要使用正则表达式
'^(cat|dog)$'
如果想要某个字符串不以大写字母开头,需要使用正则表达式
'^([a-z]|[0-9])'
- 在字符串中重复的序列
要指定重复模式和量词,量词在重复模式后面,用来说明要重复这个模式多少次
POSIX 和 Perl 都支持的量词及其含义:
? =>
重复次数为 0 或 1* =>
重复次数为 0 或更多+ =>
重复次数为 1 或更多{n} =>
重复次数为 n{n,m} =>
至少 n 次,最多 m 次{n,} =>
至少 n 次
ereg('ca+t','caaaaaaat'); // true
ereg('ca+t','ct'); // false
ereg('ca?t','caaaaaaat'); // false
ereg('ca*t','ct'); // true
使用量词和字符类来匹配美国的电话号码:
ereg('[0-9]{3}-[0-9]{3}-[0-9]{4}','303-555-1212'); // true
ereg('[0-9]{3}-[0-9]{3}-[0-9]{4}','64-9-555-1234'); // false
- 子模式:可以使用小括号把几个正则表达式组合在一起作为一个单独的单元来对待,这个单元被称为子模式
ereg('a (very )+big dog','it was a very very big dog');//true
ereg('^(cat|dog)$','cat');//true
ereg('^(cat|dog)$','dog');//true
小括号也使得与子模式匹配的字串被捕获,如果把一个数组当做第三个参数传递给匹配函数,任何捕捉到的子串都将被放进该数组。
数组的第 1 个元素是与第一个子模式匹配的子串,第二个元素是与第二个子模式匹配的子串,以此类推。
数组的第 0 个元素为所有子模式匹配到的字符串的合集(就是由后面所有的非空元素的连接而成的字符串)。
example
$arr = null;
ereg('([0-9]+)','You have 42 magic beans',$arr);
print_r($arr);
ereg('([0-9]+)([0-9]+)','You have 42 magic 55 beans',$arr);
print_r($arr);
输出:
Array
(
[0] => 42
[1] => 42
)
Array
(
[0] => 42
[1] => 4
[2] => 2
)
风格
正则表达式:
POSIX 正则表达式
- 字符类:类 => 描述
[:alnum:]
=> 字母和数字字符[0-9a-zA-Z]
[:alpha:]
=> 字母字符[a-zA-Z]
[:ascii:]
=> 7 位 ASCII 字符[\x01-\x7F]
[:blank:]
=> 水平空白符 (空格、制表)[ \t]
[:digit:]
=> 数字[0-9]
[:lower:]
=> 小写字母[a-z]
[:upper:]
=> 大写字母[A-Z]
[:punct:]
=> 任意的标点符号[.;!#<>=+...]
[:space:]
=> 空白(换行、回车、制表符、空格、垂直制表符)[\n\r\t \x0B]
[:xdigit:]
=> 16 进制数字[0-9a-fA-F]
每一个
[:something:]
类都可以被用于替代一个字符类中的字符。例如:要查找任一数字字符或大写字母 =>
[[:digit:][:upper:]]
但是不能把一个字符类当做一个范围的终点来使用:
ereg('[A-[:upper:]]','string'); // 非法的正则表达式
如果某个地区把某些字符序列当做一个单独的字符来考虑——————它们被称为排序序列。
在字符类中匹配这些多字符序列中的一个时,要把它用
[.
和.]
括起来。例如:
[st[.ch.]]
//用于匹配 s、t 或 ch
- 等价类:把字符用
[=
和=]
括起来指定,等价字符类匹配有相同整理顺序的字符。
锚:将匹配限制在字符串中特定位置。
- POSIX 锚:
^
=> 在字符串开始$
=> 在字符串末尾[[:<:]]
=> 单词开始[[:>:]]
=> 单词末尾
精确匹配某个单词:
('[[:<:]]gun[[:>:]]')
// 精确匹配gun这个单词
函数
POSIX 风格的正则表达式有 3 类函数:匹配、替换 和 拆分。
1. 匹配
- ereg()
函数:
- 原型:$found = ereg (pattern,string[,array]);
如果第三个函数指定的话就组装该数组,并根据是否存在字符串找到模式的一个匹配而返回 true 或 false
替换
- 原型:
$changed = ereg_replace (pattern,replacement,string);
- 第一个参数:要替换的字符串的模式
- 第二个参数:要用来替换的内容
- 第三个参数:原字符串
- 返回值:原字符串被 replacement 替换后的字符串
- 原型:
拆分
split()
函数
使用正则表达式来把字符串拆分成较小的块,作为一个数组返回。如果出现错误,则返回 false
- 原型:
$arr = split (pattern,string[,limit]);
- 第一个参数:用于匹配分隔成块的文本;
- 第一个参数:被拆分的字符串
- 第三个参数:用于限定要拆分成多少个小块,如果设置了该值,则数组的最后一个元素会存放剩余的字符串。
- 返回值:子串构成的数组;
Perl 兼容正则表达式
分隔符
Perl 风格的正则表达式模仿 Perl 模式的语法,即每个模式都必须用一对分隔符括起来。习惯上使用左斜杠
/
,例如:/pattern/
不过任意非数字字母的字符 (除了反斜杠
\
) 都可用于分隔一个 Perl 风格的模式,这在匹配包含斜杠的字符串是很有用的后缀选项
放在结束分隔符后面的单个字符修饰符,它用于修改正则表达式引擎的行为。后缀选项 x 可以让正则表达式引擎匹配前从正则表达式中跳过空白符和被 # 标记的注释。
- 下面两种方式是等价的:
'/([[:alpha:]]+)\s+\1/'
“`php
‘/( # start capture’[[:alpha:]]+ # a word \s+ #whitespace \1 # the same word again ) # end capture /x’- 下面两种方式是等价的:
- 匹配行为
**Perl** 正则表达式特别为单行文字匹配进行了优化
- 句点 `.` 匹配任意除换行符 `\n` 之外的字符
- 美元符号 `$` 匹配字符串的末尾或在换行符之前以换行符结尾的字符串
- 字符类
**Perl** 风格的正则表达式不仅支持 **POSIX** 字符类,还定义了一些自己的字符类。
- `\s` => 空白符 `[\r\n \t]`
- `\S` => 非空白符 `[^\r\n \t]`
- `\w` => 单词字符 `[0-9a-zA-Z_]`
- `\W` => 非单词字符 `[^0-9a-zA-Z_]`
- `\d` => 数字 `[0-9]`
- `\D` => 非数字 `[^0-9]`
- 锚
- `\b` => 单词边界(在 `\w` 和 `\W` 之间或在字符串开头或末尾)
- `\B` => 非单词边界(在 `\w` 和 `\w` 或 `\W` 和 `\W` 之间)
- `\A` => 字符串开头
- `\Z` => 字符串末尾或在末尾的 `\n` 之前
- `\z` => 字符串末尾
- `^` => 行的开头(或如果 `/m` 标志启用的话在 `\n` 之后)
- `$` => 行的末尾(或如果 `/m` 标志启用的话在 `\n` 之前)
- 量词和贪婪性
**Perl** 也支持 **POSIX** 的量词,而且是具有贪婪性
> 当有一个量词时,引擎在仍然满足匹配模式的情况下尽可能多的进行匹配的。
```php
preg_match('/(<.*>)/','do <b>not</b> press the button',$match);//在这里<b>、</b>和<b>not</b>都满足匹配条件,但是根据贪婪性原则,匹配的内容为<b>not</b>
<div class="se-preview-section-delimiter"></div>
在你需要使用最少匹配(非贪婪匹配)的时候可以使用非贪婪量词
Perl 提供了一组用于最小匹配的量词
在普通量词 (普通量词默认为贪婪量词) 的基础上附加一个
?
号
- 贪婪量词 => 非贪婪量词
?
=>??
*
=>*?
+
=>+?
{m}
=>{m}?
{m,n}
=>{m,n}?
{m,}
=>{m,}?
preg_match('/(<.*>)/','do <b>not</b> press the button',$match);//在这里$match为<b>
<div class="se-preview-section-delimiter"></div>
另外一个更快的方法是使用一个字符类来匹配每个非大于字符到下一个大于字符:
preg_match('/(<[^>]*>)/','do <b>not</b> press the button',$match);
<div class="se-preview-section-delimiter"></div>
- 非捕获匹配
如果把模式的一部分用小括号括起来,那么匹配子模式的文本被捕获并且可以在后面访问。
如果你想创建一个不捕获匹配文本的子模式,那么在 Perl 兼容正则表达式中可以
使用(?:subpattern)
结构
preg_match('/(?:ello)(.*)/','jello giafra',$match);//这里$match[1]是' biafra',而不是子模式(?:ello)匹配的ello,表明第一个子模式的匹配文本没有被捕获
<div class="se-preview-section-delimiter"></div>
- 逆向引用
可以使用一个逆向引用来引用模式中之前被捕获的字符串;
\1
引用第一个子模式的内容,
\2
引用第二个,以此类推。
如果嵌套了子模式,那么第一个引用以第一个左小括号开始,
第二个引用以第二个左小括号开始,以此类推,不能捕获超过 99 个子模式
preg_match('/([[:alpha:]]+)\s+\1/','Paris in the the spring',$m);//$m[1]是'the'
<div class="se-preview-section-delimiter"></div>
后缀选项
Perl 风格的正则表达式允许把单个字符选项 (标志) 放在正则表达式模式后面,
来修改匹配的解释或行为。
/i
=> 不区分大小写的匹配/s
=> 使句点.
匹配任何字符,包括换行符\n
/x
=> 从模式中删除空白符和注释/m
=> 使^
匹配换行符\n
之后的内容,$
匹配换行符\n
之前的内容/e
=> 如果替换字符串是 PHP 代码,使用eval()
执行该代码来得到实际的替换字符串
PHP 的 Perl 兼容正则表达式也支持在 Perl 中不支持的其他修饰符
/U
=> 颠倒子模式的贪婪性;*
和+
尽可能少地匹配而不是尽可能多/u
=> 把模式字符串当做 UTF-8 编码对待/X
=> 如果反斜杠之后跟着没有特殊意义的字符,将产生一个错误/A
=> 把锚定位在字符串的开头就像模式中有^
一样/D
=> 使$
字符仅匹配一行的末尾/S
=> 使表达式解析器更加小心的检查模式的结构,使得第二次(例如:在循环中)运行时加快速度
在一个子模式中可以同时使用多个选项:/**
内联选项
除了在模式结束分隔符之后指定模式选项之外,还可以在一个模式内部指定仅运用于部分模式的选项
语法:(?flags:subpattern)
preg_match('/I like (?i:PHP)/','I like pHp');//在这个实例中只有单词 PHP 是不区分大小写的 <div class="se-preview-section-delimiter"></div>
i、m、s、U、x 和 X 选项可被用在这种方式内部。一次可以使用多个选项。
preg_match('/eat (?ix:foo d)/','eat FoOD');//true <div class="se-preview-section-delimiter"></div>
一个选项前如果有连字符
-
表示关闭此选项preg_match('/I like (?-i:PHP)/','I like pHp');//false <div class="se-preview-section-delimiter"></div>
内置标志不能用于捕捉字符串,需要设置一个附加的小括号来完成捕捉。
preg_match('/I (like (?i)PHP) a lot/','I like pHp a lot',$match);//$match为like pHp
前向和后向断言(看不懂,暂时跳过)
剪切(一次性子模式)
可以防止正则表达式在对待某些类型的模式时出现最坏的情况,
一旦匹配,正则表达式就不会回溯子模式。剪切不会改变匹配的结果,只是让程序能尽快运行结束。
条件表达式
在正则表达式中条件表达式就像一个 if 语句。
一般格式为:
(?(condition)yespattern)
//如果条件成立,就试图匹配 yespattern
(?(condition)yespattern|nopattern)
//如果条件成立,就试图匹配yespattern,否则,就尝试匹配 nopattern断言可以是两种类型中的一种:逆向引用或前向和后向匹配