PHP基础系列之正则表达式(一)

正则表达式的定义

正则表达式就是描述字符排列模式的一种自定义的语法规则。
因为正则表达式自己具备一套很是完整的、能够编写模式的语法体系,提供了一种灵活且直观的字符串处理方法,故正则表达式也称为模式表达式。web

正则表达式的特色

  1. 正则表达式并非PHP特有的,JavaScript、Java、Perl、MySQL中均可以应用到正则表达式。
  2. 正则表达式经过构建具备特定规则的模式,与输入的字符串信息进行比较,从而实现字符串的匹配、查找、替换及分割等操做。

例子:正则表达式

"/^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i"                // 匹配网址URL的正则表达式
"/<(\S*?)[^>]*>.*?<\/\\1>|<.*?/>/i"                                // 匹配HTML标记的正则表达式
"/\w+([-+.]\w)+*@\w+([-.]\w+)*\.\w+([-.]\w+)*/"                    // 匹配E-mail地址的正则表达式
"/^<img\s+[^>]*\s*src\s*=\s*([']?)(?<url>\S+)'?[^>l ]*>$/"        // 匹配img标签

注意

  1. 正则表达式是由普通字符与具备特殊意义的字符组成字符串
  2. 效率问题,若是可使用字符串函数完成任务,那尽可能不使用正则表达式函数
  3. 对于一些复杂操做,如表单验证,计算发布文章中有多少个句子,抓取网页中某种格式的句子等使用正则表达式以及相关函数可以更好地实现
  4. 正则表达式具备必定的编写规则(语义),是一种模式
  5. 若正则表达式不与 相应的正则表达式函数使用,那么正则表达式仅仅是字符串,要达成匹配、查找、替换以及分割功能,正则表达式必须与相应的正则表达式函数配套使用。

PHP中两套支持正则表达式的函数库

  1. PCRE(Perl Compatible Regular Expression)

    由PCRE库提供,与Perl语言兼容的正则表达式、使用"preg_"为前缀命名的函数,并且表达式都应被包含在定界符中。sql

  2. POSIX(Portable Operation System Interface)

    自从PHP5.3.0开始废弃.使用一套"ereg_"为前缀命名的函数。jsp

注意

1. 两套 函数库功能相似,执行效率不一样,通常来讲,实现相同功能,使用 PCRE库提供的正则表达式效率略占优点。
2. PCRE 函数须要模式以定界符闭合。
3. 不像POSIX,PCRE 扩展没有专门用于大小写不敏感匹配的函数。
   取而代之的是,支持使用i (PCRE_CASELESS) 模式修饰符完成一样的工做。 其余模式修饰符一样可用于改变匹配策略。
4. POSIX 函数从最左面开始寻找最长的匹配,可是 PCRE 在第一个合法匹配后中止。若是字符串 
   不匹配这没有什么区别,可是若是匹配,二者在结果和速度上都会有差异。 为了说明
   这个不一样, 考虑下面的例子(来自Jeffrey Friedl 的《精通正则表达式》一书)。 
   使用模式 one(self)?(selfsufficient)? 在字符串oneselfsufficient 上匹配,
   PCRE 会匹配到oneself,可是使用 POSIX,结果将是整个字符串 oneselfsufficient。
   两个子串都匹配原始字符串,可是 POSIX 将 最长的做为结果。

函数对比

POSIX PCRE
ereg_replace() preg_replace()
ereg() preg_match()
eregi_replace() preg_replace()
eregi() preg_match()
split() preg_split()
spliti() preg_split()
sql_regcase() 无对等函数
可参照PHP Manual

正则表达式语法规则

正则表达式描述了一种字符串的匹配模式,经过这个模式在特定的函数中对字符串进行匹配、查找、替换以及分割等操做。函数

正则表达式做为一个匹配的模板,是由定界符,原子(普通字符,例如a-z)、有特殊功能的字符(称为元字符,例如*、+、?等),
以及模式修正符等部分组成的文字模式。this

