- 论坛徽章:
- 1
|
本帖最后由 iakuf 于 2014-05-13 16:28 编辑
"Perl 5 to 6" Lesson 07 - Regexes 正则表达式 (也叫 "rules")
概要- grammar URL {
- token TOP {
- <schema> '://'
- [<ip> | <hostname> ]
- [ ':' <port>]?
- '/' <path>?
- }
- token byte {
- (\d**1..3) <? $0 < 256 \}>
- }
- token ip {
- <byte> [\. <byte> ] ** 3
- }
- token schema {
- \w+
- }
- token hostname {
- (\w+) ( \. \w+ )\*
- }
- token port {
- \d+
- }
- token path {
- <[ a..z A..Z 0..9 \-_.!~\*'():@&=+$,/ ]>+
- }
- }
- my $match = URL.parse('http://perl6.org/documentation/');
- say $match<hostname>; # perl6.org
复制代码 描述
译者注: 不同于传统的正则表达式, Perl 6 不需要你记住的元字符的列表. 它通过一个简单的规则进行分类字符. 比如扩展的元语法 metasyntax 形式是 ( <...> ) 这样包含着字符. 比如 <.ws> 是尖括号包着 .ws 表示匹配空格. 其中的 . 表示不要捕获到正则的变量名 $ 中.
正则表达式已经在 Perl 6 中改良和革新, 这是重大改进之一. 我们不叫它 regular expressions 了. 因为它比起 Perl 5 更加不正规了.
正则表达式有三个大的更改和改进
语法更加干净
许多小变化使得规则更加容易编写. 例如, dot . 在匹配任何字符, 旧的 Perl 5 中的语义是要除去换行. 现在你可以使用 \N 来实现匹配零个或者多个非换行符.
修饰符现在是放在正则表达式的起始位置, 并且修改了非捕获组变成是 [...], 这比起旧的 (?:...) 更加容易读取和写.
嵌套捕获和 match 对象
在 Perl 5 中, 正则 (a(b))(c) 会给 ab 存到 $1, b 会存到 $2 和 c 存到 $3. 这种情况现在改变, 现在 $0 (枚举从零开始)包含 ab, $0[0] or $/[0][0] 包含 b, $1 存着 c. 所以括号中的每个嵌套级别反映在结果匹配对象的一个新的嵌套级别.
所有匹配变量是别名为 $/,也就是所谓的 Match object, 它实际上是一个包含了完整的匹配树型结构的对象。
命名的正则和 grammars
在 Perl 6 中开始, 你可以给你所写的正则表达式一个名字, 就象在子函数和方法中一样有自己的名字. 通过 <name> 这种方式来在其它地方重新使用这个. 你可以给多个正则表达式做成 grammars, 这就象类和支持继承和组合.
这些改变使得 Perl 6 的正则表达式和 grammars 比起 Perl 5 更加容易维护和编写.
这些变化的东西比较深, 这个文章中, 我们只能见到表面的东西.
语法更加干净
字母字符 ( 如下划线, 数字和所有 Unicode 字符 [a-zA-Z0-9_] ) 是根据字面内容来匹配, 也就是指, 它是什么就是什么, 当使用反斜杠转义这些字符时就会具有特殊的意义 ( 这是元语法 metasyntactic ).
对于所有的其它字符 ( 非 [a-zA-Z0-9_] ) 是反过来的 - 除非它转义(也就是说特殊字符 * 就是有特殊含义的,如果你想匹配一个 "星号",那就要用 "*").
- 字面意思 元语法
- a b 1 2 \a \b \1 \2 # 普通字符 [a-zA-Z0-9_]
- \* \: \. \? * : . ? # 其它字符 非 [a-zA-Z0-9_]
复制代码 并不是全部反斜杠转义的字符都是元语法 metasyntactic tokens 都有特别的含义. 除了 \a \b \1 \2 外, 可能其它的使用反斜杠转义的并没有确切的定义, 这时这些是非法的.
在正则表达式中还有另一种方法来转义字符串: 通过引号.- m/'a literal text: $#@!!'/
复制代码 在 Perl 6 中这改变了 . 的语义, 这个前面提过, 同时改变的还有 [...] 现在用于构建非捕获组. 字符类是 <[...]> 和非字符类<-[...]>. ^ 和 $ 总是匹配开始和结束的字符串. 匹配行的开始和结束是使用 ^^ 和 $$.
这意味着原来的 /s 和/m 修饰都没有了. 修饰符现在是在正则表达式中, 是在一开始的位置来给出:- if "abc" ~~ m:i/B/ {
- say "Match";
- }
复制代码 ... 这又是使用的 colon pair 符号. 你可以使用起来象子函数给个命名参数一样.
修饰符有一个短的和长的形式. 现在老 /x 修饰符是默认的, 即空格会被忽略.- 简写 长的写法 意义
- -------------------------------
- :i :ignorecase 忽略大小写 ( 旧的 /i )
- :m :ignoremark 忽略一些标记 ( 重音, 分音符号等 )
- :g :global 全局替换 ( 旧的 /g )
- :s :sigspace 正则表达式匹配所有空格
- (可选) 空格
- :P5 :Perl5 退回到 Perl 5 所兼容正则表达式语法
- :4x :x(4) Match four times (works for other numbers as well)
- :3rd :nth(3) Third match
- :ov :overlap 很象 :g, 但这会考虑重叠的匹配
- :ex :exhaustive Match in all possible ways
- :ratchet 不回溯
复制代码 这的 :sigspace 需要花些时间来解释. 它通过使用了 <.ws> 的有名字的正则替换了模式中的空格 ( 这是调用 rule ws 并不保存捕获的结果 ). 你可以覆盖该规则.
的规则默认会匹配二个单词中间所包含的一个或者多个空格. 如果不是单词之间就是匹配零个或者多个空格.
Match 对象
每个匹配会生成一个叫 match 对象的东西, 会给这些匹配到的内容存储到指定的变量 $/ 中. 它是非常有用的. 如果匹配成功并且在布尔上下文中, 它返回 Bool::True. 在字符上下文会返回匹配到的字符, 作为一个列表时会返回其中包含的捕获位置, 作为哈希使用时它包含命名捕获. 这个对象有 .from 和 .to 方法, 会返回包含首先的和最后的字符位置上找到的内容.- if 'abcdefg' ~~ m/(.(.)) (e | bla ) $<foo> = (.) / {
- say $/[0][0]; # d
- say $/[0]; # cd
- say $/[1]; # e
- say $/<foo> # f
- }
复制代码 这的 $0, $1 只是 $/[0], $/[1] 的别名. 同样 $/<x> 和 $/{'x'} 也只是 $<x> 的别名.
注意, 任何你通过 $/[...] 和 $/{...} 访问的都是 match 对象( 或者是 match 对象的列表 ). 这可以让你通过 rules 创建真实的解析树.
命名正则和 Grammars
正则表达式可以使用旧的风格 m/.../, 或者可以使用名字来给他们声明成象子函数和方法一样.- regex a { ... }
- token b { ... }
- rule c { ... }
复制代码 所不同的是 token 隐含了 :ratchet 修饰符 ( 这意味着没有回溯, 象 (?> ... ) 组包含各自的部分 ), 这个 rule 隐含了 :ratchet 和 :sigspace 二个修饰符.
要调用这些规则 ( 我们会要调用的全部的这些规则, 通过关键字声明后会相互独立 ) 只要使用尖括号中加名称: <a> 就可以使用. 这隐含地规则的字符串会放到的当前位置, 存储匹配到的结果到 $/<a> 的对象中, 也就是说, 它是也是一个命名捕获. 你也可以使用 <.a> 来调用不做命名捕获的匹配 ( 不同处在于加了前缀点 ).
grammar 是一系列规则的组合, 这很象对象中的类 ( 看 SYNOPSIS 中的例子). Grammars 可以继承, 重写规则等.- grammar URL::HTTP is URL {
- token schema { 'http' }
- }
复制代码 动机
Perl 5 的正则表达式常常不太可读, grammars 这个东西是鼓励你把一个大的正则表达式表示成为更可读的, 短的片段. 命名捕获使规则更加能实现自我文档, 而且很多东西比起以前现在更加一致.
最后, grammars 是如此强大, 你可以用它分析了解每一种编程语言, 包括 Perl6 本身. 这使得 Perl 6 的语法比起 Perl 5 更易于维护和修改.
另请参阅
http://perlcabal.org/syn/S05.html
http://perlgeek.de/en/article/mutable-grammar-for-perl-6
http://perlgeek.de/en/article/longest-token-matching |
|