[TOC]
* * * * *
## 1 正则表达式的意义
正则表达式用来从**源字符串中匹配结果字符串**
**正则表达式**是结果字符串的**抽象结构描述**。
**结果字符串**是正则表达式的**具体内容组成**。
## 2 正则表达式简单使用(php版)
>[info] 自php5.3.0起,POSIX正则表达式扩展被废弃,使用PCRE模式正则表达式,因此下文只介绍PCRE模式正则表达式
### 1 源字符串匹配一个结果字符串 preg_match()
~~~
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
~~~
> $pattern: 结果字符串的正则描述
> $subject: 源字符串
> $matches: 匹配结果数组
>> $matches[0] 完整模式匹配到的文本
>> $macthes[1] 第一个捕获子组匹配文本
>> $macthes[0] 第n个捕获子组匹配文本
> $flags: 匹配状态标记
>> PREG_OFFSET_CAPTURE
>>> 匹配时字符串相对源字符串偏移量
> 这个值会填充到$mactches数组中
> $mactches元素的第0个元素是 结果字符串
> $mactches元素的第2个元素是 结果字符串在源字符串偏移量
> $offset: 搜索开始位置,默认从字符串开始位置。
> 返回值:
> 返回匹配成功的次数。0或者1次
> preg_match() 第一次匹配成功 停止搜索
> 不同于preg_match_all() 匹配所有
> 发送错误 返回false
* * * * *
>[info] 示例1 查找文本字符串"php"
~~~
<?php
//模式分隔符后的"i"标记这是一个大小写不敏感的搜索
if ( preg_match ( "/php/i" , "PHP is the web scripting language of choice." )) {
echo "A match was found." ;
} else {
echo "A match was not found." ;
}
?>
~~~
>[info] 示例2 查找单词"web"
~~~
<?php
/* 模式中的\b标记一个单词边界,所以只有独立的单词"web"会被匹配,而不会匹配
* 单词的部分内容比如"webbing" 或 "cobweb" */
if ( preg_match ( "/\bweb\b/i" , "PHP is the web scripting language of choice." )) {
echo "A match was found." ;
} else {
echo "A match was not found." ;
}
if ( preg_match ( "/\bweb\b/i" , "PHP is the website scripting language of choice." )) {
echo "A match was found." ;
} else {
echo "A match was not found." ;
}
?>
~~~
>[info] 示例3 获取URL中的域名
~~~
<?php
//从URL中获取主机名称
preg_match ( '@^(?:http://)?([^/]+)@i' ,
"http://www.php.net/index.html" , $matches );
$host = $matches [ 1 ];
//获取主机名称的后面两部分
preg_match ( '/[^.]+\.[^.]+$/' , $host , $matches );
echo "domain name is: { $matches [ 0 ]} \n" ;
?>
~~~
输出:
`domain name is: php.net`
>[info] 示例4 命名子数组匹配
~~~
<?php
$str = 'foobar: 2008' ;
preg_match ( '/(?P<name>\w+): (?P<digit>\d+)/' , $str , $matches );
/* 下面例子在php 5.2.2(pcre 7.0)或更新版本下工作, 然而, 为了后向兼容, 上面的方式是推荐写法. */
// preg_match('/(?<name>\w+): (?<digit>\d+)/', $str, $matches);
print_r ( $matches );
?>
~~~
输出:
~~~
Array
(
[0] => foobar: 2008
[name] => foobar
[1] => foobar
[digit] => 2008
[2] => 2008
)
~~~
>[danger]Tip:如果你仅仅想要检查一个字符串是否包含另外一个字符串,不要使用 preg_match() 。 使用 strpos() 或 strstr() 替代完成工作会更快。
### 2 源字符串匹配所有结果字符串 preg_match_all()
~~~
int preg_match_all ( string $pattern , string $subject [, array &$matches [, int $flags = PREG_PATTERN_ORDER [, int $offset = 0 ]]] )
~~~
> $pattern: 结果字符串正则描述
> $subject: 源字符串
> $matches: 匹配结果的多维数组,flags会影响
> $flags: 匹配状态标记
>> PREG_PATTERN_ORDER
>>> 结果排序为 $matches[0]保存完整模式的所有匹配, $matches[1] 保存第一个子组的所有匹配,以此类推。
>> PREG_SET_ORDER
>>> 结果排序为 $matches[0] 包含第一次匹配得到的所有匹配(包含子组), $matches[1] 是包含第二次匹配到的所有匹配(包含子组)的数组,以此类推。
>> PREG_OFFSET_CAPTURE
>>> 匹配返回时会增加它相对目标字符串的偏移量。 注意这会改变matches中的每一个匹配结果字符串元素,使其 成为一个第0个元素为匹配结果字符串,第1个元素为 匹配结果字符串在subject中的偏移量。
>> 如果没有给定排序标记,假定设置为 PREG_PATTERN_ORDER 。
> $offset 搜索源字符串开始位置,默认从字符串开头搜索
> 返回值
> 返回完整匹配次数(可能是0),或者如果发生错误返回 FALSE 。
* * * * *
>[info]示例一 PREG_PATTERN_ORDER 标记
~~~
<?php
preg_match_all ( "|<[^>]+>(.*)</[^>]+>|U" ,
"<b>example: </b><div align=left>this is a test</div>" ,
$out , PREG_PATTERN_ORDER );
echo $out [ 0 ][ 0 ] . ", " . $out [ 0 ][ 1 ] . "\n" ;
echo $out [ 1 ][ 0 ] . ", " . $out [ 1 ][ 1 ] . "\n" ;
?>
~~~
输出:
~~~
<b>example: </b>, <div align=left>this is a test</div>
example: , this is a test
~~~
>[info] $out[0] 是包含匹配完整模式的字符串的数组,
>$out[1] 是包含闭合标签内的字符串的数组。
* * * * *
>[info]示例二 PREG_SET_ORDER 标记
~~~
<?php
preg_match_all ( "|<[^>]+>(.*)</[^>]+>|U" ,
"<b>example: </b><div align=\"left\">this is a test</div>" ,
$out , PREG_SET_ORDER );
echo $out [ 0 ][ 0 ] . ", " . $out [ 0 ][ 1 ] . "\n" ;
echo $out [ 1 ][ 0 ] . ", " . $out [ 1 ][ 1 ] . "\n" ;
?>
~~~
输出:
~~~
<b>example: </b>, example:
<div align="left">this is a test</div>, this is a test
~~~
>[info]示例三 查找所有文本中的电话号码。
~~~
<?php
preg_match_all ( "/\(? (\d{3})? \)? (?(1) [\-\s] ) \d{3}-\d{4}/x" ,
"Call 555-1212 or 1-800-555-1212" , $phones );
?>
~~~
>[info]示例四 查找匹配的HTML标签(贪婪)
~~~
<?php
//\\2是一个后向引用的示例. 这会告诉pcre它必须匹配正则表达式中第二个圆括号(这里是([\w]+))
//匹配到的结果. 这里使用两个反斜线是因为这里使用了双引号.
$html = "<b>bold text</b><a href=howdy.html>click me</a>" ;
preg_match_all ( "/(<([\w]+)[^>]*>)(.*?)(<\/\\2>)/" , $html , $matches , PREG_SET_ORDER );
foreach ( $matches as $val ) {
echo "matched: " . $val [ 0 ] . "\n" ;
echo "part 1: " . $val [ 1 ] . "\n" ;
echo "part 2: " . $val [ 2 ] . "\n" ;
echo "part 3: " . $val [ 3 ] . "\n" ;
echo "part 4: " . $val [ 4 ] . "\n\n" ;
}
?>
~~~
输出:
~~~
matched: <b>bold text</b>
part 1: <b>
part 2: b
part 3: bold text
part 4: </b>matched: <a href=howdy.html>click me</a>
part 1: <a href=howdy.html>
part 2: a
part 3: click me
part 4: </a>
~~~
>[info]示例五 使用子命名组
~~~
<?php
$str = <<<FOO
a: 1
b: 2
c: 3
FOO;
preg_match_all ( '/(?P<name>\w+): (?P<digit>\d+)/' , $str , $matches );
/* 下面代码在php 5.2.2(pcre 7.0)或更高版本下工作, 不过, 为了向后兼容
* 推荐使用上面的方式. */
// preg_match_all('/(?<name>\w+): (?<digit>\d+)/', $str, $matches);
print_r ( $matches );
?>
~~~
输出:
~~~
Array
(
[0] => Array
(
[0] => a: 1
[1] => b: 2
[2] => c: 3
) [name] => Array
(
[0] => a
[1] => b
[2] => c
) [1] => Array
(
[0] => a
[1] => b
[2] => c
) [digit] => Array
(
[0] => 1
[1] => 2
[2] => 3
) [2] => Array
(
[0] => 1
[1] => 2
[2] => 3
))
~~~
### 3 源字符串使用正则表达式分隔preg_split()
~~~
array preg_split ( string $pattern , string $subject [, int $limit = -1 [, int $flags = 0 ]] )
~~~
> $pattern:结果字符串正则描述
> $subject:源字符串
> $limit: 限制分割得到字符串个数,最后一个包含所有剩余部分
> -1,0,null代表不限制,
> $flags: 匹配状态标记
> PREG_SPLIT_NO_EMPTY
> 返回分隔后的非空部分。
> PREG_SPLIT_DELIM_CAPTURE
> 返回用于分隔的模式中的括号表达式将被捕获
> PREG_SPLIT_OFFSET_CAPTURE
> 对于每一个出现的匹配返回时将会附加字符串偏移量
> 每个元素成为一个由第0 个元素为分隔后的子串,第1个元素为该子串在subject 中的偏移量组成的数组
> 返回值
> 返回一个使用 pattern 边界分隔 subject 后得到 的子串组成的数组。
* * * * *
>[info] 示例一 获取搜索字符串的部分
~~~
<?php
//使用逗号或空格(包含" ", \r, \t, \n, \f)分隔短语
$keywords = preg_split ( "/[\s,]+/" , "hypertext language, programming" );
print_r ( $keywords );
?>
~~~
输出:
~~~
Array
(
[0] => hypertext
[1] => language
[2] => programming
)
~~~
>[info] 示例二 将一个字符串分隔为组成它的字符
~~~
<?php
$str = 'string' ;
$chars = preg_split ( '//' , $str , - 1 , PREG_SPLIT_NO_EMPTY );
print_r ( $chars );
?>
~~~
以上例程会输出:
~~~
Array
(
[0] => s
[1] => t
[2] => r
[3] => i
[4] => n
[5] => g
)
~~~
>[info] 示例三 分隔一个字符串并获取每部分的偏移量
~~~
<?php
$str = 'hypertext language programming' ;
$chars = preg_split ( '/ /' , $str , - 1 , PREG_SPLIT_OFFSET_CAPTURE );
print_r ( $chars );
?>
~~~
输出:
~~~
Array
(
[0] => Array
(
[0] => hypertext
[1] => 0
) [1] => Array
(
[0] => language
[1] => 10
) [2] => Array
(
[0] => programming
[1] => 19
))
~~~
### 4 源字符串搜索和替换 preg_replace()
~~~
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
~~~
> $pattern: 结果字符串正则描述 字符串或数组
> $replacement: 替换的字符串 字符串或数组
> 1 $pattern字符串,$replacement字符串:对应替换
> 2 $pattern数组,$replacement字符串:都使用本字符串替换
> 3 $pattern数组,$replacement数组
> 数组长度相等 对应替换
> 前者大于后者 多余使用空字符串替换
> 4 $replacement可以包含后向引用$n(也可以使用\\n)
> 后向引用见下文介绍
> $subject: 源字符串 字符串或数组
> $subject数组,在$subject的每一个元素上进行,返回值是替换结果组成的数组
> $limit: 替换最大次数 默认为-1 无限
> $count: 保存替换的完成次数
> 返回值
>> 如果subject是一个数组, preg_replace() 返回一个数组,
其他情况下返回一个字符串。
>> 如果匹配被查找到,替换后的subject被返回,
其他情况下 返回没有改变的 subject。如果发生错误,返回 NULL 。
* * * * *
>[info] 示例一 使用后向引用紧跟数值原文
~~~
<?php
$string = 'April 15, 2003' ;
$pattern = '/(\w+) (\d+), (\d+)/i' ;
$replacement = '${1}1,$3' ;
echo preg_replace ( $pattern , $replacement , $string );
?>
~~~
输出
`April1,2003`
>[info] 示例二 preg_replace() 中使用基于索引的数组
~~~
<?php
$string = 'The quick brown fox jumped over the lazy dog.' ;
$patterns = array();
$patterns [ 0 ] = '/quick/' ;
$patterns [ 1 ] = '/brown/' ;
$patterns [ 2 ] = '/fox/' ;
$replacements = array();
$replacements [ 2 ] = 'bear' ;
$replacements [ 1 ] = 'black' ;
$replacements [ 0 ] = 'slow' ;
echo preg_replace ( $patterns , $replacements , $string );
?>
~~~
输出
`The bear black slow jumped over the lazy dog.`
>[info] 示例三 替换一些值
~~~
<?php
$patterns = array ( '/(19|20)(\d{2})-(\d{1,2})-(\d{1,2})/' ,
'/^\s*{(\w+)}\s*=/' );
$replace = array ( '\3/\4/\1\2' , '$\1 =' );
echo preg_replace ( $patterns , $replace , '{startDate} = 1999-5-27' );
?>
~~~
输出:
`$startDate = 5/27/1999`
>[info] 示例四 剥离空白字符
~~~
<?php
$str = 'foo o' ;
$str = preg_replace ( '/\s\s+/' , ' ' , $str );
// 将会改变为'foo o'
echo $str ;
?>
~~~
> 这个例子剥离多余的空白字符
* * * * *
>[info] 示例五 使用参数count
~~~
<?php
$count = 0 ;
echo preg_replace (array( '/\d/' , '/\s/' ), '*' , 'xp 4 to' , - 1 , $count );
echo $count ; //3
?>
~~~
输出:
~~~
xp***to
3
~~~
* * * * *
### 5 源字符串搜索和替换 preg_replace_callback()
~~~
mixed preg_replace_callback ( mixed $pattern , callable $callback , mixed $subject [, int $limit = -1 [, int &$count ]] )
~~~
> $pattern:同上
> $callback: 替换回调函数,参数是匹配结果数组
>> string handler ( array $matches )
>> 使用匿名函数作为 preg_replace_callback() 调用时的回调
> $subject:同上
> $limit:同上
> $count:同上
* * * * *
>[info] 示例一 preg_replace_callback() 示例
~~~
<?php
// 将文本中的年份增加一年.
$text = "April fools day is 04/01/2002\n" ;
$text .= "Last christmas was 12/24/2001\n" ;
// 回调函数
function next_year ( $matches )
{
// 通常: $matches[0]是完成的匹配
// $matches[1]是第一个捕获子组的匹配
// 以此类推
return $matches [ 1 ].( $matches [ 2 ]+ 1 );
}
echo preg_replace_callback (
"|(\d{2}/\d{2}/)(\d{4})|" ,
"next_year" ,
$text );
?>
~~~
输出:
~~~
April fools day is 04/01/2003
Last christmas was 12/24/2002
~~~
>[info] 示例二 preg_replace_callback() 使用递归构造处理BB码的封装
~~~
<?php
$input = "plain [indent] deep [indent] deeper [/indent] deep [/indent] plain" ;
function parseTagsRecursive ( $input )
{
/* 译注: 对此正则表达式分段分析
* 首尾两个#是正则分隔符
* \[indent] 匹配一个原文的[indent]
* ((?:[^[]|\[(?!/?indent])|(?R))+)分析:
* (?:[^[]|\[(?!/?indent])分析:
* 首先它是一个非捕获子组
* 两个可选路径, 一个是非[字符, 另一个是[字符但后面紧跟着不是/indent或indent.
* (?R) 正则表达式递归
* \[/indent] 匹配结束的[/indent]
* /
$regex = '#\[indent]((?:[^[]|\[(?!/?indent])|(?R))+)\[/indent]#';
if (is_array($input)) {
$input = '<div style="margin-left: 10px">'.$input[1].'</div>';
}
return preg_replace_callback($regex, 'parseTagsRecursive', $input);
}
$output = parseTagsRecursive($input);
echo $output;
?>
~~~
### 6 源字符串搜索和替换 preg_filter()
### 7 转义正则表达式 preg_quote()
### 8 返回匹配模式的数组条目 preg_grep()
### 9 查找正则执行的最后一个错误 preg_last_error()
## 3 正则表达式规则详解
### 3-1 语法概述
正则表达式可以看做一种**语法经过压缩的程序语言**
因此从程序语言的角度看待正则表达式,
* * * * *
#### 3-1-1正则界定符
>[info] 1 正则表达式使用一组字符串表示界定符,
正则表达式与php使用`<?php` `?>`作为界定符原理相同,
正则表达式使用界定符包围正则字符串作为正则代码。
经常遇到界定符包括**正斜线(/)、hash符号(#) 以及取反符号(~) 括号() {} []**
~~~
/foo bar/ ; "/"界定符
#^[^0-9]$# ; "#"界定符
+php+ ; "+"界定符
%[a-zA-Z0-9_-]% ; "%"界定符
~~~
正则表达式中需要匹配界定符时 使用反斜线进行转义
~~~
/http:\/\// ;"/"作为界定符,匹配http://时,"/"需要转义
#http://# ;"#"作为界定符,匹配http://时,"/"不需要转义
~~~
>[info]2 可以用 preg_quote()函数对其进行转义
>[info]3 可以在结束分隔符后面增加模式修饰符。
* * * * *
#### 3-1-2语法组成
正则表达式中的语法可以分为**数据结构与逻辑控制**
**数据结构包括:**
>[info] 1 常量字符串:普通字符组成
> 2 变量字符串, 特殊字符组成
**逻辑控制包括:**
>[info] 1 变量字符串获取,变量字符串赋值,
> 2 匹配位置控制,逻辑if判断,逻辑for循环,逻辑或判断
### 3-2 正则表达式数据结构
#### 3-2-1 常量字符串
**常量字符串通常用来精确匹配字符串组成。**
>[info] 1 匹配常量字符串hello
~~~
<?php
application/index/controller/Index.php
public function hello(){
$str='hello world';
$pattern='/hello/';
preg_match($pattern,$str,$matches);
dump($matches);
}
?>
~~~
输出
~~~
http://127.0.0.1/tp5/public/index.php/Index/Index/hello
array(1) {
[0] => string(5) "hello"
}
~~~
>[info] 2 匹配忽略大小写常量字符串hello
~~~
<?php
application/index/controller/Index.php
public function hello(){
$str='Hello world';
$pattern='/hello/i';
preg_match($pattern,$str,$matches);
dump($matches);
}
?>
~~~
输出
~~~
http://127.0.0.1/tp5/public/index.php/Index/Index/hello
array(1) {
[0] => string(5) "Hello"
}
~~~
#### 3-2-2 变量字符串
**变量字符串使用正则表达式中的特殊字符模糊匹配字符串**
>[info] 1 变量字符串元素
变量字符串使用正则表达式的**特殊字符组成**
这里的特殊字符串可以看做**程序语言的数据类型**概念
根据其组成的复杂度分为**简单变量字符,字符类变量字符**
简单变量字符 使用**单个特殊元字符**表示
字符类变量字符 使用**带有方括号[]声明格式**表示
>[info] 2 简单变量字符
~~~
\d ;匹配一个数字字符 等价于[0-9]
\D ;匹配一个非数字字符 等价于[^0-9]
\s ;匹配一个任意空白字符 等价于[\n\r\t\v\f]
\S ;匹配一个任意非空白字符 等价于[^\n\r\t\v\f]
\w ;匹配一个字母/数字/下划线/汉字字符 等价于[A-Za-z0-9_]
\W ;匹配一个任意非单词字符 等价于[^A-Za-z0-9_]
. ;匹配一个 除换行\n外的任意字符 等价于[^\n]
~~~
~~~
\n ;匹配一个换行符 等价于\x0a和\cL
\r ;匹配一个回车符 等价于\x0d和\cM
\t ;匹配一个水平制表符 等价于\x09和\cI
\v ;匹配一个垂直制表符 等价于\x0b和\cK
\f ;匹配一个换页符 等价于\x0c和\cL
~~~
~~~
<?php
application/index/controller/Index.php
public function hello(){
$str='<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p>欢迎使用 <b>ThinkPHP5</b>!</p></div><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_bd568ce7058a1091"></thinkad>';
// 数字字符
$pattern1='/\d/';
preg_match($pattern1,$str,$matches1);
dump($matches1);
// 空白字符
$pattern2='/\s/';
preg_match($pattern2,$str,$matches2);
dump($matches2);
//字符/数字/下划线/汉字字符
$pattern3='/\w/';
preg_match($pattern3,$str,$matches3);
dump($matches3);
}
?>
~~~
输出
~~~
http://127.0.0.1/tp5/public/index.php/Index/Index/hello
array(1) {
[0] => string(1) "0"
}
array(1) {
[0] => string(1) " "
}
array(1) {
[0] => string(1) "s"
}
~~~
>[info] 3 字符类变量字符
~~~
[xyz] ;匹配xyz其中一个字符
[a-z] ;匹配a到z其中一个字符
[A-Z] ;匹配A到Z其中一个字符
[0-9] ;匹配0到9其中一个字符
[^xyz] ;匹配除xyz以外的一个字符
[^a-z] ;匹配除a到z外的一个字符
~~~
~~~
<?php
application/index/controller/Index.php
public function hello(){
$str='<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p>欢迎使用 <b>ThinkPHP5</b>!</p></div><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_bd568ce7058a1091"></thinkad>';
// x y z中一个
$pattern1='/[xyz]/';
preg_match($pattern1,$str,$matches1);
dump($matches1);
// a-z中一个
$pattern2='/[a-z]/';
preg_match($pattern2,$str,$matches2);
dump($matches2);
// A-Z中一个
$pattern3='/[A-Z]/';
preg_match($pattern3,$str,$matches3);
dump($matches3);
// 0-9中一个
$pattern4='/[0-9]/';
preg_match($pattern4,$str,$matches4);
dump($matches4);
}
?>
~~~
输出:
~~~
http://127.0.0.1/tp5/public/index.php/Index/Index/hello
array(1) {
[0] => string(1) "y"
}
array(1) {
[0] => string(1) "s"
}
array(1) {
[0] => string(1) "T"
}
array(1) {
[0] => string(1) "0"
}
~~~
### 3 正则表达式逻辑控制
#### 3-3-1 匹配位置控制(^ $ \b)
~~~
^x ;匹配以x开头的字符串
$x ;匹配以x结尾的字符串
\bxy\b ;匹配以x开头y结尾的单词
~~~
~~~
<?php
application/index/controller/Index.php
public function hello(){
$str='<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p>欢迎使用 <b>ThinkPHP5</b>!</p></div><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_bd568ce7058a1091"></thinkad>';
// 是否已s开头
$pattern1='/^a/';
preg_match($pattern1,$str,$matches1);
dump($matches1);
// 是否以a结尾
$pattern2='/a$/';
preg_match($pattern2,$str,$matches2);
dump($matches2);
// 以t开头中间是字母的e结尾单词
$pattern3='/\bt\w*e\b/';
preg_match($pattern3,$str,$matches3);
dump($matches3);
}
?>
~~~
输出:
~~~
array(0) {
}
array(0) {
}
array(1) {
[0] => string(4) "type"
}
~~~
#### 3-3-2 匹配字符或控制(|)
~~~
x|y ;匹配x或y中一个字符
[a-z]|[A-Z] ;匹配a到z或A到Z中一个字符
~~~
#### 3-3-3 字符匹配获取和赋值(()\1,()$1, (?<name>)<name>)
变量获取:匹配分组()
>[info] 使用()可以获取指定匹配内容到分组中
~~~
(exp) 匹配exp变量,并存储到结果分组中
~~~
~~~
(\d) 匹配数字分组,并进行存储
((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?) 匹配多个分组,并进行存储
~~~
变量赋值:反向引用\1 $1 <name>
>[info] 获取分组后,可以使用反向引用进一步操作
> 可以使用\n或者$n的形式获取对应分组内容(n从1开始)
> 也可以指定组名<Word>进行反向引用
~~~
\b(\w+)\b\s+\1\b
;使用\1对匹配内容(\w)进行反向引用
\b(?<Word>\w+)\b\s+\k<Word>\b
;使用<Word>对匹配内容进行反向引用
~~~
#### 3-3-4 字符前后if判断 ((?=),(?!),(?<=),(?<!))
~~~
(?<=exp)x 匹配前面是exp的x (if x.prev == exp){匹配x}
(?<!exp)x 匹配前面不是exp的x (if x.prev != exp){匹配x}
x(?=exp) 匹配后面是exp的x (if x.next == exp){匹配x}
x(?!exp) 匹配后面不是exp的x (if x.next != exp){匹配x}
多个判断组合
或判断 (?<=bullock|donkey)x 匹配前面是bullock或者donkey的x
且判断 (?<=\d{3})(?<!999)foo 匹配前面是3个数字而且不是999的foo
嵌套判断 (?<=(?<!foo)bar)baz 嵌套判断匹配前面有 ”bar” 但是 ”bar” 前面没有 ”foo” 的 ”baz”。
嵌套判断 (?<=\d{3}…(?<!999))foo 匹配前面有三个数字字符紧跟 3 个不是 999 的任意字符的 ”foo”。
~~~
#### 3-3-5 字符循环次数for循环 (*,+,?,{n,m})
~~~
x* ;匹配零或多次个x字符 for(i=0;i>=0;i++){匹配x*i}
x+ ;匹配一次或多次个x字符 for(i=0;i>0;i++){匹配x*i}
x? ;匹配零次或一次个x字符 for(i=0;0<=i<=1;i++){匹配x*i}
x{n} ;匹配n次个x字符 for(i=0;i==n;i++){匹配x*i}
x{n,} ;匹配n次或更多次个x字符 for(i=0;i>=n;i++){匹配x*i}
x{n,m} ;匹配n到m次个x字符 for(i=0;n<=x<=m;i++){匹配x*i}
~~~
#### 3-3-6 匹配模式修饰符
~~~
\i ;忽略大小写
\m ;多行字符串检测
\s ;.字符匹配包括换行符(\n)在内的所有字符
~~~
#### 3-3-7 子组高级控制
>[info] 1 命名性子组
(?<name>exp) 匹配exp变量,并存储到名称为name的结果分组中
* * * * *
>[info] 2 非捕获性子组
(?:exp) 匹配exp变量,不进行存储,
* * * * *
>[info] 3 一次性子组
(?>exp) 匹配exp变量,不对其内部进行匹配
使用\d+foo 匹配123456bar
当最长匹配6个数字后,匹配foo失败时
正则解析器会尝试匹配5个数字,再匹配foo,如果失败
则依次尝试4个数字,3个数字,2个数字等
为了第一次匹配失败后,就停止匹配,可以使用一次性子组
(?>\d+)foo 匹配 123456bar
第一次匹配失败时就返回false
* * * * *
>[info] 4 条件性子组
(?(conditon)yes-pattern|no-pattern)
**condtion为数字**, 该数字代表的(之前的)子组得到匹配时
使用yes-pattern,否则存在no-pattern使用no-pattern
~~~
( \( )? [^()]+ (?(1) \) )
~~~
**condtion为字符串** , 得到对模式或子模式的递归调用时满足?
**condition是前后判断**,判断匹配成功
使用yes-pattern,否则存在no-pattern使用no-pattern
~~~
(?(?=[^a-z]*[a-z])\d{2}-[a-z]{3}-\d{2}|\d{2}-\d{2}-\d{2})
~~~
## 4 正则表达式示例分析
~~~
1 application\index\controller\Regextest.php
<?php
namespace app\index\controller;
class Regextest extends Controller
{
public function test()
{
$extend = $this->getRegex('extend');
$block = $this->getRegex('block');
$literal = $this->getRegex('literal');
$include = $this->getRegex('include');
$taglib = $this->getRegex('taglib');
$layout = $this->getRegex('layout');
$tag = $this->getRegex('tag');
dump($extend);
dump($block);
dump($literal);
dump($include);
dump($taglib );
dump($layout);
dump($tag);
}
private function getRegex($tagName)
{
$begin = '{';
$end = '}';
$single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false;
$regex = '';
switch ($tagName) {
case 'block':
if ($single) {
$regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?<name>[\w\/\:@,]+)\\1(?>[^' . $end . ']*)|\/' . $tagName . ')' . $end;
} else {
$regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?<name>[\w\/\:@,]+)\\1(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end;
}
break;
case 'literal':
if ($single) {
$regex = '(' . $begin . $tagName . '\b(?>[^' . $end . ']*)' . $end . ')';
$regex .= '(?:(?>[^' . $begin . ']*)(?>(?!' . $begin . '(?>' . $tagName . '\b[^' . $end . ']*|\/' . $tagName . ')' . $end . ')' . $begin . '[^' . $begin . ']*)*)';
$regex .= '(' . $begin . '\/' . $tagName . $end . ')';
} else {
$regex = '(' . $begin . $tagName . '\b(?>(?:(?!' . $end . ').)*)' . $end . ')';
$regex .= '(?:(?>(?:(?!' . $begin . ').)*)(?>(?!' . $begin . '(?>' . $tagName . '\b(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end . ')' . $begin . '(?>(?:(?!' . $begin . ').)*))*)';
$regex .= '(' . $begin . '\/' . $tagName . $end . ')';
}
break;
case 'restoreliteral':
$regex = '<!--###literal(\d+)###-->';
break;
case 'include':
$name = 'file';
case 'taglib':
case 'layout':
case 'extend':
if (empty($name)) {
$name = 'name';
}
if ($single) {
$regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?<name>[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end;
} else {
$regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?<name>[\$\w\-\/\.\:@,\\\\]+)\\1(?>(?:(?!' . $end . ').)*)' . $end;
}
break;
case 'tag':
$begin = '{';
$end = '}';
if (strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1) {
$regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>[^' . $end . ']*))' . $end;
} else {
$regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>(?:(?!' . $end . ').)*))' . $end;
}
break;
}
return '/' . $regex . '/is';
}
}
~~~
输出:
~~~
string(86) "/{extend\b(?>(?:(?!name=).)*)\bname=([\'\"])(?<name>[\$\w\-\/\.\:@,\\]+)\1(?>[^}]*)}/is"
string(89) "/{(?:block\b(?>(?:(?!name=).)*)\bname=([\'\"])(?<name>[\w\/\:@,]+)\1(?>[^}]*)|\/block)}/is"
string(97) "/({literal\b(?>[^}]*)})(?:(?>[^{]*)(?>(?!{(?>literal\b[^}]*|\/literal)}){[^{]*)*)({\/literal})/is"
string(87) "/{include\b(?>(?:(?!file=).)*)\bfile=([\'\"])(?<name>[\$\w\-\/\.\:@,\\]+)\1(?>[^}]*)}/is"
string(86) "/{taglib\b(?>(?:(?!name=).)*)\bname=([\'\"])(?<name>[\$\w\-\/\.\:@,\\]+)\1(?>[^}]*)}/is"
string(86) "/{layout\b(?>(?:(?!name=).)*)\bname=([\'\"])(?<name>[\$\w\-\/\.\:@,\\]+)\1(?>[^}]*)}/is"
~~~
对于extend正则分解如下
~~~
/{ ;开始标签{
extend\b 以extend开始的单词匹配
(?> ; ?> 子组一次性匹配声明
(?: ; ?: 子组不捕获匹配声明
(?!name=). ; 后面不是name=的任意字符.
)* ;任意字符数量为零或多个
)\b ;以上述字符为结束
name= ;匹配name=
([\'\"]) ;匹配单引号或者双引号子组
(?<name> ;命名子组name声明
[\w\/\:@,]+ ;双引号中的字符串组成
)
\\1 ;匹配的单引号或者双引号子组
(?>
(?> ;子组一次性匹配声明
[^}]* ;非}的零或多个字符
) ;这个做什么?
}/ ;结束标签}
~~~
- 更新记录
- 概述
- 文件索引
- 函数索引
- 章节格式
- 框架流程
- 前:章节说明
- 主:(index.php)入口
- 主:(start.php)框架引导
- 主:(App.php)应用启动
- 主:(App.php)应用调度
- C:(Controller.php)应用控制器
- M:(Model.php)数据模型
- V:(View.php)视图对象
- 附:(App.php)应用启动
- 附:(base.php)全局变量
- 附:(common.php)模式配置
- 附:(convention.php)全局配置
- 附:(Loader.php)自动加载器
- 附:(Build.php)自动生成
- 附:(Hook.php)监听回调
- 附:(Route.php)全局路由
- 附:(Response.php)数据输出
- 附:(Log.php)日志记录
- 附:(Exception.php)异常处理
- 框架工具
- 另:(helper.php)辅助函数
- 另:(Cache.php)数据缓存
- 另:(Cookie.php)cookie操作
- 另:(Console.php)控制台
- 另:(Debug.php)开发调试
- 另:(Error.php)错误处理
- 另:(Url.php)Url操作文件
- 另:(Loader.php)加载器实例化
- 另:(Input.php)数据输入
- 另:(Lang.php)语言包管理
- 另:(ORM.php)ORM基类
- 另:(Process.php)进程管理
- 另:(Session.php)session操作
- 另:(Template.php)模板解析
- 框架驱动
- D:(\config)配置解析
- D:(\controller)控制器扩展
- D:(\model)模型扩展
- D:(\db)数据库驱动
- D:(\view)模板解析
- D:(\template)模板标签库
- D:(\session)session驱动
- D:(\cache)缓存驱动
- D:(\console)控制台
- D:(\process)进程扩展
- T:(\traits)Trait目录
- D:(\exception)异常实现
- D:(\log)日志驱动
- 使用范例
- 服务器与框架的安装
- 控制器操作
- 数据模型操作
- 视图渲染控制
- MVC开发初探
- 模块开发
- 入口文件定义全局变量
- 运行模式开发
- 框架配置
- 自动生成应用
- 事件与插件注册
- 路由规则注册
- 输出控制
- 多种应用组织
- 综合应用
- tp框架整合后台auto架构快速开发
- 基础原理
- php默认全局变量
- php的魔术方法
- php命名空间
- php的自动加载
- php的composer
- php的反射
- php的trait机制
- php设计模式
- php的系统时区
- php的异常错误
- php的输出控制
- php的正则表达式
- php的闭包函数
- php的会话控制
- php的接口
- php的PDO
- php的字符串操作
- php的curl
- 框架心得
- 心:整体结构
- 心:配置详解
- 心:加载器详解
- 心:输入输出详解
- 心:url路由详解
- 心:模板详解
- 心:模型详解
- 心:日志详解
- 心:缓存详解
- 心:控制台详解
- 框架更新
- 4.20(验证类,助手函数)
- 4.27(新模型Model功能)
- 5.4(新数据库驱动)
- 7.28(自动加载)