`
deepfuture
  • 浏览: 4411125 次
  • 性别: Icon_minigender_1
  • 来自: 湛江
博客专栏
073ec2a9-85b7-3ebf-a3bb-c6361e6c6f64
SQLite源码剖析
浏览量:80123
1591c4b8-62f1-3d3e-9551-25c77465da96
WIN32汇编语言学习应用...
浏览量:70323
F5390db6-59dd-338f-ba18-4e93943ff06a
神奇的perl
浏览量:103573
Dac44363-8a80-3836-99aa-f7b7780fa6e2
lucene等搜索引擎解析...
浏览量:286521
Ec49a563-4109-3c69-9c83-8f6d068ba113
深入lucene3.5源码...
浏览量:15039
9b99bfc2-19c2-3346-9100-7f8879c731ce
VB.NET并行与分布式编...
浏览量:67776
B1db2af3-06b3-35bb-ac08-59ff2d1324b4
silverlight 5...
浏览量:32280
4a56b548-ab3d-35af-a984-e0781d142c23
算法下午茶系列
浏览量:46072
社区版块
存档分类
最新评论

perl-环视匹配

 
阅读更多

1 环视基础

环视只进行子表达式的匹配,不占有字符,匹配到的内容不保存到最终的匹配结果,是零宽度的。环视匹配的最终结果就是一个位置。

环视的作用相当于对所在位置加了一个附加条件,只有满足这个条件,环视子表达式才能匹配成功。

环视按照方向划分有顺序和逆序两种,按照是否匹配有肯定和否定两种,组合起来就有四种环视。顺序环视相当于在当前位置右侧附加一个条件,而逆序环视相当于在当前位置左侧附加一个条件。

表达式

说明

(?<=Expression)

逆序肯定环视,表示所在位置左侧能够匹配Expression

(?<!Expression)

逆序否定环视,表示所在位置左侧不能匹配Expression

(?=Expression)

顺序肯定环视,表示所在位置右侧能够匹配Expression

(?!Expression)

顺序否定环视,表示所在位置右侧不能匹配Expression

对于环视的叫法,有的文档里叫预搜索,有的叫什么什么断言的,这里使用了更多人容易接受的《精通正则表达式》中“环视”的叫法,其实叫什么无所谓,只要知道是什么作用就是了,就这么几个语法规则, 还是很容易记的

2 环视匹配原理

环视是正则中的一个难点,对于环视的理解,可以从应用和原理两个角度理解,如果想理解得更清晰、深入一些,还是从原理的角度理解好一些,正则匹配基本原理参考 NFA引擎匹配原理。

上面提到环视相当于对“所在位置”附加了一个条件,环视的难点在于找到这个“位置”,这一点解决了,环视也就没什么秘密可言了。

顺序环视匹配过程

对于顺序肯定环视(?=Expression)来说,当子表达式Expression匹配成功时,(?=Expression)匹配成功,并报告(?=Expression)匹配当前位置成功。

对于顺序否定环视(?!Expression)来说,当子表达式Expression匹配成功时,(?!Expression)匹配失败;当子表达式Expression匹配失败时,(?!Expression)匹配成功,并报告(?!Expression)匹配当前位置成功;

顺序肯定环视的例子已在NFA引擎匹配原理中讲解过了,这里再讲解一下顺序否定环视。

源字符串:aa<p>one</p>bb<div>two</div>cc

正则表达式:<(?!/?p\b)[^>]+>

这个正则的意义就是匹配除<p…>或</p>之外的其余标签。

匹配过程:

首先由字符“<”取得控制权,从位置0开始匹配,由于“<”匹配“a”失败,在位置0处整个表达式匹配失败,第一次迭代匹配失败,正则引擎向前传动,由位置1处开始尝试第二次迭代匹配。

重复以上过程,直到位置2,“<”匹配“<”成功,控制权交给“(?!/?p\b)”;“(?!/?p\b)”子表达式取得控制权后,进行内部子表达式的匹配。首先由“/?”取得控制权,尝试匹配“p”失败,进行回溯,不匹配,控制权交给“p”;由“p”来尝试匹配“p”,匹配成功,控制权交给“\b”;由“\b”来尝试匹配位置4,匹配成功。此时子表达式匹配完成,“/?p\b”匹配成功,那么环视表达式“(?!/?p\b)”就匹配失败。在位置2处整个表达式匹配失败,新一轮迭代匹配失败,正则引擎向前传动,由位置3处开始尝试下一轮迭代匹配。

在位置8处也会遇到一轮“/?p\b”匹配“/p”成功,而导致环视表达式“(?!/?p\b)”匹配失败,从而导致整个表达式匹配失败的过程。

重复以上过程,直到位置14,“<”匹配“<”成功,控制权交给“(?!/?p\b)”;“/?”尝试匹配“d”失败,进行回溯,不匹配,控制权交给“p”;由“p”来尝试匹配“d”,匹配失败,已经没有备选状态可供回溯,匹配失败。此时子表达式匹配完成,“/?p\b”匹配失败,那么环视表达式“(?!/?p\b)”就匹配成功。匹配的结果是位置15,然后控制权交给“[^>]+”;由“[^>]+”从位置15进行尝试匹配,可以成功匹配到“div”,控制权交给“>”;由“>”来匹配“>”。

此时正则表达式匹配完成,报告匹配成功。匹配结果为“<div>”,开始位置为14,结束位置为19。其中“<”匹配“<”,“(?!/?p\b)”匹配位置15,“[^>]+”匹配字符串“div”,“>”匹配“>”。

逆序环视基础

对于逆序肯定环视(?<=Expression)来说,当子表达式Expression匹配成功时,(?<=Expression)匹配成功,并报告(?<=Expression)匹配当前位置成功。

对于逆序否定环视(?<!Expression)来说,当子表达式Expression匹配成功时,(?<!Expression)匹配失败;当子表达式Expression匹配失败时,(?<!Expression)匹配成功,并报告(?<!Expression)匹配当前位置成功;

顺序环视相当于在当前位置右侧附加一个条件,所以它的匹配尝试是从当前位置开始的,然后向右尝试匹配,直到某一位置使得匹配成功或失败为止。而逆序环视的特殊处在于,它相当于在当前位置左侧附加一个条件,所以它不是在当前位置开始尝试匹配的,而是从当前位置左侧某一位置开始,匹配到当前位置为止,报告匹配成功或失败。

顺序环视尝试匹配的起点是确定的,就是当前位置,而匹配的终点是不确定的。逆序环视匹配的起点是不确定的,是当前位置左侧某一位置,而匹配的终点是确定的,就是当前位置。

所以顺序环视相对是简单的,而逆序环视相对是复杂的。这也就是为什么大多数语言和工具都提供了对顺序环视的支持,而只有少数语言提供了对逆序环视支持的原因。

JavaScript中只支持顺序环视,不支持逆序环视。

Java中虽然顺序环视和逆序环视都支持,但是逆序环视只支持长度确定的表达式,逆序环视中量词只支持“?”,不支持其它长度不定的量词。长度确定时,引擎可以向左查找固定长度的位置作为起点开始尝试匹配,而如果长度不确定时,就要从位置0开始尝试匹配,处理的复杂度是显而易见的。

目前只有.NET中支持不确定长度的逆序环视。

逆序环视匹配过程

源字符串:<div>a test</div>

正则表达式:(?<=<div>)[^<]+(?=</div>)

这个正则的意义就是匹配<div>和</div>标签之间的内容,而不包括<div>和</div>标签本身。

匹配过程:

首先由“(?<=<div>)”取得控制权,从位置0开始匹配,由于位置0是起始位置,左侧没有任何内容,所以“<div>”必然匹配失败,从而环视表达式“(?<=<div>)”匹配失败,导致整个表达式在位置0处匹配失败。第一轮迭代匹配失败,正则引擎向前传动,由位置1处开始尝试第二次迭代匹配。

直到传动到位置5,“(?<=<div>)”取得控制权,向左查找5个位置,由位置0开始匹配,由“<div>”匹配“<div>”成功,从而“(?<=<div>)”匹配成功,匹配的结果为位置5,控制权交给“[^<]+”;“[^<]+”从位置5开始尝试匹配,匹配“a test”成功,控制权交给“(?=</div>)”;由“</div>”匹配“</div>”成功,从而“(?=</div>)”匹配成功,匹配结果为位置11。

此时正则表达式匹配完成,报告匹配成功。匹配结果为“a test”,开始位置为5,结束位置为11。其中“(?<=<div>)”匹配位置5,“[^<]+”匹配“a test”,“(?=</div>)”匹配位置11。

逆序否定环视的匹配过程与上述过程类似,区别只是当Expression匹配失败时,逆序否定表达式(?<!Expression)才匹配成功。

到此环视的匹配原理已基本讲解完,环视也就没有什么秘密可言了,所需要的,也只是多加练习而已。

3 环视应用

需求:数字格式化成用“,”的货币格式。

正则表达式:(?<=\d)(?<!\.\d*)(?=(?:\d{3})+(?:\.\d+|$))

测试代码:

  1. double[] data = new double[] { 0121231234123451234561234567123456789123456789012.345123.4561234.5612345.6789123456.7891234567.8912345678.9 };
  2. foreach (double d in data)
  3. {
  4. richTextBox2.Text += "源字符串:" + d.ToString().PadRight(15) + "格式化:" + Regex.Replace(d.ToString()@"(?<=\d)(?<!\.\d*)(?=(?:\d{3})+(?:\.\d+|$))"",") + "\n";
  5. }

输出结果:

源字符串:0 格式化:0

源字符串:12 格式化:12

源字符串:123 格式化:123

源字符串:1234 格式化:1,234

源字符串:12345 格式化:12,345

源字符串:123456 格式化:123,456

源字符串:1234567 格式化:1,234,567

源字符串:123456789 格式化:123,456,789

源字符串:1234567890 格式化:1,234,567,890

源字符串:12.345 格式化:12.345

源字符串:123.456 格式化:123.456

源字符串:1234.56 格式化:1,234.56

源字符串:12345.6789 格式化:12,345.6789

源字符串:123456.789 格式化:123,456.789

源字符串:1234567.89 格式化:1,234,567.89

源字符串:12345678.9 格式化:12,345,678.9

实现分析:

首先根据需求可以确定是把一些特定的位置替换为“,”,接下来就是分析并找到这些位置的规律,并抽象出来以正则表达式来表示。

1、 这个位置的左侧必须为数字

2、 这个位置右侧到出现“.”或结尾为止,必须是数字,且数字的个数必须为3的倍数

3、 这个位置左侧相隔任意个数字不能出现“.”

由以上三条,就可以完全确定这些位置,只要实现以上三条,组合一下正则表达式就可以了。

根据分析,最终匹配的结果是一个位置,所以所有子表达式都要求是零宽度。

1、 是对当前所在位置左侧附加的条件,所以要用到逆序环视,因为要求必须出现,所以是肯定的,符合这一条件的子表达式即为“(?<=\d)

2、 是对当前所在位置右侧附加的条件,所以要用到顺序环视,也是要求出现,所以是肯定的,是数字,且个数为3的倍数,即“(?=(?:\d{3})*)”,到出现“.”或结尾为止,即“(?=(?:\d{3})*(?:\.|$))

3、 是对当前所在位置左侧附加的条件,所以要用到逆序环视,因为要求不能出现,所以是否定的,即“(?<!\.\d*)

因为零宽度的子表达式是非互斥的,最后匹配的都是同一个位置,所以先后顺序是不影响最后的匹配结果的,可以任意组合,只是习惯上把逆序环视写在左侧,顺序环视写在右侧。

 

$x='154234231';
$x=~s/(?<=\d)(?=(?:\d\d\d)+$)/,/g;
print $x;

 154,234,231PS F:\pro>

分享到:
评论

相关推荐

    grep、sed、awk、perl等对正则表达式的支持的差别

    - Perl的正则表达式最为强大,支持多种高级特性,如非贪婪匹配、捕获组、环视等。 在实际使用中,选择哪个工具主要取决于需求的复杂度和效率。对于简单的搜索任务,grep可能就足够了;如果需要进行行内的替换或修改...

    PERL编程24学时教程

    3. **字符串操作** - 掌握字符串的拼接、查找、替换等基本操作,以及正则表达式的使用,它是Perl中强大的文本匹配工具。 4. **流程控制** - 学习条件语句(if/else,switch/case)和循环语句(for、while、until)...

    正则应用之——逆序环视探索.rar

    在Python、Java、Perl等语言中,逆序环视是完全支持的。 总的来说,逆序环视是正则表达式中的一个强大工具,能够帮助我们更精确地定位和提取字符串中的信息。通过理解和熟练运用逆序环视,我们可以编写出更加高效且...

    PCRE:PERL 5 正则表达式模式匹配-开源

    这个算法基于Perl的规则,支持多种高级构造,如前瞻和后瞻断言、递归模式、条件分支、子模式引用以及环视等。这些构造使得正则表达式能够处理复杂的模式匹配任务,例如识别嵌套结构、匹配平衡括号等。 PCRE库的...

    关于正则表达式的学习实战资料

    - 不同编程语言可能使用不同的正则表达式引擎,如Perl兼容(PCRE)或JavaScript引擎,这可能导致某些特性或行为的差异。 13. **使用正则表达式库** - 在编程中,通常使用内置的正则表达式库,如Python的`re`模块...

    DEELX 是一个在 C++ 环境下的与 Perl 兼容的正则表达式引擎

    6. **后向引用**:在环视断言中使用 `\g{N}` 实现后向引用,匹配与之前第N个捕获组相同的内容。 DEELX V1.2 版本可能包含了对库的性能优化、新的功能特性和错误修复。使用DEELX 的开发者可以通过`testDeelx.zip`中...

    pcre[1].tar.gz_pcre_pcre-6._正则表达式

    2. 强大的匹配机制:PCRE支持多种匹配模式,包括贪婪与非贪婪匹配、分支预测、回溯、环视等高级特性,能应对复杂的文本匹配需求。 3. Unicode支持:除了基本的ASCII字符集,PCRE还支持Unicode字符集,允许在正则...

    JAVA 正则表达式总结_V2

    Java正则表达式是Java编程语言中用于处理字符串的强大工具,它基于Perl风格的正则表达式,提供了灵活且强大的文本匹配和搜索功能。在Java中,正则表达式主要通过`java.util.regex`包来实现。下面是对Java正则表达式...

    正则表达式系统教程非常好的学习资料

    - 使用环视条件(lookaround)来精确控制匹配的上下文。 - 学习使用条件表达式、递归和后向引用,处理更复杂的匹配需求。 通过这个“正则表达式资料打包(正则全集)”的学习,你可以逐步掌握正则表达式的精髓,无论...

    正则表达式:深入理解与应用.zip

    - **编程语言**:大多数编程语言都内置了正则表达式支持,如JavaScript、Python、Java、Perl等。 - **文本编辑器**:如vim、Emacs等编辑器提供了强大的正则表达式搜索和替换功能。 - **命令行工具**:如grep、sed...

    java正则表达式详解(PDF)

    - **环视操作**:允许在主匹配之前或之后进行条件检查,但不包含在匹配结果中。 - **反向引用**:在分组中使用`\number`引用前面分组的内容。 7. **实例应用** - **验证邮箱格式**:`^[a-zA-Z0-9._%+-]+@[a-zA-Z...

    非常有用的正则表达式测试工具

    正则表达式还可以用于高级操作,如环视(前瞻和后瞻断言)、条件匹配、递归等,这些功能在RegexTester中都可以得到很好的支持。 总之,RegexTester作为一个强大的正则表达式测试工具,能够帮助用户快速测试、学习...

    正则表达式,查询方便

    pattern)`负向后向环视,匹配前面不是`pattern`的位置。 3. 非贪婪匹配:在量词后加上`?`,如`.*?`,使得匹配尽可能少的字符。 总的来说,正则表达式是一种强大的文本处理工具,熟练掌握它可以极大地提高处理文本...

    pcre2-10.20.tar.gz

    - **模式匹配**:支持PCRE语法的正则表达式匹配,包括贪婪和非贪婪匹配、反向引用、环视等高级特性。 - **Unicode支持**:处理Unicode字符集,包括Unicode属性和分类。 - **匹配选项**:允许在运行时动态调整匹配...

    正则表达式详细文档CHM版.rar

    - **引擎实现**:不同的编程语言或工具对正则表达式的支持程度不同,例如JavaScript使用的是NFA引擎,而Java和PCRE(Perl Compatible Regular Expressions)使用的是DFA引擎。 5. **应用实例** - **邮箱验证**:`...

    精通正则表达式.pdf

    正则表达式(Regular Expression,简称regex)是用于...通过阅读《精通正则表达式》这本书,读者将能够掌握正则表达式的高级特性和技巧,如条件表达式、递归模式、环视断言等,从而在处理复杂文本任务时更加得心应手。

    正则表达式测试工具RegexTester

    - **正则表达式引擎**:不同的正则表达式引擎(如Perl、JavaScript、PCRE等)可能有不同的语法和特性,RegexTester通常会支持多种引擎供选择。 5. **应用场景** - **网页抓取**:正则表达式用于从HTML或XML文档中...

Global site tag (gtag.js) - Google Analytics