Home  >  Article  >  php教程  >  PHP 正则 如何匹配不出现某段字符串的写法!(保留备份)

PHP 正则 如何匹配不出现某段字符串的写法!(保留备份)

WBOY
WBOYOriginal
2016-06-06 19:49:441088browse

网友cfc4n问及关于(?!)的正则表达式问题。回答之后,顺便总结了一下Perl语言中如何匹配“不出现”某元素,贴在这里。 问题 问题描述 有如下文本,如何使用正则式,将其中 不含color选项的item 匹配出来? 1 2 3 4 5 6 7 8 item color:red; /item item size:1

网友cfc4n问及关于(?!)的正则表达式问题。回答之后,顺便总结了一下Perl语言中如何匹配“不出现”某元素,贴在这里。

问题

问题描述

有如下文本,如何使用正则式,将其中不含color选项的item匹配出来?

1
2
3
4
5
6
7
8


    color:red;


    size:12;
    number:45;
    type:good;

典型的错误答案

新手容易提供这样的错误答案:<span><span><item></item></span><span>.*?</span><span>(</span><span>?!</span>color<span>)</span><span>.*?</span>item<span>></span></span>。其出发点是正确的:只有当color不出现在目标字串时,该匹配才是所需要的。事实上,这样的正则表达式不能如君所愿,它匹配所有的<span><item>...</item></span>。这是为什么呢?

Perl之排除型匹配

最简单的排除型匹配

匹配是<span><span>=~</span></span>, 不匹配当然是 <span><span>!~</span></span> 了。写到这里想到,在正则式中,凡是由<span><span>=</span></span>组成的正则式符号,全可以使用<span><span>!</span></span>来替代,以表现相反的意思。例如<span><span>(</span><span>?=</span><span>)</span></span><span><span>(</span><span>?!</span><span>)</span></span><span><span>(</span><span>?<span>)</span></span></span><span><span>(</span><span>?<span>)</span></span></span><span><span>=~</span></span><span><span>!~</span></span>

返回正题,看个例子。如果要检测某字串是否含有good,当然要用<span><span>if</span> <span>(</span><span>$string</span> <span>=~</span> <span>/good/</span><span>)</span></span>,如果<span><span>$string</span></span>里有good则条件为真,否则为假;

如果要检测某字串是否含有good,可以用<span><span>if</span> <span>(</span><span>$string</span> <span>!~</span> <span>/good/</span><span>)</span></span>,如果<span><span>$string</span></span>里没有good则条件为真,否则为假。

这种匹配测试,较适合于在大段的字串中搜索某个简单的模式,然后对于匹配的结果作出两种不同的判断,非此即彼。虽然迅速干练,但是对于复杂情况的判断,还是有些累赘。

对于文章开始提出的问题而言,当然可以这样解决:先搜索所有的 <span><item>...</item></span>,然后分别判断是否存在color项即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

#!/usr/bin/perl -w

my $text=
    color:red;


    size:12;
    number:45;
    type:good;

END


my @result = $text=~ m!<span><item></item></span>.*?item>!sg;
foreach $item (@result)
{
    if ($item !~ /color/)
    {
        print "$item";
    }
}

输出结果是:

1
2
3
4
5


    size:12;
    number:45;
    type:good;

虽然也不错,但是它总是“宁可错杀不可错放”地找完所有可能项,再一一重新进行排除。能否一开始就先界定,我们要找的是不含color的item呢?排除型匹配正是为此而生。

排除型匹配

不好意思,“排除型匹配”这个词是我生造的。其它的说法或许是“否定断言”,“否定环视”等等。后两者的命名,都是从匹配过程的角度出发;而此处命名,是从结果出发。具体说来,就是使用 <span><span>(</span><span>?!...</span><span>)</span></span><span><span>(</span><span>?<span>)</span></span></span>作为辅助条件判断,来简化正则表达式,方便快捷地找到符合要求的匹配。

这两个东东的使用方法类似,都是指,当前位置不出现某种模式。不同的是,<span><span>(</span><span>?!...</span><span>)</span></span>是指当前位置的右边,而<span><span>(</span><span>?<span>)</span></span></span>自然就是指左边了。