例子:编码

"/^https?\/\/(([a-zA-Z0-9_-])+(\.)?)*(\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i/"url

或者code

'/^<a.*?(?|\\t|\\r|\\n)?href=[\'"]?(.+?)[\'"]?(?(?|\\t|\\r|\\n)+.*?)?>(.+?)<\/a.*?>$/sim'server

1. 定界符中使用的是两个斜线"/",将模式放在它之间说明。
2. 原子用到了<、a、href、=、'、"、/、>等普通字符和\t、\r、\n等转义字符。
3. 元字符使用了[]、()、|、.、?、*、+等具备特殊含义的字符。
4. 用到模式修正符是在定界符最后一个斜线以后的三个字符"s"、"i"、"m"。

定界符

定界符为PCRE不一样于POSIX的特色之一。

除了字母、数字和反斜线之外的字符皆可为定界符

例子:
{ }# #||!!

/<\/w+>/              // 使用反斜线做为定界符号 合法
|(\d{3})-\d+|Sm     // 使用竖线"|"做为定界符号 合法
!^(?i)PHP[34]!        // 使用感叹号"!"做为定界符 合法
{^\s+(\s+)?$}       // 使用花括号"{}"做为定界符号 合法
/href='(.*)'        // 非法定界符,缺乏结束定界符
1-\d3-\d3-\d4/      // 非法定界符号,缺乏起始定界符

注意

若是没有特殊要求,通常使用//做为定界符

定界符是成对的,有开始符号,也有结束符号。

原子

原子是正则表达式中最基本的组成单位,并且在每一个模式中最少要包含一个原子。

原子是全部那些未显式指定为元字符的打印(能够在屏幕上输出的字符)和非打印字符(看不到的)组成的。
简单地说,可以在正则表达式中单独使用的字符就可称为原子

将其划分为五类

1. 普通字符做为原子

普通字符是编写正则表达式时最多见的原子,包括全部的大写和小写字母字符、全部数字等,例如:a-z、A-Z、0-9。

2. 非打印字符(看不到的)做为原子

原子字符                    \x09 含义描述

+ \cx        匹配由x指明的控制符。
             例如,\c\M匹配一个Control-M或者回车符。x的值必须为A-Z或a-z之一,
             不然,将c视为一个原义的'c'字符。

+ \f         匹配一个换页符。等价于\x0c和\cL

+ \n         匹配一个换行符。等价于\x0a和\cJ

+ \r         匹配一个回车符。等价于\x0d和\cM

+ \t         匹配一个制表符。等价于\x09和\cI

+ \v         匹配一个垂直制表符。等价于\x0b和\cK

3.通用字符类型作为原子

前面介绍的原子,都是一个原子只能匹配一个字符。可是有时候咱们须要一个原子能够匹配一类字符。

在正则表达式中能够直接使用一些表明范围的原子

原子字符            含义描述

\d                     匹配任意一个十进制数字,等价于[0-9]

\D                    匹配任意一个除十进制数字之外的字符,等价于[^0-9]

\w                    匹配任意一个数字、字母、下划线,等价于[0-9a-zA-Z_]

\W                    匹配除数字、字母、下划线之外的任意一个字符,等价于[^0-9a-zA-Z_]

\s                    匹配任意一个空白字符,等价于[\f\t\n\v\r]

\S                    匹配除空白字符之外的任何一个字符,等价于[^\t\f\n\v\r]

4. 自定义原子表([])做为原子

有些时候,上面六个通用字符并不能知足咱们的需求,咱们须要自定义一类原子,好比说奇数(1,3,5,7,9).
因此这时候须要咱们自定义一类原子(称之为类原子),使用原子表"[]"就能够定义一组彼此平等的原子,
只能匹配原子表中一个字符。

1表示匹配除表内原子外的任意字符,一般称之为排除原子表。

此外,在原子表中可使用连字符(-)链接一组按照ASCII码顺序排列的原子,可简化书写。

例子

