正则表达式的真正威力(0)

原文

我在浏览StackOverflow上关于PHP的问题时经常看到有人提问如何使用正则表达式解析HTML。对于该问题的回答通常是这样的:

你不能使用正则表达式解析HTML,因为HTML不是正则的。使用XML解析器吧。

这种回答——在该问题的语境下——是有误导性的甚至是完全错误的。接下来我会展示现代正则表达式是多么强大。

“正则”到底是什么意思

形式语言理论中,说一个东西是“正则(regular)”那么其语法的产生式规则必须满足如下形式之一:

B -> a
B -> aC
B -> ε

这些带箭头的规则可以这样读,“左边可以被右边替换”。所以第一条规则是“B可以用a替换”,第二条是“B可以用aC替换”,第三条是“B可以用空字符串替换”(ε是代表空字符串的符号)。

BCa又代表什么呢?根据惯例,大写字母表示“非终结符”——可以进一步分解的符号,小写字母表示“终结符”——不能进一步分解的符号。

这么说可能有点抽象,我们来看一个例子:自然数的语法定义如下。

N -> 0
N -> 1
N -> 2
N -> 3
N -> 4
N -> 5
N -> 6
N -> 7
N -> 8
N -> 9
N -> 0N
N -> 1N
N -> 2N
N -> 3N
N -> 4N
N -> 5N
N -> 6N
N -> 7N
N -> 8N
N -> 9N

这个语法表达了下面的意义:

自然数N是
... 0到9的任意一个数字
或者
... 0到9的任意一个数字后面再加上一个自然数

这个例子中数字0到9就是终结符(不能进一步分解)并且N是唯一的非终结符(可以进一步分解)。

如果将上面自然数的语法规则和正则语法的定义比较一下,你就会发现自然数语法规则满足标准:前面十条规则符合形式B -> a并且接下来的十条规则符合形式B -> aC。因此定义自然数的语法是正则的。

你可能还注意到了,定义这样一个简单的东西的语法就显得很臃肿。如何用更加简洁的方式来表达相同的概念呢?

这就是正则表达式派上用场的时候了:上面的语法等同于正则表达式[0-9]+(真是简洁太多了)。对于每一种正则的语法都可以进行这种转换:每一种正则的语法都有相对应的正则表达式来定义其所有合法的字符串。

正则表达式可以匹配什么

那么问题就来了,正则表达式只可以匹配正则的语法吗?答案是肯定也是否定的:

按照形式语法中的定义来看正则表达式就只是用于解析正则的语法。然而当程序员谈论“正则表达式”时,不是说的形式语法,指的是他们所使用的编程语言实现的正则表达式变体。并且这些正则实现和原始的正则式概念没多大关系。

任何一个现代正则表达式实现能匹配的语言远超过正则语言。接下来就要讨论正则的能力界限到底在哪儿。

为了方便讨论,我将会专注于PCRE的正则实现,因为我对其了解最多(PHP使用该实现)。大部分其他正则实现是相似的,所以下面的讨论也同样适用。

语言层级

为了分析正则表达式能匹配什么和不能匹配什么,先了解一下除了正则语言还存在哪些其他类型的语言。下面是乔姆斯基谱系

Chomsky hierarchy:

/-------------------------------------------\
|                                           |
|     Recursively enumerable languages      | Type 0
|                                           |
|   /-----------------------------------\   |
|   |                                   |   |
|   |    Context-sensitive languages    |   | Type 1
|   |                                   |   |
|   |   /---------------------------\   |   |
|   |   |                           |   |   |
|   |   |  Context-free languages   |   |   | Type 2
|   |   |                           |   |   |
|   |   |   /-------------------\   |   |   |
|   |   |   | Regular languages |   |   |   | Type 3
|   |   |   \-------------------/   |   |   |
|   |   \---------------------------/   |   |
|   \-----------------------------------/   |
\-------------------------------------------/

乔姆斯基将语言划分为四种类型:

正则语言(类型3)是最弱的,然后是上下文无关语言(类型2),上下文相关语言(类型1)以及最后无所不能的递归可枚举语言(类型0)。

乔姆斯基谱系是一个包含性的层级,上图中小盒子完全包括在更大的盒子里面。例如每一个正则语言同时也是一个上下文无关语言(反之则不成立)。

因此我们在层级中向上走一级:我们已经知道正则表达式可以匹配任意正则语言。然而也可以匹配上下文无关语言吗?

(请注意:我在说“正则表达式”的时候是从程序员的视角来看的,而不是形式语言理论的视角。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值