众所周知,现代正则表达式实现(最值得注意的是PCRE)与regular grammars的原始概念几乎没有共同之处。例如,您可以解析context-free grammar {a n b n 的经典示例; n> 0}(例如aaabbb
)使用此正则表达式(demo):
~^(a(?1)?b)$~
我的问题是:你能走多远?是否也可以解析context-sensitive grammar {a n b n c n ; n> 0}(例如{{1}使用PCRE?
答案 0 :(得分:33)
受到NullUserExceptions答案的启发(他已经因为一个案例失败而已删除)我想我自己找到了一个解决方案:
$regex = '~^
(?=(a(?-1)?b)c)
a+(b(?-1)?c)
$~x';
var_dump(preg_match($regex, 'aabbcc')); // 1
var_dump(preg_match($regex, 'aaabbbccc')); // 1
var_dump(preg_match($regex, 'aaabbbcc')); // 0
var_dump(preg_match($regex, 'aaaccc')); // 0
var_dump(preg_match($regex, 'aabcc')); // 0
var_dump(preg_match($regex, 'abbcc')); // 0
亲自尝试:http://codepad.viper-7.com/1erq9v
如果你考虑没有正向前瞻断言的正则表达式((?=...)
部分),你就有了这个:
~^a+(b(?-1)?c)$~
这只会检查是否存在任意数量的a
s,后跟相同数量的b
和c
s。
这还不能满足我们的语法,因为a
的数量也必须相同。我们可以通过检查a
的数量是否等于b
的数量来确保。这就是前瞻断言中的表达式:(a(?-1)?b)c
。 c
是必需的,因此我们不仅匹配b
的一部分。
我认为这令人印象深刻地表明,现代正则表达式不仅能够解析非常规语法,而且甚至可以解析非上下文无关的语法。希望这将打破无休止的鹦鹉学舌“你不能用正则表达式做X,因为X不规则”
答案 1 :(得分:11)
我的问题是:你能走多远?
为了不创建一个难以理解的标点符号的代码,我将冒险投票并回答一个不同的,但非常相关的问题: 应该 >你去?
正则表达式解析器是您工具箱中的辉煌事物,但它们不是全部并且完成所有编程。以可读的方式编写解析器的能力 是您工具箱中的一件好事。
应该使用正则表达式直到它们开始使您的代码难以理解。除此之外,他们的价值充其量是可疑的,最糟糕的是破坏性。对于这种特殊情况,而不是使用像hideous这样的东西:
~^(?=(a(?-1)?b)c)a+(b(?-1)?c)$~x
(向NikiC道歉),绝大多数人试图维护它要么必须完全取代,要么花费大量的时间阅读和理解,你可能要考虑类似于非RE,“正确解析器”解决方案(伪代码):
# Match "aa...abb...bcc...c" where:
# - same character count for each letter; and
# - character count is one or more.
def matchABC (string str):
# Init string index and character counts.
index = 0
dim count['a'..'c'] = 0
# Process each character in turn.
for ch in 'a'..'c':
# Count each character in the subsequence.
while index < len(str) and str[index] == ch:
count[ch]++
index++
# Failure conditions.
if index != len(str): return false # did not finish string.
if count['a'] < 1: return false # too few a characters.
if count['a'] != count['b']: return false # inequality a and b count.
if count['a'] != count['c']: return false # inequality a and c count.
# Otherwise, it was okay.
return true
这将在未来更容易维护。我总是向人们建议他们应该假设那些追随他们的人(他们必须维护他们所写的代码)是知道你住在哪里的精神病患者 - 在我的情况下,这可能是对的,我不知道你住在哪里: - )
除非您对此类正则表达式有真正的需求(有时候有很好的理由,例如解释语言中的性能),否则应优化可读性第一
答案 2 :(得分:10)
以下是使用带有.NET正则表达式的balancing groups的替代解决方案:
^(?'a'a)+(?'b-a'b)+(?(a)(?!))(?'c-b'c)+(?(b)(?!))$
不是PCRE,但可能会引起关注。
编辑:为组a添加了缺失的平衡检查,以及在线示例。
答案 3 :(得分:2)
没有提到的解决方案:
^(?:a(?=a*(\1?+b)b*(\2?+c)))+\1\2$
在the regex demo中查看匹配和失败的内容。
这使用自引用组(his vertical regex上使用的@Qtax的想法)。