'/[apj]sp/'             //  能够匹配asp、jsp、PSP三种,从原子表中仅选择一个做为原子
'/[^apj]sp/'            //   能够匹配除了asp、PSP、jsp三种之外的字符串,如xsp,ysp,zsp等
'/0[xX][0-9a-fA-F]/'    //   能够匹配一个简单的十六进制数,如0x2f、0X3AE或0x4aB等

5. 一些特殊字符和元字符做为原子

在正则表达式中,任何一个符号均可以做为原子使用,但若是该符号在正则表达式中有特殊意义,可使用转义字符""
取消它的特殊意义,做为一个普通字符使用。 如 "\. \* \+ \? \( \<\>"

" "转义字符能够将有意义的字符转成没意义的字符,还能够将没意义的字符转为有意义的字符

元字符

元字符是用于构建正则表达式的具备特殊含义的字符,如"*"、"+"、"?"等。

元字符不能单独出现,必须用来修饰原子。

若是要在正则表达式中使用包含元字符自己,为了使其失去特殊含义,则必须在前面加上""进行转义

正则表达式的元字符

元字符                含义描述

\*                     匹配0次,1次或者屡次其前面的原子

\+                   匹配1次或屡次其前的原子

?                   匹配0次或者1次其前的原子

.                  匹配除了换行符外的任意一个字符

|                   匹配两个或多个分支选择

{n}                表示其前面的原子刚好出现n次

{n,}               表示其前面的原子出现很多于n次

{n,m}              表示其前面的原子至少出现n次,最多出现n次

^或\A              匹配输入字符串的开始位置(或在多行模式下行的开头,即紧随一换行符后)

$或\Z              匹配输入字符串(或者在多行模式下行的结尾,即紧随一换行符后)

\b                 匹配单词的边界

\B                 匹配除单词边界之外的部分

[]                 匹配方括号中指定的任意一个原子

[^]                匹配除方括号中的原子之外的任意一个字符,排除原子表

()                 匹配其总体为一个原子,即模式单元。能够理解为由多个单个原子组成的大原子

\xn                   匹配n,其中n为十六进制转义值。十六进制转义值必须为肯定的两个数字长。
                   例如,`"\x41"`匹配`A`。`"\x041"`则等价于`"\x04&1"`。正则表达式中可使用ASCII编码。

1. 限定符

限定符用来指定正则表达式的一个给定原子必需要出现多少次才能知足匹配。

总共有"*""+""?""{n}""{n,}""{n,m}"六种限定符,他们之间的区别主要是重复匹配的次数不一样。

其中"*""+""{n,}"限定符是贪婪的,由于它们会尽量地匹配文字。

注意

元字符 "*" 表示0次、1次或屡次匹配其前的原子,也可使用"{0,}"完成一样的匹配

"+"可使用"{1,}"表示,

"?"可使用"{0,1}"表示。

2. 边界限制(断言)

用来限定字符串或单词的边界范围,以便得到更准确的匹配结果。元字符"^"("\A")"$"("\Z")
分别指字符串的开始于结束,而"\b"用于描述字符串中每一个单词的前或者后边界,
与之相反的元字符"\B"表示非单词边界。

例如:

有一个字符串 "this is a test", 使用边界限制以下:

"/^this/"                匹配此字符是否以字符串"this"开始的,匹配成功

"/test$/"                匹配此字符是否以字符串"test"结束的,匹配成功

"/\bis\b/"                匹配此字符串中是否含有单词"is",由于在字符串"is"两边都须要有边界

"/\Bis\b/"                查找字符串"is"时,左边不能有边界而右边必须右边界,如"this"匹配成功

3. 句号(.)

在字符类之外,模式中的圆点能够匹配目标中的任何一个字符,包括不可打印字符。但不匹配换行符号(默认状况下),至关于"[^\n]"(UNIX系统)或者"[^\n\r]"(Windows系统)。

可是若是设定了模式修正符"s",则圆点也会匹配换行符。

