正規表現の学習(1) --- 基本ルールのまとめ
正規表現の使い方をゲリラ的に遊んできたので、まとめてみます。愚かさなどのどうしようもない理由により、ASCII コードの文字と、PHP やその他の Unicode やその他のジャンルに関連する文字の一致については、出会ったときに覚えるだけなので、簡単に書くしかありません。将来的には、通常の状況に対処するだけで十分です。
まず、規則性の概念を受け入れる必要があります。これは、文字列検索 strpos などの単純なテキスト検索で、特定の文字列の出現を検索するだけです。たとえば、このように 5 文字を検索する場合、最初の 2 文字は数字、最後の 3 文字は文字であり、単純に文字列検索やその他の方法を使用する場合、大文字と小文字は区別されません。と非常に面倒に思えますが、正規表現を使うと非常に簡単です。通常のルールは非常に強力で柔軟です。もちろん、柔軟な一般ルールもたくさんあります。日常生活では、F ドライブ *.txt にあるすべてのテキスト ファイルを検索したり、正規表現スーパー検索をサポートする Sublime Text などの一部のエディターで検索したりするなど、無意識のうちに正規表現に似たものを使用します。 Linux コマンドでもよく使用されます: find . -name '*.log' -print は、現在のディレクトリ内で拡張子 log を持つファイルを検索するか、ログをクエリする場合に使用します。いいえ、似ているとしか言えません): show_list インターフェイスなどを記録する行の出力、および Apache の書き換えモジュールによるアクセス リンクの制御、これらは似ているか、正規表現の適用です。
PHP では、正規表現をサポートするために、preg、ereg、mb_ereg という 3 つのエンジンが使用されます。いわゆるエンジンの簡単な理解は、通常の一致検索を実行するときに PHP をサポートする基礎となるライブラリとインターフェイスです。さまざまなライブラリ さまざまな名前がありますが、現在使用されているのは preg で、機能と速度の点で他の 2 つよりも優れています。 preg エンジン (またはスイート)、つまり「Perl Regular Expression」(Perl 正規表現) は、当時の ereg スイートのパフォーマンスに満足できず、より良いライブラリを作りたいと考えていたある専門家から生まれました。 Perl 処理の通常のソース コードを調べましたが、彼の前の別の大きな牛もこの問題に遭遇しました。この大きな牛 (後者) は、Perl の通常のソース コードを研究し、それが面倒で複雑であることがわかったので、互換性のある一連の通常のルールを作成しました。 Perl を使用したライブラリ PCRE (Perl 互換正規表現) は、優れた効率性と完全なドキュメントを備えて明確に記述され、その後、前の専門家がそれを PHP に書き直して、現在までゆっくりと改良されてきました。したがって、preg は PCRE と互換性があり、PCRE は Perl の正規表現と互換性があります。 以上が歴史です。
単純な正規表現の例: '/abc/' は、文字列内の 3 つの文字 abc と一致することを意味します。これは、通常の文字列検索方法と完全に同等です。特殊記号には特殊な文字列があります。意味があり、規則的なパターンを表すこの文字列は一般に、 パターン (パターン) と呼ばれます。
1. 区切り文字
正規表現を定義するときは、最初に区切り文字を指定する必要があります。これは、式がここで始まり、そこで終わることを示します。最も一般的に使用されるのは、「一致」などの / です。 /abc/' は abc で、a から始まり c で終わります。 php では、#,! などの他のものを使用することもできます。 、{} (左側に {、右側に } を使用します) など。すべては個人の習慣によって異なります。
2. アトム
アトムは正規表現における最も基本的な単位であり、その機能とともに5つに分類します。
まず、a、B、c、1、2、_ などの最も一般的な文字をアトムとして使用できます。
'/9527/' '/misson failed/' '/PHP_VERSION/'
一部の特殊文字と メタ文字 (メタ文字) も使用できます。ただし、特別な意味を表す独自の記号と競合しない限り、正規表現は非常に複雑です。これは、これらの特別な意味を持つシンボルの助けにあります (特別な機能については後で説明します)。このシンボルを正規表現で特別な意味と一致させたい場合は、エスケープする必要があります。二重引用符で囲まれた文字列と同様に、二重引用符もエスケープする必要があるため、理解しやすいです。これらの特殊記号には、.、*、?、,'、"、,/ などが含まれます。実際、一重引用符と二重引用符は変換する必要がない場合があります。すべては、作成する式に一重引用符が使用されているかどうかによって異なります。エラーを避けるために、正規表現を記述するときは通常、一重引用符で囲んだ文字列を使用します。また、独自の区切り文字と競合しないようにしてください。そうしないと、システムが式が早期に終了したとみなしてエラーが発生します。 > 非印刷文字は、一般に空白文字、つまりスペース、水平タブ文字 t、垂直タブ文字 v、復帰文字 r、改行文字 n などを指します。バグはここに簡単に埋められます。エラーを減らすために PHP 一重引用符で囲まれた文字列を使用してください
'/a\.b\?c\+/' '/ab\/123/'
前面说过,可以匹配一般的单个字符,如9527,9后面接着一个5,再接着一个2,然后是7,现在匹配的并不限于这几个数字,比如我要匹配两个数字,只要是数字就行,对于这种带有通用性质的字符,正则也有表示某一类字符的的表示形式,比如表示数字用\d,表示一个0到9的十进制数字,而\D(大写形式)表示非十进制数字,\w表示匹配一个单词,在php的正则流派里边,单词的定义是大写字母A-Z,小写字母a-z,数字0-9以及下划线_,相应的\W大写形式表示相反的意义。
'/\d\d/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配两个数字</span> '/\D/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配一个除数字之外的任意字符</span> '/\w/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配一个词</span> '/\W/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配一个除了词的任意字符</span> '/\s/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配一个空白字符</span> '/\S/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配一个非空白字符</span>
3.元字符
元字符就是有特殊意义的字符,如*、+、?、. 、|、^、$等,一般不能单独出现,只有在修饰它前面的一个(或一些)原子时才表现出它自己的特殊含义,也就是说,它要配合上面说的原子使用才有意义。它有特殊意义,但就是要匹配它时,就得使用转义符\了,转为普通字符。
?:量词,有或者没有
现在要匹配一个单词意思颜色,有两种写法,colour或color,中间的u要么有1个,要么没有,元字符?就适合,而且,如果没有加括号限制,元字符量词这类元字符只对位于它前面一个原子起作用,这里?只作用于u
'/colou?r/'
+:量词,一个或多个
比如匹配一个或多个数字,注意元字符修饰的原子有可能是个序列,但未加括号限制只对一个有效
'/\d+/'
*:量词,0个、1个或多个,即任意数量
'/\d*/'
区间:规定重复出现次数
前面的次数限制毕竟比较死板,来个活的,区间用一对{}表示,{n}表示出现n次,{n,}表示大于或等于n次,{m,n}表示至少出现m次,至多出现n次,最好m小于n,别故意为难系统~
'/auth{0,1}/' <span style="color: #008000;">//</span><span style="color: #008000;"> 出现0次到1次,即?</span> '/auth{1,}/' <span style="color: #008000;">//</span><span style="color: #008000;"> 至少出现1次,即+</span> '/auth{3}/' <span style="color: #008000;">//</span><span style="color: #008000;"> h要出现3次</span>
匹配任意字符
.,点号,匹配任意字符,在php中,默认情况下除了换行符,它可以匹配任意任意一一个字符,另外一种情况下,它就真的匹配任意字符,包括换行符,后面再说。
多选结构
|,表示或,依靠它可以生成一个多选分支,需要注意的是|在正则中优先级最低,,下面的不是|作用的不是左右的r和c,而是|左右的子表达式,哪怕把一个表达式写成'/\d+\s*abc{2,5}|ack?\d/',它仍作用于它前面和后面的子表达式\d+\s*abc{2,5}和ack?\d,只要没有加括号限制
'/color|colour/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配color或colour</span>
字符组
匹配若干字符之一,现在要匹配abc中任一个字符,可以这样:[abc],包在一堆中括号里边,加入要匹配大写A到大写Z任一个字符,可以这样:[A-Z],中间一个连字符在字符组里边就成了有特殊意义的元字符表示从什么到什么,,其他还有数字[0-9],或者只是数字、字母的部分值:[2-5]、[c-h],有几个需要注意的地方:
1. 连字符-只有在字符组内部,且在两个字符之间才被认为是有效的元字符,出了字符组跟a一样是普通字符;
2. 如果字符组内部确实需要匹配-,最好将它放在字符组内最前面,如[-a-z],匹配小写字母或-,在php中也可以将它放在最后,如[c-k-](居然没报错-_-#),但放在最前面还是最保险;
3. 对于系统不认可的由-标识的顺序字符,对于php会报Compilation failed警告,如[a-9]、[9-0]
4. 字符组的顺序一般为从小到大,不能[9-0],大小写一般分开写[A-Za-z],但在php中,至少俺这个5.5版本可以大小写混合写[A-z],表示匹配从A到Z和a到z的字母,不提倡这样做,其他语言流派肯能不支持
'/C[EFIMT]O/' '/[A-Za-z0-9_]/' <span style="color: #008000;">//</span><span style="color: #008000;"> 相当于\w,表示单词</span> '/[a-z]+/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配一个或多个小写字母</span>
排除型字符组
前面是匹配任意一个[]内的字符,现在恰好相反,不匹配任意一个字符[]内的字符,如[^a-z]:不匹配任意一个小写字母,在字符组内最前面加^表示取反。一个有意思的例子:
'/abc[^ABC]/'<span style="color: #000000;"> 字符串1: </span>'abcK'<span style="color: #000000;"> 字符串2: </span>'abc' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配?</span>
字符串1很显然,字符串2匹配吗?注意字符组:它匹配未出现的字符。这里abc后面要跟一个除A、B或C的字符,但是不能没有,这便是字符组的坑。
单词边界
有时为了匹配一个完整单词,一般单词给人印象是左右带有空格,专门有一个表示单词的这种边界元字符\b,注意它并不匹配一个空格,而是一个位置,所以单独的'abc'也被看作一个单词,虽然它左右并没有空格,关于匹配位置后面再详说。但是正则中,至少目前对单词的定义还没那么强大,就是大小写字母、数字加下划线,跟单词边界相反的就是\B,只要不是单词边界都能匹配
'/\b\w+\b/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配一个单词</span> 'hello world' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配hello(只匹配一次的话)</span> 'abc' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配abc</span>
括号限定
括号的第一个强大功能,是限定元字符的作用范围,将表达式分成一个个子表达式,如'/col(ou)?r/',加了括号后,?元字符作用的对象是ou而不再只是u,'/abc|def/'指的是匹配abc或者def,但a(bc|de)f匹配的是abcf或者adef,类似编程语言括号运算符,括号内是一个小的单元,相当于一个大原子,当然它还有一个重要作用:分组,后面再说。
行的开头与结尾
每一行字符串都有一个开头和结尾,开头和结尾指一个位置,虽然我们在描述一串字符串会说以...结尾,但正则中不指具体字符,匹配的是位置。比如'/^a.+/',匹配以a开头的字符串,^表示开头,$表示结尾,下面是几个值得注意的例子
'/^/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配一个行开头,只要是一行字符串都有开头,哪怕空字符串,无实际意义</span> '/^$/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配一个行开头,紧接着是行结尾,即匹配一个空行</span> '/^hello$/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配以hello开头的字符串,随后改行便结尾,即匹配只有hello字符串的行,该行再无其他字符</span> '/^hello.*hello$/' <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配一个行开头,接着是hello,接着可能是若干其他字符,然后是hello,紧接着是行结尾,即匹配一个一hello开头,hello结尾,中间有若干字符的字符串</span>
注意在默认情况下,脱字符^和美元符号$表示匹配一个字符串的开头和结尾,比如"hello",结尾在紧挨着o的右边,那么"hello\nabc"(在php的双引号字符串中,\n是元序列,表示换行符,但php的单引号字符串\n只是普通字符,一个\一个n,特此说明)该看做一行还是两行呢?答案是,在preg中,默认情况下,仍被看做一行,它的结尾是紧挨着c的右边,可以验证下
<?<span style="color: #000000;">php </span><span style="color: #800080;">$pattern</span> = '/hello$/'; <span style="color: #008000;">//</span><span style="color: #008000;"> 以hello结尾</span> <span style="color: #800080;">$subject</span> = "hello\nabc"; <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配字符串的结尾,而不是逻辑行的结尾</span> <span style="color: #008080;">preg_match</span>(<span style="color: #800080;">$pattern</span>, <span style="color: #800080;">$subject</span>, <span style="color: #800080;">$match</span><span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> 'match=><pre class="brush:php;toolbar:false">'<span style="color: #000000;">; </span><span style="color: #008080;">var_dump</span>(<span style="color: #800080;">$match</span>); <span style="color: #008000;">//</span><span style="color: #008000;"> 无匹配内容</span>
我们把"abc\n"称为一个逻辑行,把"abc\ndef\n"称为有两个逻辑行,因为逻辑上它是有两行字符串的,但preg默认不匹配多个逻辑行(可能是坑),管你几个换行符就当一行字符串看,$直接匹配到这个字符串最后一个位置。那么php的正则能不能匹配多个逻辑行呢?当然行,要用到模式修饰符,后面再说。
除了以^表示行开头,$表示行结尾,在preg中,\A也表示开头,\z和\Z都表示结尾,不同的是\Z能匹配到最后的换行符,而\z不行。
3. 分组和捕获
一个简单的例子,匹配以单引号或双引号开头的字符串,结尾需要是对应的双引号或单引号(中间不存在转义过的双引号或单引号)
'/["\'].*["\']/'
如果是上面那样,第一个字符组匹配了双引号,是无法保证第二个字符组匹配相同的双引号,即现在的要求是,前面那个字符组匹配了什么,后面那个字符组也需要匹配相同的字符,不是给一个相同的正则表达式就行。
前面说过括号的第一大作用,限定某些元字符的作用范围,括号的另一大作用就是分组捕获,这是正则的特性。比如'/ab(cd)ef(gh)ij/',有两个括号,preg引擎会对每个括号中匹配到的文本进行记录,以左括号由左往右数,以数字编号,\1记录的是第一个左括号所在的里边的内容,\2记录第二个左括号所在的里边的内容,第n个左括号对应\n,preg最多可记录4096个(强大!),这里\1对应cd,\2对应gh。括号起到了分组的作用,而类似\1、\2...\n称为反向引用。注意:引用的是正则匹配到的文本,而不是引用的正则表达式。
<span style="color: #800080;">$pattern</span> = '/(\w)(\d)(.*)/'; <span style="color: #008000;">//</span><span style="color: #008000;"> 匹配一个词,一个十进制数字和若干任意字符</span> <span style="color: #800080;">$subject</span> = 'a57h'<span style="color: #000000;">; </span><span style="color: #008080;">preg_match</span>(<span style="color: #800080;">$pattern</span>, <span style="color: #800080;">$subject</span>, <span style="color: #800080;">$match</span><span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> 'match=><pre class="brush:php;toolbar:false">'<span style="color: #000000;">; </span><span style="color: #008080;">var_dump</span>(<span style="color: #800080;">$match</span>);
结果:
preg_match方法将捕获的文本放在$match参数中,数组索引的1、2、3对应的元素分别是捕获到的\1、\2、\3三个分组文本(索引0列出的是整个表达式匹配到的文本)。
对于括号嵌套的,只看左括号的相对顺序,比如'/(abc(def)g(hij))(k)/',有效的左括号为4个,\1对应abc的左括号整个括起来的内容(abcdefghij),也包括里边嵌套括号匹配的内容,\2对应def,\3对应hij,\4对应k。
所以对于上边引号对应的例子可以是这样的:'/(["\'].*)\1/',后面的字符要与第一个匹配到的文本一致(注意不是模式一致)。
但是新的变种又来了,有的想分组捕获,但有的只想分个组,限定下范围,不想捕获,?:来了,(?: ... ),在分组的括号最前面添加问号冒号,就表示取消文本捕获。因此对于'/(?:abc(de)fg(?:hij))/',\1捕获的是de,没有\2。
除了数字捕获(\后面跟数字来标识捕获内容),还可使用命名捕获,即给捕获到的文本取名字,用法(?P
<span style="max-width:90%">$pattern</span> = '/\w(?P<key1>\w\w)\s+(?P<key2>\d+)/'<span style="color: #000000;">; </span><span style="color: #800080;">$subject</span> = 'abcd 233'<span style="color: #000000;">; </span><span style="color: #008080;">preg_match</span>(<span style="color: #800080;">$pattern</span>, <span style="color: #800080;">$subject</span>, <span style="color: #800080;">$match</span><span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> 'match=><pre class="brush:php;toolbar:false">'<span style="color: #000000;">; </span><span style="color: #008080;">var_dump</span>(<span style="color: #800080;">$match</span>);
结果:
在php的命名捕获时,把原来数字形式的捕获也记录了,key1与\1对应,key2与\2对应,反正多一种不嫌多。
捕获的另一个巨大的用处,就是在替换操作时,对匹配的文本的引用,比如现在有表达式 '/123(\d\d\d)ok([a-z][a-z])end/',括号捕获到三个数字和两个小写字母,我们想提取出来,怎么办,就preg来说,使用简单的替换方法:preg_replace。一个例子
<span style="max-width:90%">$pattern</span> = '/123(\d\d\d)ok([a-z][a-z])end/'<span style="color: #000000;">; </span><span style="color: #800080;">$subject</span> = '123233okhiend'<span style="color: #000000;">; </span><span style="color: #800080;">$replacement</span> = '$1---$2'; <span style="color: #008000;">//</span><span style="color: #008000;"> 在替换字符串中,对捕获到文本的引用</span> <span style="color: #800080;">$ret</span> = <span style="color: #008080;">preg_replace</span>(<span style="color: #800080;">$pattern</span>, <span style="color: #800080;">$replacement</span>, <span style="color: #800080;">$subject</span><span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> 'ret=><pre class="brush:php;toolbar:false">'<span style="color: #000000;">; </span><span style="color: #008080;">var_dump</span>(<span style="color: #800080;">$ret</span>);
括号捕捉到了233和hi,如果是反向引用我们知道是\1和\2,替换的字符串$replacement中,使用$1、$2来捕捉它,注意\1、\2是用在原始的正则表达式中的,这里是替换字符串中,当然,替换操作的是字符串的副本。
结果:
如果用命名捕获来操作,使用对应的$name来引用,亲测貌似不行,估计是怕跟上下文中的变量相冲突,但是上面说了,在命名捕获时,数字索引仍然是有效的,所以在命名捕获时,我们仍然可以再替换字符串中通过$1来引用匹配到的文本。
一个需要注意的问题:假设我捕获到了第1组,替换文本是'$15',1后边又跟着个数字,容易出错,为提高解析效率,可使用{}将数字括起来,如'${1}5',另,这对命名捕获仍无效。
4. 环视
前面的行开头、结尾是对位置的匹配,环视( lookaround)也是对位置的匹配,对于这种位置匹配,可能一个更熟悉的名字是:零宽断言。^、$、\A、\Z、\b、\B都算零宽断言,包括环视结构,当然行开头和结尾也多称为锚点。比如俺曾做的一个小项目中,有这样的需求,使用一个别人搭建的框架,每一个model类处理一张表,表名根据model类的类名确定,它的类名一般是这样的class externalLinks extends Model{...},子类名是这样externalLinks,它的表名是gw_external_links,gw_不说,从externalLinks到external_links需要在左边是小写字母,右边是大写字母的地方插一个_,再转小写,而且类名不一定只有这样的,比如abcDefGhi,可能是3个大小写单词,个数不确定,用字符串的替换显得比较麻烦,因为个数不确定,用正则轻而易举就解决了。凡是这种某地方的左边是啥,右边是啥,明显的位置要求的,都可以用环视试一下。环视分为四种:
肯定顺序环视:(?=...),匹配某个位置,它的右边是...
肯定逆序环视:(?<=...),匹配某个位置,它的左边是...
否定顺序环视:(?!...),匹配某个位置,它的右边不是...
否定逆序环视:(?
不看...就是环视的符号定义,顺序、逆序的区别在于一个从左往右看,一个从右往左看。环视在位置检测时非常有用。例如现在要匹配abc,它的右边必须是数字,可以这样
'/abc(?=\d)/'
说说俺自己上面那个问题,找到左边是小写字母,右边是大写字母的位置,可以这样
'/(?=[A-Z])(?<=[a-z])/'
如果左右两边都需要检测的,像这种,顺序环视跟逆序环视的左右顺序的顺序不用管,哪个放左边哪个放右边都行(等价于'/(?<=[a-z])(?=[A-Z])/')。一般来说逆序环视放左边,顺序环视放右边,一个好习惯是以位置为中心,想象这个位置左边应该有什么,右边应该有什么。上面的模式找到位置了用_替换(实际是插入)掉问题基本就解决了。再比如
<span style="max-width:90%">$pattern</span> = '/\w+(?=\d)/'<span style="color: #000000;">; </span><span style="color: #800080;">$subject</span> = 'abcd 233'<span style="color: #000000;">; </span><span style="color: #008080;">preg_match</span>(<span style="color: #800080;">$pattern</span>, <span style="color: #800080;">$subject</span>, <span style="color: #800080;">$match</span><span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> 'match=><pre class="brush:php;toolbar:false">'<span style="color: #000000;">; </span><span style="color: #008080;">var_dump</span>(<span style="color: #800080;">$match</span>);
结果:
想想为啥?匹配一个或多个词,它的右边必须是数字,只有'23'符合,它的右边恰好为数字,2的前面是空格,不属于\w范围,而'233'的后面啥都没有。
注意:只匹配位置,不匹配实际文本,这个有时在多次匹配时有很大区别。
对于单词边界符\b,也可用环视写出来,单词的开始边界是左边非单词右边为单词:(?
5. 占有优先 vs 忽略优先
标准量词都是匹配优先的,如*、+、?以及区间量词{m,n},它们总是在自己的范围内尽可能多的匹配字符,对于{m,n}(m 结果: 上例在匹配单词时总是尽可能匹配最多,所以单看\w+会一直匹配到字符串结尾3处,但随后的环视检查出现右边不是一个数字(什么都没有),于是交还一个词,再看看右边是不是数字,这下是了(hello12)然后就匹配成功返回,这里有一个回溯的过程(下一篇写写),不细纠结,有的书称这种匹配优先为贪婪模式,即标准量词默认都是贪婪的,尽可能多的去匹配的,那么与之对应的就有非贪婪模式。 在量词后面加个问好,表示非贪婪模式,即忽略优先,如*?、+?、??、{m,n}?,非贪婪模式下的量词总是尽可能匹配少的字符,还是上面那个例子,如果把$pattern改为下面呢? 匹配的结果是'hello',过程是首先+?使得\w每匹配一个词就检查下右边是不是数字,是的话匹配就此停止,返回结果,要保证匹配到的字符最好嘛,不是的话继续匹配,直到右边是数字便立即停止返回。忽略优先可以提前返回结果,当然可能不是最优的,提高匹配速度(下一篇再总结下)。 有木有倔强的不交还的列?当然有,这就是占有优先,它在标准量词后面加一个+,如*+、++、?+、{m,n}+,把上面的例子再改改,有意向不到的结果-_-# 记住占有优先的特点:不交还!\w+一直匹配到hello123结尾,发现之后啥都没有,失望至极,然后停止匹配,报告匹配失败了,没错这次居然是匹配失败了!占有优先仍属于匹配优先,尽可能多的去匹配,只是匹配到最后如果发现某条件不符,不好意思,他不服,直接停止。所以占有优先可以提前报告失败,提高匹配速度。 忽略下占有优先的脾气,来看一个新东西:固化分组。表示方式:(?>...)(它应该属于分组捕获的,俺觉得放这儿更好),同占有优先功能一样,它也是优先匹配,并且不交还。所以再把上面的$pattern改为这个样子,也是匹配失败滴。 6. 模式修饰符 模式修饰符(pattern modifier),放在模式的最后面,结束定界符之后,如'/....../imx',i、m、x表示不同的修饰符,可以一次使用多个。模式修饰符是对正则表达式整体效果起一个调节作用。 i:匹配时不区分大小写,如'/abc/i',对匹配到的abc任一个大小写都可; m:将字符串视为多个逻辑行,还记得前面说匹配一行字符串的开头^和结尾$吗?默认preg将字符串视为一行,哪怕里边有有效的换行符,如果加了修饰符m,将带匹配字符串按照换行符分行,这些行称为逻辑行,^匹配每一个逻辑行的开头,$匹配每一个逻辑行的结尾; s:还记得前面说的匹配任一字符的元字符 . (点号)吗?preg默认情况下点号匹配任一个字符,除了换行符,那么加了s修饰符,点号就连换行符也不拒绝了; x:这个修饰符对于复杂的正则表达式非常有用,因为它可以在正则中添加注释!!!而且它会忽略正则中的空白符,当然,也包括换行,为了说明乱举一例: e:它在特定的地方有用,即preg_replace方法中,preg的preg_replace方法原型如下 第二个参数$replacement,会将匹配到文本替换为它,使用e修饰符,$replacement不仅可以用简单的文本,不仅可以对捕获到的文本进行$1式的引用,更流弊的是,可以写php代码,以字符串的形式(有点eval的感觉),例 しかし、結果を見ようとしたときに、エラーが発生しました: /e 修飾子は非推奨です。代わりに preg_replace_callback を使用してください、ナニ!パターン シンボル e は非推奨になりました。preg_replace_callback メソッドを使用して、一致した結果を処理します。方法については、マニュアルを参照してください。それを使ってください。 D: この修飾子が設定されている場合、パターン内の $ は文字列の末尾 (EOS、文字列の終わり) にのみ一致し、EOS より前の改行文字には一致しません。つまり、とみなされません。複数行として。ただし、m 修飾子が設定されている場合、このオプションは無視されます。 U: * と *? の意味が逆になり、元の一致優先度が無視優先度になり、元の無視優先度が一致優先度になります。人々をさらにめまいさせる以外に、それは何の役にも立たないようです。 上記には、正規表現の再帰、地獄、忘れてくださいなど、強力なものもいくつかあります。 もう一度見るのを忘れました>>>エスケープ <span style="max-width:90%">$pattern</span> = '/\w+(?=\d)/'<span style="color: #000000;">; </span><span style="color: #800080;">$subject</span> = 'hello123'<span style="color: #000000;">; </span><span style="color: #008080;">preg_match</span>(<span style="color: #800080;">$pattern</span>, <span style="color: #800080;">$subject</span>, <span style="color: #800080;">$match</span><span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> 'match=><pre class="brush:php;toolbar:false">'<span style="color: #000000;">; </span><span style="color: #008080;">var_dump</span>(<span style="color: #800080;">$match</span>);
<span style="max-width:90%">$pattern</span> = '/\w+?(?=\d)/'; <span style="color: #008000;">//</span><span style="color: #008000;"> 忽略优先</span>
<span style="color: #800080;">$pattern</span> = '/\w++(?=\d)/'; <span style="color: #008000;">//</span><span style="color: #008000;"> 占有优先</span>
<span style="color: #800080;">$pattern</span> = '/(?>\w+)(?=\d)/'; <span style="color: #008000;">//</span><span style="color: #008000;"> 固化分组</span>
<span style="color: #008000;">//</span><span style="color: #008000;"> 使用x修饰符</span> <span style="color: #800080;">$pattern</span> = '<span style="color: #000000;">/(\w+ \d) (?: # 这里匹配啥啥啥 "\w" | "\d" | # 这里应当这么理解那啥 [-A-Z]+ # 这里你自己猜 )/x</span>'<span style="color: #000000;">; </span><span style="color: #800080;">$subject</span> = 'wcwieu2832z28'<span style="color: #000000;">; </span><span style="color: #008080;">preg_match</span>(<span style="color: #800080;">$pattern</span>, <span style="color: #800080;">$subject</span>, <span style="color: #800080;">$match</span>);
<span style="color: #0000ff;">mixed</span> <span style="color: #008080;">preg_replace</span>(<span style="color: #0000ff;">mixed</span> <span style="color: #800080;">$pattern</span>, <span style="color: #0000ff;">mixed</span> <span style="color: #800080;">$replacement</span>, <span style="color: #0000ff;">mixed</span> <span style="color: #800080;">$subject</span> [, int <span style="color: #800080;">$limit</span> = -1 [,int &<span style="color: #800080;">$count</span> ]])
<span style="color: #800080;">$ret</span> = <span style="color: #008080;">preg_replace</span>('/\d([A-Z]+)/e', 'strtolower("$1")', '5BBC'<span style="color: #000000;">); </span><span style="color: #0000ff;">echo</span> 'ret=><pre class="brush:php;toolbar:false">'<span style="color: #000000;">; </span><span style="color: #008080;">var_dump</span>(<span style="color: #800080;">$ret</span>);