首页 > php教程 > php手册 > 正文

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

WBOY
发布: 2016-06-06 19:49:44
原创
1055 人浏览过

网友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;

1
2
3
4
5
6
7
8

    color:red;


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

典型的错误答案<span><span><item></span><span>.*?</span><span>(</span><span>?!</span>color<span>)</span><span>.*?</</span>item<span>></span></span> <span><item>...</item></span>

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

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>

<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,可以用

,如果

里没有good则条件为真,否则为假。

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

对于文章开始提出的问题而言,当然可以这样解决:先搜索所有的

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></span>.*?item>!sg;
foreach $item (@result)
{
    if ($item !~ /color/)
    {
        print "$item";
    }
}

,然后分别判断是否存在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></span>.*?item>!sg;
foreach $item (@result)
{
    if ($item !~ /color/)
    {
        print "$item";
    }
}

输出结果是:

表>

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

排除型匹配

不好意思,“排除型匹配”这个词是我生造的。其它的说法或许是“否定断言”,“否定环视”等等。后两者的命名,都是从匹配过程的角度出发;而此处命名,是从结果出发。具体说来,就是使用 <code><span><span>(</span><span>?!...</span><span>)</span></span><span><span>(</span><span>?<!...</span><span>)</span></span>(

?!...)<code><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>这两个东东的使用方法类似,都是指,当前位置

不出现
某种模式。不同的是,<h3></h3>(<blockquote> <p>?!...<strong></strong>)</p> <p></p> </blockquote>是指当前位置的右边,而<strong></strong>(<code><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>
自然就是指左边了。
  • 这里隆重推出Anrs同学翻译的教程: 环视一以及环视二。仔细阅读这两文章,彻底明白环视这两个概念,将会提升您的正则表达式功力。后文将建立在您已经理解环视这个概念的基础上。
  • 闲话一句。既然使用“左边”和“右边”既形象又好懂,为什么没见过“左瞻”,“右瞻”,“左向”,“右向”,反而全是些“前瞻后瞻”,“正向逆向”这样的不好理解的说法呢?撕烤者也同有此问。我的理解是,或许是为了照顾阿语等从右向左书写的用户的习惯吧。无论如何,将从 <code><span><span>.</span></span>
  • ^
  • <span><span>(</span><span>?!</span>com<span>)</span></span><code><span>fanfou.</span>$
  • 的方向称之为“向前”总不会错。
  • <span><span>[</span>a<span>-</span>z<span>]</span><span>{</span><span>2</span><span>,</span><span>4</span><span>}</span></span>描述当前位置(左侧或右侧)的模式,从而辅助判断正则式是否匹配,是环视的作用。它只描述,不消耗字符;只辅助判断,从不单独出现。这与
  • ^
  • $简直如出一辙。

    一则例子

    例子. 现在有许多与fanfou.com类似的网址。如何写一条正则表达式,来匹配域名含fanfou,但是TLS不是.com的模式? 答案:/bfanfou.(?!com)[a-z]{2,4}b/i。分析这条正则表达式: 以b开始,明确字符边界;fanfou主域名不可少;.匹配一个普通的点号;此处不要使用点号元字符;(?!com)表示此处(即从fanfou.的右边)不得出现com三个连续字符;[a-z]{2,4}表示是2至4位的拉丁字母;因为域名的TLS最短是2位(如.au, .us),最长可为4位(如.info, .asia);右侧边界同样重要,否则我们之前的{2,4}就白费了;使用i表示不分大小写;这是域名的特征之一。 回到本题 按照要求,一步步建立这条正则式。
    • 该正则式匹配的是<span><item>...</item></span>结构。因此,正则式以<span><span><item></span></span>开始。
    • <span><item></span><span></item></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></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></span>(?:(?!color).)+?item>

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

    exclude, lookaround, negate, perl

    原文来自:http://iregex.org/blog/negate-match.html

    1
    2
    3
    4
    5


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

    1
    2
    3
    4
    5

        尺寸:12;
        数量:45;
        类型:好;
    来源:php.cn
    本站声明
    本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
    热门推荐
    热门教程
    更多>
    最新下载
    更多>
    网站特效
    网站源码
    网站素材
    前端模板
    关于我们 免责声明 Sitemap
    PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!