处理圆点与处理^和$是彻底独立的,惟一的联系是涉及换行

注意

1. `".*?"`或者`".+?"`组合来匹配除换行符之外的任何字符串。
   例如:`"/<b>.*?<\/b>/"`能够匹配以`<b>`,`</b>`标签开始于结束的任何不包括换行符的任意字符串

4. 模式选择符(|)

竖线字符"|"用来分隔多选一模式,在正则表达式中匹配两个或更多的选择之一。

|的优先级是最低的,因此应在最后考虑其功能。

例如:LAMP | J2EE表示匹配LAMP,也能够匹配J2EE,因为|其优先级最低,因此并
不表示匹配'LAMP2EE'或者'LAMJEE'

也能够像这样使用 "/Linux|Apache|MySQL|PHP/",表示能够从中任意匹配一组。

5. 模式单元

模式单元是使用元字符"()"将多个原子组成大原子使用。

一个模式单元中的表达式将被优先匹配

例子:

'/(very)*good/' //能够匹配good、very good、very very good

6. 后向引用

后向引用是一个正则表达式中一个重要的应用点。

使用()标记的开始和结束的多个原子,不只仅是一个独立的单元,也是一个子表达式(也称之为子模式)。

**对于一个正则表达式模式或部分模式两边添加圆括号将致使相关匹配存储到一个临时缓冲区中,能够被捕获供

之后使用**。

所捕获的每一个子匹配都按照正则表达式模式中从左以后所遇到的内容存储。存储子匹配的缓冲区编号从1开始,
连续编号直至最大99个表达式。

每一个缓冲区均可以使用\n访问,其中n为一个即是特定缓冲区的一位或两位十进制数。

例如:"\1""\2""\3"等形式的引用,在正则表达式模式中使用时还须要在前面加上一个反斜线,
将反斜线再次转义,
例如:"\\1""\\2""\\3"等形式的引用。

以下所示:

'/^\d{4}\W\d{2}\W\d{2$}/'    // 这是一个匹配日期的格式,如2008-08/08或者2008/08-08 等
'/^\d{4}(\W)\d{2}\\1\d{2$}/'    // 这是一个匹配日期的格式,如2008-08-08或者2008/08/08 等

当你要使用模式单元而又不想存储匹配结果时,可使用非捕获元字符"?:""?=""?!"来忽略对相关匹配的保存。

注意

在一些正则表达式中,使用非存储模式单元是必要的,能够改变其后向引用的顺序。

`/(Windows)(Linux)\\2OS/`          --- 使用"\2"再次引用第二个缓冲区中的字符串"Linux"
`/(?:Windows)(Linux)\\1OS/`        --- 使用"?:"忽略了第一个子表达式的存储,因此"\1"引用的就是"Linux"

7. 模式匹配的优先级

在使用正则表达式时,须要注意匹配的书序。一般相同优先级从左到右进行运算,不一样优先级的运算先高后低。

** 顺 序 **    ** 元 字 符 **               ** 描 述 **

1                \                         转义符号

2                ()、(?:)、(?=)、[]        模式单元和原子表

3               *、+、?、{n}、{n,}、{n,m}  重复匹配

4               ^、$、\b、\B、\A、\Z        边界限制

5               |                           模式选择

模式修正符

模式修正符在正则表达式定界符以外使用(最后一个斜线"/"以后)。

模式修正符能够调整正则表达式的解释,扩展了正则表达式在匹配、替换等操做时的某些功能;
并且模式修正符能够组合使用,更加强了正则表达式处理能力

模式修正符能够单个使用,也能够多个组合使用

模式修正符

**模式修正符**                        **功能描述**

i                                 在和模式进行匹配时不区分大小写

m                                 将字符串视为多行。默认开的正则开始`"^"`和`"$"`将目标字符串做为单一的一"行"
                                  字符(甚至其中包含有换行符也是如此)。若是在修饰符中加上`"m"`,那么开始和结束将会指字符串的每一行,每一行的开头就是`"^"`,结尾就是`"$"`。

