正则表达式匹配工具 Regex Golf 习题,据说练习通过17题后就能精通正则表达式。 引用自 http://jimliu.net/2014/01/04/regex-golf/ 1.Plain String Type a regex in the box. You get ten points per correct match. Hit Enter to go to the next ‘level’.

送分题,毫无疑问,答案是foo,207分。

2.Anchors You are deducted one point per character you use, and ten if you match something you shouldn’t.

考察对边界的应用。最开始我给出的是ick$,后来发现是ck$,最后丧心病狂的发现是k$,得分分别是206, 207, 208。

3.Ranges The test vectors were generated by grepping /usr/dict/words. Can you tell?

考察对范围的应用,同时结合边界和量词。我的答案是^[a-f]+$,当然^[a-f]*$也是可以的,并且得分一样,都是202。

4.Backrefs This doesn’t really work as a tutorial. Not really clear what you’re supposed to do here.

从题面上看,是考察对于反向引用的使用。我给的答案是(.{3,}).\1,得分是199,找了很久没有发现优化空间,直到我看了牛人解答之后,竟然给出了(...).\1,我真蠢。

阶段性小结 毫无疑问前4关是比较简单的,其中前3关都是正则中会用到的基本用法,第4关只要是比较熟练的同学也肯定是会的,能否拿高分差别在于够不够省。

5.Abba Let’s pretend this one is not a rehash of the last one.

说实话这个题目相当具有迷惑性,也挺有难度,微博上看的确有不少同学卡在这题上了。

观察题面,要求不匹配字符串内含形如abba组合的串,首先可以简单地使用反向引用构造出(.)(.)\2\1。

有了它后,怎么做到不匹配呢?这里要用到负向前瞻,负向前瞻是零宽断言的一种,JavaScript中的负向前瞻形如(?!exp),匹配后面不是exp的串。

OK,下面一步步构造,首先用(?!(.)(.)\2\1)试试,发现左边的都匹配上了,右边的……也匹配上了- -|。然后用一个很损的办法,^(?!(.)(.)\2\1),右边干掉了2个。

这时候其实回想一下题目,我们要排除的是形如abba的串,那么在刚才的基础上加上^(?!.(.)(.)\2\1.),对了!191分。

再仔细想一下,对abba再后面的串其实没必要再限制了,优化到^(?!.*(.)(.)\2\1),193分。

JavaScript的正则只支持前瞻(Look Ahead)而不支持后瞻(Look Behind),也就是说我们只能“用右边的东西限制当前位置”。

6.A man, a plan You’re allowed to cheat a little, since this one is technically impossible.

对称串嗯哼?有了前面的经验这题不会很难的,我构造了半天,中间也经过几次升级,结果用了(.)(.).?\2\1.?$,得分是175。

牛X答案给的是^(.)[^p].*\1$,我只想说这东西相当牛X,而且很符合题目描述cheat a little,它能拿到177分。

7.Prime The length is not part of the string. I should probably have chosen a different color.

非常非常好玩的一道题,要不是我曾经看过M67的一篇博客知道正则有判断素数这种神奇的用法,这道题简直无从下手。

首先用^x?$|^(xx+?)\1+$判断长度是合数,因为没有长为0或1的,所以直接精简到^(xx+?)\1+$,nice!错误答案全部匹配,正确答案全部不匹配。

有了上面的经验,剩下的不会有什么难度了,^(?!(xx+?)\1+$),285分到手。而牛人答案^(?!(..+)\1+$)中却没有对中间的xx+?启用非贪婪,达到286分,这个因为我不怎么看得懂这个素数匹配的原理,所以我也不再妄加评判了……

8.Four You can get an extra point by ignoring the name of this level.

观察题面,它要匹配的其实是形如aaaa或者是aaaa,按照这个思路,可以构造出.(.)(?:.\1){3,}|(.).(?:\2.){3,},这样已经可以拿到179分了。

其实进一步观察,不难发现其实形态1中的开头和形态2中的结尾是并不重要的,所以我们想要的其实只是aaa而已,那么(.)(.\1){3}就可以完美解决了,并且能够得到199分。

阶段性小结 5~8题开始有点没节操了,但至少还是在技术技巧的范围内的,第7题天马行空,报酬也丰厚。

事实上我从9题开始就胡诌了。

9.Order Cheat.

描述一点也不含蓄,看题面,发现需要匹配的是非降序的串。

然后我实在是不会了,我本来天真的以为(.)[\1-z]中的范围表达式是可以利用反向引用的,结果当然是不行。于是我随便写了个^a[b-z]+$,惨淡地得了41分。

然后看了牛人解答我哭了,^.{5}[^e]?$,果然是cheat,当然也不得不佩服这观察力,199分。

10.Triples Multiples of 7 are left as an exercise for the reader.

题面很简单,就是能被3整除。搜索了一下,有能构造匹配能被3整除的2进制数的办法,但是这里是10进制。于是胡诌继续,00就这么搞上去,也能118分你敢信?

答案是00($|3|6|9|12|15)|4.2|.1.+4|55|.17,满分达到了596分。我觉着这货跟数学没啥关系,就是靠观察力硬凑……

11.Glob 题面非常有意思,要构造一个xxx matches yyy这种,也就是matches的前面充当正则的时候能匹配后面的串。

当然如果往这个方向想,那就肯定做不出来的(好残酷),当我看到答案的时候,我吓尿了:ai|c$|^p|[bcnrw][bnopr]这要有多强大的观察力才能做出来。

我的答案*,58分惨淡收场。

12.Balance This one is also impossible, but there’s a finite number of test cases.

描述都说impossible,当场吓尿。你猜我给的是什么?说出来吓死你:^$,得分8也吓死你……

@Thomas凭借<<<<得到了146分,我佩服他想象力!

牛人答案是^(<(<(<(..)>)>)>)$得分288,但这依然并不是完美答案。

括号配对对于正则是一个非常大的难题,因为正则文法的定义先天就不是递归的。我们现在用的加强的正则表达式,尽管加入了反向引用、计数、前瞻后瞻等高级功能,但我理解它的数学定义并没有超过这个范畴,而是在实现引擎层面上新增的功能。

但其实正则来处理括号配对不是不可能的,C#标准库的正则是支持平衡组的,我也没有实际用过,就不深入聊这个了。

阶段性小结 9~12题是相当丧心病狂,不“作弊”基本没法正常做了。而且后面的题目作者好像已经完全懒得写描述了。

13.Powers 从题目名字看,就是幂(我不是要说杨幂)。

付出了把键盘敲烂的代价,我丧心病狂地写出了^((((((((((x)\10?)\9?)\8?)\7?)\6?)\5?)\4?)\3?)\2?)\1?$,虽然是完美匹配,但估计是因为表达式太长,只得了56分。

牛人答案是^(?!(.(..)+)\1*$),这个很有意思,我们来解读一下。

首先我只看到了正确答案是长度为2的整数次幂的串,而没有观察错误答案,这不得不说是一个重大失误!

错误答案中比较短的几个,长度是3,5,7,11,13的串,可以表示为2n+1,那么可以先构造出^(.(xx)+)$,发现还匹配了401和1025。

那么,长度为28,48,160,600的呢?把他们做因数分解,发现28=722, 48=32222, 160=522222, 600=75222,也就是说他们都是(2n+1)pow(2, m)的形态。于是在上面的基础上构造出来^(.(xx)+)\1$,能够成功匹配所有错误答案了!

结合第7题中的狗血方法,稍作修改就得到^(?!(.(..)+)\1*$)这个答案,其实也不过才93分而已嘛……当然不看答案的话我也真心找不到这规律。

14.Long Count 我会告诉你我直接把左边的那串数字拿来用了吗?191分。

答案是^((.+)0 \2+1 ?)*$仔细看看应该能明白,还是凭借细心的观察,这样可以拿到253分。

15.Long Count v2 我会告诉你我又直接把左边的那串数字拿来用了吗?191分。

答案又是^((.+)0 \2+1 ?)*$,这样又可以拿到253分。我根本没观察跟14题有啥区别,所以不评论了。

16.Alphabetical 毫无头绪的一题,aerate骗到33分,好累,感觉不会再爱了。

答案是.r.{32}r|a.{10}te|n.n..,317分,我的建议是不要试图解读它。