Groovy Tip 29 正则表达式 三
本篇主要来谈谈"捕获组"和"非匹配组"以及与它们相关联的一些概念。
"捕获组"应该来说是一个很重要的特性,特别是在进行文字处理的时候。比如,我们经常会遇到一些文字或数字跟一些符号混合在一起,而我们需要把这些文字或数字从这些符号中分离出来。这时候,我们就可以用到"捕获组"。
先从一个简单的例子说起。比如,我们有如下的一个email地址:
我们需要从上面的email地址中分离出"fgp"、"sina"和"com"来,如果使用"split"方法的话,我们需要做两次"split"动作才能达到我们的要求。
但是,如果使用"捕获组"的话,我们只需要做一次动作。如:
def amail = 'fgp@sina.com'
def re = /(.*)@(.*)/.(.*)/
def matcher = (amail =~ re)
println matcher[0]
运行结果为:
["fgp@sina.com", "fgp", "sina", "com"]
再举一个看起来有那么一点点实用的例子,比如我们有如下的一组价格表,由商品名称、价格以及它们所能打的折扣组成。
computer 3000¥ 10%
mouse 50¥ 0%
memory 200¥ 20%
现在,我们希望把商品名称、价格和打折分别提取出来。
使用"捕获组"的代码如下:
def goods =
"""computer 3000¥ 10%
mouse 50¥ 0%
memory 200¥ 20%"""
def groups = {
def re = /(.*) (.*)¥ (.*)%/
def matcher = (it =~ re)
println matcher[0]
}
goods.split('/n').each(groups)
运行上述代码的结果为:
["computer 3000¥ 10%", "computer", "3000", "10"]
["mouse 50¥ 0%", "mouse", "50", "0"]
["memory 200¥ 20%", "memory", "200", "20"]
相比较而言,"非匹配组"的使用就更为复杂一些,这里面除了"非匹配组"本身的概念,还有一些相关的概念需要说明。
首先要说明的是"最大匹配"和"最小匹配"的概念。在正则表达式中,我们的一些操作符,如"?"、"*"和"+"在默认的情况下,都是指的"最大匹配";如果需要需要"最小匹配",则需要在上述操作符后面加上"?"操作符,才能表示它们是"最小匹配"。
下面来举一个经典的例子来说明。比如我们有如下的一个html语句:
<td>abc</td>
那么,我们先进行如下的配置:
def html = '<td>abc</td>'
def re = /<.*>/
def matcher = (html =~ re)
println matcher[0]
再进行如下的匹配:
def html = '<td>abc</td>'
def re = /<.*?>/
def matcher = (html =~ re)
println matcher[0]
其中,第一段代码就进行的就是"最大匹配",运行结果为:
<td>abc</td>
第二段代码为"最小匹配",运行结果为:
<td>
所谓"非匹配组",指的是在一个字符串里,有我们想要的匹配组,也有我们不想要的非匹配组。我们想要的匹配组好说,就是使用我们上面所说到的"捕获组"来解决;那么我们不想要的非匹配组,我们该怎么处理呢?
要匹配"非匹配组",我们要做的工作其实是很简单,就是括号,并且在括号里以"?:"开头。下面来举一个例子说明。
还是以上面的价格表为例,比如我们有如下的价格表:
computer Intel CUP 3000¥ 10%
mouse made in China mainland 50¥ 0%
memory made in Taiwan 200¥ 20%
这个价格表比前面的价格表更为复杂一些,中间夹杂了一些对商品的描述。现在,我们还是希望取出商品名称、价格和打折来,而不需要商品的描述。
这样,我们就用到了"非匹配组",代码如下:
def goods =
"""computer Intel CUP 3000¥ 10%
mouse made in China mainland 50¥ 0%
memory made in Taiwan 200¥ 20%"""
def groups = {
def matcher = (it =~ /(.*?)(?: .+)+ (.*)¥ (.*)%/);
if (matcher.matches())
{
println matcher[0]
}
}
goods.split('/n').each(groups)
运行结果为:
["computer Intel CUP 3000¥ 10%", "computer", "3000", "10"]
["mouse made in China mainland 50¥ 0%", "mouse", "50", "0"]
["memory made in Taiwan 200¥ 20%", "memory", "200", "20"]
在上面的代码中,正则表达式中的"(?: .+)+"就是"非匹配组"。值得注意的是,该正则表达式的开头"(.*?)",就用到了"最小匹配"的概念,如果我们把其中的问号去掉,变成"最大匹配",那么结果又将是什么样子呢?
def goods =
"""computer Intel CUP 3000¥ 10%
mouse made in China mainland 50¥ 0%
memory made in Taiwan 200¥ 20%"""
def groups = {
def matcher = (it =~ /(.*)(?: .+)+ (.*)¥ (.*)%/);
if (matcher.matches())
{
println matcher[0]
}
}
goods.split('/n').each(groups)
运行结果为:
["computer Intel CUP 3000¥ 10%", "computer Intel", "3000", "10"]
["mouse made in China mainland 50¥ 0%", "mouse made in China", "50", "0"]
["memory made in Taiwan 200¥ 20%", "memory made in", "200", "20"]
可以看到,上面就不是我们想要的结果了。