s                                 若是设定了次修正符,则模式中的圆点元字符`"."`匹配全部的字符,包括换行符。
                                  即将字符串视为单行,换行符做为普通字符看待。

x                                 模式中的空白忽略不计,除非它已经被转义。

e                                 **只有在`preg_replace()`函数中,在替换字符串对逆向引用作正常的替换,将其做为
                                  PHP代码求值,而且其结果来替换所搜索的字符串**

U                                 本修正符反转了匹配数量的值使其使其不是默认的重复,
                                  而变成在后面跟上`"?"`才变得重复。**__这和Perl语言不兼容__**。也能够经过在模式中设定(U)修正符或者数量符以后跟一个问号`"?"`(例如.*?)来使用此选项。

D                                 模式中的美圆元字符仅匹配目标字符串的结尾。没有此选项时,若是最后
                                  一个字符是换行符,则美圆符号也会匹配次字符以前的内容。若是设定了m修正符,则忽略此选项

注意:

  1. 模式 "/Web Server/ix"能够用来匹配字符串"webserver",忽略大小写和空白。
  2. 贪婪匹配,在匹配成功的前提下,尽量多的去匹配*,+,{n,},.*是贪婪的,例如:/a.*e/去匹配字符串"abcd fsdfsdfsesfdfsdfsesdfedfsdfses",因为'.*'是贪婪的匹配,会从这个字符串中匹配出"abcd fsdfsdfsesfdfsdfsesdfedfsdfse",从第一个a开始,知道最后一个字母e结束,都属于'.*'内容。若是想取消这种贪婪模式,可使用模式修正符"U"或者在模式中使用.*?,在.*后面加个?。为了兼容Perl正则函数,可能会没有模式修正符"U",咱们建议使用在".*"后加"?"来实现懒惰模式(即非贪婪模式)若是"U""?"同时使用,像这样"/a.*?e/U",则匹配"abcdfsdfsdfsesfdfsdfsesdfedfsdfse",至关于又启用了贪婪模式。
  3. 惰性模式,在匹配成功的前提下,尽量少的去匹配(注意点同上)
  4. 模式"/^is/m"能够匹配字符串"this\nis\nais\ntest"中的'is',由于使用模式修正符"m"将字符串视为多行,第二行出现的"is"匹配成功。默认的正则开始^和结束'$'将目标字符串做为单一的一“行”(甚至包含换行符也是如此)。

Note:

看到这里是否是懵逼了?没错,我也懵逼。
不过没有关系,秉承Learning by doing的准则,
在接下来的教程中,我会一一演示。

更多文章请访问个人博客:Noapes

实例

/**
 *  无乱码截取中文字符
 * @param $str
 * @param int $start
 * @param $length
 * @param string $charset
 * @param bool|true $suffix
 * @return string|void
 */

function msubstr($str, $start=0, $length, $charset="utf-8", $suffix=true)
{
    if(function_exists("mb_substr"))
        return mb_substr($str, $start, $length, $charset);
    elseif(function_exists('iconv_substr')) {
        return iconv_substr($str,$start,$length,$charset);
    }
    $re['utf-8']   = "/[x01-x7f]|[xc2-xdf][x80-xbf]|[xe0-xef][x80-xbf]{2}|[xf0-xff][x80-xbf]{3}/";
    $re['gb2312'] = "/[x01-x7f]|[xb0-xf7][xa0-xfe]/";
    $re['gbk']    = "/[x01-x7f]|[x81-xfe][x40-xfe]/";
    $re['big5']   = "/[x01-x7f]|[x81-xfe]([x40-x7e]|xa1-xfe])/";
    preg_match_all($re[$charset], $str, $match);
    $slice = join("",array_slice($match[0], $start, $length));
    if($suffix) return $slice;
    return $slice;
}

echo msubstr('哈哈哈你好啊啊',2,3,'gb2312');

  1. sdf
相关文章
相关标签/搜索