今天查看 beancount 的配置,总觉得配置写的有点笨,稍微优化了一点点。其中把.bean 和.beancount 文件都映射到 beancount-mode,之前是用两条语句完成,应该用正则表达式`"\\.bean\\(count\)\\'"`一行语句就设置完成就好。可能是习惯使然,顺便查了一下 auto-mode-alist 的值,竟然发现里面的值都是类似`"\\.bean\\(?:count\)\\'"`的形式,里面多了一个`?:`,不知是什么作用。祭出 google 大法后,才明白`?:`的作用。

要理解?=和?!,首先需要理解前瞻,后顾,负前瞻,负后顾四个概念:

1
2
3
4
5
6
7
8
// 前瞻:
exp1(?=exp2) 查找exp2前面的exp1
// 后顾:
(?<=exp2)exp1 查找exp2后面的exp1
// 负前瞻:
exp1(?!exp2) 查找后面不是exp2的exp1
// 负后顾:
(?<!exp2)exp1 查找前面不是exp2的exp1

举例:

1
2
"中国人".replace(/(?<=中国)人/, "rr") // 匹配中国人中的人,将其替换为rr,结果为 中国rr
"法国人".replace(/(?<=中国)人/, "rr") // 结果为 法国人,因为人前面不是中国,所以无法匹配到

要理解?:则需要理解捕获分组和非捕获分组的概念:

1
2
()表示捕获分组,()会把每个分组里的匹配的值保存起来,使用$n(n是一个数字,表示第n个捕获组的内容)
(?:)表示非捕获分组,和捕获分组唯一的区别在于,非捕获分组匹配的值不会保存起来

举例:

1
2
// 数字格式化 1,123,000
"1234567890".replace(/\B(?=(?:\d{3})+(?!\d))/g,",") // 结果:1,234,567,890,匹配的是后面是3*n个数字的非单词边界(\B)

之前一直觉得自己对正则表达式已经掌握的不错了,现在看来还是远远不够,学无止尽啊!