这里隆重推出Anrs同学翻译的教程: 环视一以及环视二。仔细阅读这两文章,彻底明白环视这两个概念,将会提升您的正则表达式功力。后文将建立在您已经理解环视这个概念的基础上。

闲话一句。既然使用“左边”和“右边”既形象又好懂,为什么没见过“左瞻”,“右瞻”,“左向”,“右向”,反而全是些“前瞻后瞻”,“正向逆向”这样的不好理解的说法呢?撕烤者也同有此问。我的理解是,或许是为了照顾阿语等从右向左书写的用户的习惯吧。无论如何,将从 <span><span>^</span></span><span>$</span>的方向称之为“向前”总不会错。

描述当前位置(左侧或右侧)的模式,从而辅助判断正则式是否匹配,是环视的作用。它只描述,不消耗字符;只辅助判断,从不单独出现。这与<span><span>^</span></span><span>$</span>简直如出一辙。

一则例子

例子. 现在有许多与fanfou.com类似的网址。如何写一条正则表达式,来匹配域名含fanfou,但是TLS不是.com的模式?

答案:<span><span>/</span><span>\bfanfou</span>\<span>.</span><span>(</span><span>?!</span>com<span>)</span><span>[</span>a<span>-</span>z<span>]</span><span>{</span><span>2</span><span>,</span><span>4</span><span>}</span><span>\b</span><span>/</span>i</span>。分析这条正则表达式:

  • <span><span>\b</span></span>开始,明确字符边界;
  • fanfou主域名不可少;
  • <span>\<span>.</span></span>匹配一个普通的点号;此处不要使用点号元字符;
  • <span><span>(</span><span>?!</span>com<span>)</span></span>表示此处(即从<span>fanfou.</span>的右边)不得出现com三个连续字符;
  • <span><span>[</span>a<span>-</span>z<span>]</span><span>{</span><span>2</span><span>,</span><span>4</span><span>}</span></span>表示是2至4位的拉丁字母;因为域名的TLS最短是2位(如.au, .us),最长可为4位(如.info, .asia);
  • 右侧边界同样重要,否则我们之前的{2,4}就白费了;
  • 使用i表示不分大小写;这是域名的特征之一。

回到本题

按照要求,一步步建立这条正则式。

  • 该正则式匹配的是<span><item>...</item></span>结构。因此,正则式以<span><span><item></item></span></span>开始。
  • <span><item></item></span><span></span>之间不得出现color,是这条正则式的难点。因为,<span>color</span>可能位于这个结构之内的任意一点,因此要规定,此内任意一点都不得出现color一词。这样的点为:<span><span>(</span><span>?!</span>color<span>)</span><span>.</span></span>。这样的点重复1+次,正则式写为<span><span>(</span><span>(</span><span>?!</span>color<span>)</span><span>.</span><span>)</span><span>+</span></span>。注意这里有个小陷阱:不要写为<span><span>(</span><span>?!</span>color<span>)</span><span>.+</span></span>,否则它只描述了最左侧的一点不得出现color,其余部分则都无所谓。而写为<span><span>(</span><span>(</span><span>?!</span>color<span>)</span><span>.</span><span>)</span><span>+</span></span>则保证每一点都不出现color。
  • 正则式此时为<span><item></item></span>((?!color).)+?item>。为了节省资源,括号通常写成非捕获模式<span><span>(</span><span>?:...</span><span>)</span></span>;为了保证点号匹配换行符,可以指定s模式或使用<span><span>[</span>\<span>s</span><span>\S</span><span>]</span></span>代替点号元字符。此处仍使用点号。正则式修改为<span><item></item></span>(?:(?!color).)+?item>

总体来说,环视相对于基本的元字符还是要抽象一些。不过一旦理解并掌握了它,就会发现它在精确匹配和替换时十分有用。上面的分析,希望有所帮助。如果您有类似的问题,欢迎提出。

exclude, lookaround, negate, perl

原文来自:http://iregex.org/blog/negate-match.html
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn