正则表达式中的非获取匹配

正则表达式是代码中的常用技巧,主要用来匹配符合某种句法规则的字符串。常用的正则表达式本文不再赘述,详细可以参考wikiPythonJava)。本文主要讨论的是非获取匹配的正则表达式。

常见的非获取匹配有以下几种:

(?:pattern) 主要用来匹配pattern但不获取结果。我们看下面的例子。

>>> re.match("win (7|vista)", "win 7").group()
'win 7'

>>> re.match("win (7|vista)", "win 7").groups()
('7',)

>>> re.match("win (?:7|vista)", "win 7").group()
'win 7'

>>> re.match("win (?:7|vista)", "win 7").groups()
()

可以看出,使用这种模式括号中匹配内容并没有存储供以后使用。它是比“win 7|win vista”更加简便的形式。

以上是Python的示例,Java代码如下(以后的例子不再赘述):

Pattern ptn = Pattern.compile("win (7|vista)");
Matcher m = ptn.matcher("win 7");
while (m.find()) {
    for (int i = 0; i <= m.groupCount(); i++) {
        System.out.println(m.group(i));
    }
}

(?=pattern) 正向肯定预查。它在任何匹配pattern的字符串处查找字符串,它仅仅是检查是否匹配,而不将预查的结果包含其中。当然,它也是非获取匹配。看个例子就明白了。

>>> re.match("win (?=7|vista)", "win 7").group()
'win '

>>> re.match("win (?=7|vista)", "win 7").groups()
()

(?!pattern) 正向否定预查。例子如下:

>>> re.match("win (?!7|vista)", "win 8").group()
'win '

>>> re.match("win (?!7|vista)", "win 8").groups()
()

(?<=pattern) 反向肯定预查。查找某个字串之前是否满足pattern。

>>> re.search("(?<=95|98)win", "98win").group()
'win'

>>> re.search("(?<=95|98)win", "98win").groups()
()

(?<!pattern) 反向否定预查。

>>> re.search("(?<!95|98)win", "7win").group() 
'win'

>>> re.search("(?<!95|98)win", "7win").groups()
()

现在我们来看实际中的需求。现在我有一个字符串,这个字符串是由中文和英文组成,现在我要把这个字符串切成一个个只由中文或者英文组成的字串。比如说将“McCulloch与Pitts将神经系统”切成”McCulloch“、“与”、“ Pitts“、”将神经系统“。

首先要知道匹配中文的正则是”[\u4e00-\u9fa5]“,在Python中,我们可以使用findall方法:

reg = re.compile(u"[\u4e00-\u9fa5]+|[A-Za-z]+")
print "\t".join([w for w in reg.findall(u"McCulloch与Pitts将神经系统")])

对于Java,除了使用这种方式匹配,我们还使用刚刚说过的非获取匹配来达到同样的效果。对于需求的句子,可以看到要切分的地方,要么是中文接英文,要么是英文接中文。所以我们在反向肯定预查到中文然后正向肯定预查到英文处调用split操作,或者在反向肯定预查到英文正向肯定预查到中文处调用同样的操作。于是有:

Pattern ptn = Pattern.compile(
    "(?<=[\u4e00-\u9fa5])(?=[A-Za-z])|(?<=[A-Za-z])(?=[\u4e00-\u9fa5])", 
    Pattern.UNICODE_CASE);
String[] words = ptn.split("McCulloch与Pitts将神经系统");
for(String word: words) {
    System.out.print(word + "\t");
}

可以看到达到了一样的效果,但是,在我的测试中,此法对Python中的正则库中的split方法并不适用。所以我们解决问题确实需要灵活多变的策略。

最后,我们再讨论以下js的实现。对于js来说,由于js不支持反向预查。所以,用和Python方法类似的简单策略。我们使用加上“g“修饰符的match方法(表示全局匹配,而不是只匹配一次)。

var testStr = "McCulloch与Pitts将神经系统";
var regExp = /[\u4e00-\u9fa5]+|[A-Za-z]+/g;

testStr.match(regExp).forEach(function(i){
    document.write(i+"  ");
});

赞这篇文章

分享到

给作者留言

关于作者

残阳似血(@秦续业),程序猿一枚,把梦想揣进口袋的挨踢工作者。现加入阿里云,研究僧毕业于上海交通大学软件学院ADC实验室。熟悉分布式数据分析(DataFrame并行化框架)、基于图模型的分布式数据库和并行计算、Dpark/Spark以及Python web开发(Django、tornado)等。

博客分类

点击排行

标签云

扫描访问

主题

残阳似血的微博