4.20. 限制范围
4.20 限制范围#
之前我们说:正则表达式尝试匹配最长可能的字符串,因此能造成一些意料不到的问题。比如看这个匹配括号内任意数量字符的正则表达式。
".*"
让我们看一下一个 troff 宏,有两个括号括起来的参数如下所示:
.Se "Appendix" "Full Program Listings"
要匹配第一个参数,我们可能需要如下正则表达式来描述这个模式:
\.Se ".*"
然而它最终匹配了一整行,因为在模式中的第二个引号匹配了这一行的最后一个引号。
➜ ch03 git:(daily) ✗ gres '\.Se ".*"' '00' sampleLine
00
如果你知道有多少个参数,你可以将它们一一指定。
\.Se ".*" ".*"
虽然这像你预期的那样工作,但每一行可能不会有同样数量的参数,这会造成遗漏,你只是想要第一个参数。
➜ ch03 git:(daily) ✗ gres '\.Se ".*" ".*"' '00' sampleLine
00
下面是一个不同的正则表达式,它匹配了两个引号之间最短的可能内容:
"[ˆ"]*"
它匹配了 “一个双引号接着任意个不是双引号的字符,紧接着一个双引号”。
➜ ch03 git:(daily) ✗ gres '"[^"]*"' '00' sampleLine
.Se 00 "Full Program Listings"
现在让我们看看几行数字,它们用点字符(.)作为两列数字之间的指向符:
1........5
5........10
10.......20
100......200
匹配指向符的困难在于它们的数量是可变的。假如你匹配所有的指向符为一个 tab。你可能写一个正则表达式去匹配行如下:
[0-9][0-9]*\.\.*[0-9][0-9]*
这个表达式可能会意外的匹配这一行:
see Section 2.3
为了限制匹配,你可以指定所有行共有的最小数量的点。
[0-9][0-9]*\.\{5,\}[0-9][0-9]*
这个表达式使用了 sed 的里面有的大括号 {}
来匹配 “一个数字接着至少五个点,再接着一个数字”。为了看清楚发生了什么,我们将展示替换指向符的点为连接符的一个 sed 命令。然而我们还没有学习 sed 的替换元字符 —— \(
和 \)
来保存正则表达式的一部分和用 \1
和 \2
来重新调用保存部分的语法。因此这个命令可能看起来相当复杂(它确实是复杂)但是它完成了这个工作。
➜ ch03 git:(daily) ✗ sed "s/\([0-9][0-9]*\)\.\{5,\}\([0-9][0-9]*\)/\1-\2/" sample2
1-5
5-10
10-20
100-200
可以写类似的表达式来匹配在数据列之间的 tab 或前导的 tab。你可以更改列的顺序也可以将替换为另一个分割符。你应该动手时自己实验一下。使用 sed 或 gres 来进行简单和复杂的替换。