Java正则表达式——分组与捕获

今天处理语料的时候,遇到了问题,问题如下:一些标注好的标有艺术家、歌曲名、专辑名等的音乐评论的帖子,例如:<artist>Smile</artist>组合继第一张专辑《<album>Smile</album>》之后又推出的一张舞曲大碟。我想在这样含有两个不同标注实体(艺术家、专辑名)的的句子当中统计动词的频率。

    好了,现在问题清楚了,决定了要用正则来解决,查资料,把网上一篇还可以的文章转载下来:

    转自:http://blog.csdn.net/lovingprince/archive/2008/08/06/2774819.aspx

    正则表达式在字符串处理中经常使用,关于正则简单的用法相信有一点程序基础的人都懂得一些,这里就不介绍简单基础了。这里主要讲解一下在JAVA中实现了的正则的高级用法-分组与捕获。

    对于要重复单个字符,非常简单,直接在字符后卖弄加上限定符即可,例如 a+ 表示匹配1个或一个以上的a,a?表示匹配0个或1个a。这些限定符如下所示:

 

X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m}

X,至少 n 次,但是不超过 m 次

 

 

但是我们如果要对多个字符进行重复怎么办呢?此时我们就要用到分组,我们可以使用小括号"()"来指定要重复的子表达式,然后对这个子表达式进行重复,例如:(abc)? 表示0个或1个abc 这里个括号的表达式就表示一个分组

 

   分组可以分为两种形式,捕获组和非捕获组。

 

捕获组 

捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C))) 中,存在四个这样的组:

    ((A)(B(C)))
    \A
    (B(C))
    (C)

组零始终代表整个表达式

之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。捕获的子序列稍后可以通过 Back 引用在表达式中使用,也可以在匹配操作完成后从匹配器检索。

 

 Back 引用 是说在后面的表达式中我们可以使用组的编号来引用前面的表达式所捕获到的文本序列(是文本不是正则)

 

 例如 ([" ']).* \1   其中使用了分组,\1就是对引号这个分组的引用,它匹配包含在两个引号或者两个单引号中的所有字符串,如,"abc" 或 " ' " 或 '  ,但是请注意,它并不会对" a'或者 'a"匹配。原因上面已经说明,Back引用只是引用文本而不是表达式。

 

非捕获组 

      以 (?) 开头的组是纯的非捕获 组,它不捕获文本,也不针对组合计进行计数。就是说,如果小括号中以?号开头,那么这个分组就不会捕获文本,当然也不会有组的编号,因此也不存在Back 引用。

      在Java中,支持的非捕获组,有如下几种:

 

   
   
   
(?=X)     X,通过零宽度的正 lookahead
(?!X)     X,通过零宽度的负 lookahead
(?<=X)     X,通过零宽度的正 lookbehind
(?<!X)     X,通过零宽度的负 lookbehind
 

 

 

 

这四个非捕获组用于匹配表达式X,但是不包含表达式的文本。

 

(?=X ) 零宽度正先行断言。仅当子表达式 X 在 此位置的右侧匹配时才继续匹配。例如,\w+(?=\d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。
(?!X) 零宽度负先行断言。仅当子表达式 X 不在 此位置的右侧匹配时才继续匹配。例如,例如,\w+(?!\d) 与后不跟数字的单词匹配,而不与该数字匹配
(?<=X) 零宽度正后发断言。仅当子表达式 X 在 此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。
(?<!X) 零宽度负后发断言。仅当子表达式 X 不在此位置的左侧匹配时才继续匹配。例如,(?<!19)99 与不跟在 19 后面的 99 的实例匹配

 

 

 

举例:

 

 上面都是理论性的介绍,这里就使用一些例子来说明一下问题:

   1、测试匹配性   (?<!4)56(?=9) 这里的含义就是匹配后面的文本56前面不能是4,后面必须是9组成。因此,可以匹配如下文本 5569  ,与4569不匹配。

 

  2 、提取字符串   提取 da12bka3434bdca4343bdca234bm   提取包含在字符a和b之间的数字,但是这个a之前的字符不能是c,b后面的字符必须是d才能提取。

 

        例如这里就只有3434这个数字满足要求。那么我们怎么提取呢?

       首先我们写出提取这个字符串的表达式: (?<!c)a(\d+)bd  这里就只有一个捕获组(\d+)

       JAVA代码片段如下:

  1.  Pattern Pattern.compile("(?<!c)a(\\d+)bd");
  2.  Matcher p.matcher("da12bca3434bdca4343bdca234bm");
  3.  while(m.find()){
  4.    System.out.println(m.group(1)); //我们只要捕获组1的数字即可。结果 3434
  5.    System.out.println(m.group(0)); // 0组是整个表达式,看这里,并没有提炼出(?<!c)的字符 。结果 a3434bd
  6.  }

    可以看到,非捕获组,最后是不会返回结果的,因为它本身并不捕获文本。

最后用了两种方法解决:

解法一:比较笨的方法,没有使用非捕获组:

  Pattern p = Pattern.compile("<(.*)>.*</\\1>");……

  Matcher m = p.matcher(line);
  HashSet<String> setstr = new HashSet<String>();
  while( m.find()){
    if( !setstr.contains(m.group(1)) && !setstr.isEmpty()){
        //这里的line就是符合要求的内容

解法二:使用非捕获组

  Pattern p = Pattern.compile("<(.*)>.*</\\1>.*<(?!\\1)");
一个正则表达式就可解决。

再次印证了正则表达式的重要性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值