资讯专栏INFORMATION COLUMN

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

Anchorer / 1078人阅读

摘要:正则表达式作为一个匹配的模板,是由定界符,原子普通字符,例如有特殊功能的字符称为元字符,例如等以及模式修正符等部分组成的文字模式。正则表达式中可以使用编码。限定符限定符用来指定正则表达式的一个给定原子必须要出现多少次才能满足匹配。

正则表达式的定义

正则表达式就是描述字符排列模式的一种自定义的语法规则。
由于正则表达式本身具有一套非常完整的、可以编写模式的语法体系,提供了一种灵活且直观的字符串处理方法,故正则表达式也称为模式表达式。

正则表达式的特点

正则表达式并不是PHP特有的,JavaScript、Java、Perl、MySQL中都可以应用到正则表达式。

正则表达式通过构建具有特定规则的模式,与输入的字符串信息进行比较,从而实现字符串的匹配、查找、替换及分割等操作。

例子:

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

正则表达式是由普通字符与具有特殊意义的字符组成字符串

效率问题,如果可以使用字符串函数完成任务,那尽量不使用正则表达式函数

对于一些复杂操作,如表单验证,计算发布文章中有多少个句子,抓取网页中某种格式的句子等使用正则表达式以及相关函数能够更好地实现

正则表达式具有一定的编写规则(语义),是一种模式

若正则表达式不与 相应的正则表达式函数使用,那么正则表达式仅仅是字符串,要达成匹配、查找、替换以及分割功能,正则表达式必须与相应的正则表达式函数配套使用。

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

PCRE(Perl Compatible Regular Expression)

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

POSIX(Portable Operation System Interface)

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

注意
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)、有特殊功能的字符(称为元字符,例如*、+、?等),
以及模式修正符等部分组成的文字模式。

例子:

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

或者

"/^(.+?)$/sim"

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

定界符为PCRE不同于POSIX的特点之一。

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

例子:
{ }# #||!!

//              // 使用反斜线作为定界符号 合法
|(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指明的控制符。
             例如,cM匹配一个Control-M或者回车符。x的值必须为A-Z或a-z之一,
             否则,将c视为一个原义的"c"字符。

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

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

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

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

+ v         匹配一个垂直制表符。等价于x0b和cK
3.通用字符类型做为原子

前面介绍的原子,都是一个原子只能匹配一个字符。但是有时候我们需要一个原子可以匹配一类字符。

在正则表达式中可以直接使用一些代表范围的原子

原子字符            含义描述

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

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

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

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

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

S                    匹配除空白字符以外的任何一个字符,等价于[^	f
v
]
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              匹配输入字符串的开始位置(或在多行模式下行的开头,即紧随一换行符后)

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

                 匹配单词的边界

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

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

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

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

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

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

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

其中"*""+""{n,}"限定符是贪婪的,因为它们会尽可能地匹配文字。

注意

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

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

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

2. 边界限制(断言)

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

例如:

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

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

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

"/is/"                匹配此字符串中是否含有单词"is",因为在字符串"is"两边都需要有边界

"/Bis/"                查找字符串"is"时,左边不能有边界而右边必须右边界,如"this"匹配成功
3. 句号(.)

在字符类以外,模式中的圆点可以匹配目标中的任何一个字符,包括不可打印字符。但不匹配换行符号(默认情况下),相当于"[^ ]"(UNIX系统)或者"[^ ]"(Windows系统)。

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

处理圆点与处理^和$是完全独立的,唯一的联系是涉及换行

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

4. 模式选择符(|)

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

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

例如:LAMP | J2EE表示匹配LAMP,也可以匹配J2EE,由于|其优先级最低,所以并
不表示匹配"LAMP2EE"或者"LAMJEE"

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

5. 模式单元

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

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

例子:

"/(very)*good/" //可以匹配good、very good、very very good

6. 后向引用

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

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

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

以后使用**。

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

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

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

如下所示:

"/^d{4}Wd{2}Wd{2$}/"    // 这是一个匹配日期的格式,如2008-08/08或者2008/08-08 等
"/^d{4}(W)d{2}1d{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、A、        边界限制

5               |                           模式选择
模式修正符

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

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

模式修正符可以单个使用,也可以多个组合使用

模式修正符

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

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

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

s                                 如果设定了次修正符,则模式中的圆点元字符`"."`匹配所有的字符,包括换行符。
                                  即将字符串视为单行,换行符作为普通字符看待。

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

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

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

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

注意:

模式 "/Web Server/ix"可以用来匹配字符串"webserver",忽略大小写和空白。

贪婪匹配,在匹配成功的前提下,尽可能多的去匹配*,+,{n,},.*是贪婪的,例如:/a.*e/去匹配字符串"abcd fsdfsdfsesfdfsdfsesdfedfsdfses",由于".*"是贪婪的匹配,会从这个字符串中匹配出"abcd fsdfsdfsesfdfsdfsesdfedfsdfse",从第一个a开始,知道最后一个字母e结束,都属于".*"内容。如果想取消这种贪婪模式,可以使用模式修正符"U"或者在模式中使用.*?,在.*后面加个?。为了兼容Perl正则函数,可能会没有模式修正符"U",我们建议使用在".*"后加"?"来实现懒惰模式(即非贪婪模式)如果"U""?"同时使用,像这样"/a.*?e/U",则匹配"abcdfsdfsdfsesfdfsdfsesdfedfsdfse",相当于又启用了贪婪模式。

惰性模式,在匹配成功的前提下,尽可能少的去匹配(注意点同上)

模式"/^is/m"可以匹配字符串"this is ais test"中的"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");



  • sdf ↩

  • 文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/30573.html

    相关文章

    • PHP小知识点

      摘要:那些琐碎的知识点作者记录的的很奇特很难记的知识点。易错知识点整理注意和的区别中和都是输出的作用,但是两者之间还是有细微的差别。今天手头不忙,总结一下,分享过程中掌握的知识点。 深入理解 PHP 之:Nginx 与 FPM 的工作机制 这篇文章从 Nginx 与 FPM 的工作机制出发,探讨配置背后的原理,让我们真正理解 Nginx 与 PHP 是如何协同工作的。 PHP 那些琐碎的知识...

      hover_lew 评论0 收藏0
    • Java开发

      摘要:大多数待遇丰厚的开发职位都要求开发者精通多线程技术并且有丰富的程序开发调试优化经验,所以线程相关的问题在面试中经常会被提到。将对象编码为字节流称之为序列化,反之将字节流重建成对象称之为反序列化。 JVM 内存溢出实例 - 实战 JVM(二) 介绍 JVM 内存溢出产生情况分析 Java - 注解详解 详细介绍 Java 注解的使用,有利于学习编译时注解 Java 程序员快速上手 Kot...

      LuDongWei 评论0 收藏0
    • PHP 正则达式入门 Getting Started with PHP Regular Expre

      摘要:兼容的正则表达式已经实现了很多使用不同解析引擎的正则函数。中主要有两个正则解析器一个称为,另一个称为兼容正则表达式。在中,每个正则表达式模式都是使用符合格式的字符串。 原文链接: Getting Started with PHP Regular Expressions Last-Modified: 2019年5月10日16:23:19译者注: 本文是面向0正则基础的phper, 很多...

      zhangke3016 评论0 收藏0
    • 正则系列——JavaScript正则达式基础语法巩固篇

      摘要:基础语法巩固正则表达式引擎根据正则去匹配字符的时候,是通过检查索引的方式。妻原配就是好,可以直接使用正则表达式调用它。下面解释一下上面这些常用的元字符查找单个字符,除了换行和行结束符。正则系列文章整理到了 上一章内容:正则表达式实战篇 知识回顾 前2章分别学习了正则表达式入门技巧,以及遇到正则需求该如何去分析问题,还有正则表达式实战的一些场景解释。 这一章内容偏向理论,推荐你点击开头的...

      phodal 评论0 收藏0

    发表评论

    0条评论

    Anchorer

    |高级讲师

    TA的文章

    阅读更多
    最新活动
    阅读需要支付1元查看
    <