找回密码
 FreeOZ用户注册
查看: 2265|回复: 6
打印 上一主题 下一主题

[论坛技术] Java文本解析小记——小key出品

[复制链接]
跳转到指定楼层
1#
发表于 30-6-2009 16:18:42 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?FreeOZ用户注册

x
曾几何时我们小心翼翼地用BufferedReader.readLine()一行一行地读入文本,
用StringTokenizer.nextToken()一个个地取出字句,进行手动的分析。

自从Java 1.4引入Regex之后,这种苦日子就离我们越来越远了。但是,晦涩的
Java Regex总是让我们快乐不起来,朋友们总是说,为什么同是regex,Perl
能让人那么快乐,而Java总会象20年的老车那样发着各种吱吱歪歪的噪音?

本文的目的就是和大家一起探讨一下Java文本解析的应用。请明白,这里不是教程,
我只是按自己的喜好,写一些自己的个人观点,欢迎批评。
回复  

使用道具 举报

2#
 楼主| 发表于 30-6-2009 17:09:54 | 只看该作者

正则三步曲

1. Pattern p = Pattern.compile(xxx);
2. Matcher m = p.matcher(yyyyy);
3. m.matches()/find();

如果只是想测试一下是否match,可以一步处理:
static boolean Pattern.matches(regex, input);

如果只是想用正则表达式分割字符串,可以两步走:
1. Pattern p = Pattern.compile(xxx);
2. String [] ss = p.split(str); 或 String [] ss = p.split(str, limit)

这和String.split()是一致的。事实上String.java的实现就是这样:
  1. 2292     public String[] split(String regex, int limit) {
  2. 2293         return Pattern.compile(regex).split(this, limit);
  3. 2294     }
复制代码
由于正则表达式本身对于一些标点会有不同的定义,如果这些标点其实是literals,
那就需要用转义来表明。如果正则表达式太长(一般都不会太长),这样的转义太
多太烦,可以用Pattern.quote()来进行处理。

以上内容应该就是Pattern类的全部了。

评分

参与人数 1威望 +30 收起 理由
ubuntuhk + 30 谢谢分享!

查看全部评分

回复  

使用道具 举报

3#
 楼主| 发表于 30-6-2009 17:50:01 | 只看该作者

渐入佳境:MatchResult

Pattern和Matcher是正则表达式的两个支撑类。Pattern是编译器,
而Matcher是匹配引擎。引擎毕竟是复杂的,在看这个引擎之前,我们先来看看
这个引擎的输出:MatchResult.

MatchResult的方法列表可分成三组:
start
end
group

而又可以分成两个方面:group与整体。

这里需要解释什么叫做group。。。。需要吗?直接看程序吧:
  1.   1 import java.util.regex.*;
  2.   2
  3.   3 public class RegexGroupTest {
  4.   4     public static void main(String [] args){
  5.   5         Matcher m = Pattern.compile(args[0]).matcher(args[1]);
  6.   6
  7.   7         if(m.find())
  8.   8         {
  9.   9             System.out.println("start = " + m.start());
  10. 10             System.out.println("end   = " + m.end());
  11. 11             System.out.println("group = " + m.group());
  12. 12
  13. 13             System.out.println("");
  14. 14             System.out.println("groupCount = " + m.groupCount());
  15. 15             int gc = m.groupCount();
  16. 16             for(int k=0; k<=gc; k++)
  17. 17             {
  18. 18                 System.out.printf("\t[%d]: start = " + m.start(k) + "\n", k);
  19. 19                 System.out.printf("\t[%d]: end   = " + m.end(k) + "\n", k);
  20. 20                 System.out.printf("\t[%d]: group = " + m.group(k) + "\n", k);
  21. 21                 System.out.println("");
  22. 22             }
  23. 23
  24. 24         }
  25. 25     }
  26. 26 }
复制代码
请注意Line-16,这里用了 k <= gc,原因是,0那个是整体的,如果用 k < gc,就少一块了。
请看运行结果:
  1. $ java RegexGroupTest "(he)((.*)(o))" hello
  2. start = 0
  3. end   = 5
  4. group = hello

  5. groupCount = 4
  6.         [0]: start = 0
  7.         [0]: end   = 5
  8.         [0]: group = hello

  9.         [1]: start = 0
  10.         [1]: end   = 2
  11.         [1]: group = he

  12.         [2]: start = 2
  13.         [2]: end   = 5
  14.         [2]: group = llo

  15.         [3]: start = 2
  16.         [3]: end   = 4
  17.         [3]: group = ll

  18.         [4]: start = 4
  19.         [4]: end   = 5
  20.         [4]: group = o
复制代码
从这里例子里,可以很容易看到group的定义。注意这里.group()方法的返回值是字符串,
是匹配后得到的字符串。

这样下来,MatchResult就已经没啥可说的了。
回复  

使用道具 举报

4#
 楼主| 发表于 30-6-2009 18:15:48 | 只看该作者

三种匹配方式

在上面的例子中,我们已经初步用过.find()这种匹配方式。

一共有三种匹配方式:
1. matches()
2. lookingAt()
3. find()
无论哪一种匹配方式,如果返回true,则表示可以通过上面的MatchResult的相应方法进行
信息读取。而反过来,如果之前没有进行匹配操作就试图执行MatchResult相关的操作,
会得到IllegalStateException(Thread多次start()也会出这个Exception)

matches
完全匹配。就是从头到尾的匹配一次。
源代码为:
  1. 501     public boolean matches() {
  2. 502         return match(from, ENDANCHOR);
  3. 503     }
复制代码
lookingAt
和matches稍有不同,lookingAt()依然从头开始匹配,但只匹配前面部分,
不需要匹配整个字符串。(其实是region)
  1. 578     public boolean lookingAt() {
  2. 579         return match(from, NOANCHOR);
  3. 580     }
复制代码
find
find顺次的查找匹配串,直接字符串结束或找到匹配为止。
  1. 520     public boolean find() {
  2. 521         int nextSearchIndex = last;
  3. 522         if (nextSearchIndex == first)
  4. 523             nextSearchIndex++;
  5. 524
  6. 525         // If next search starts before region, start it at region
  7. 526         if (nextSearchIndex < from)
  8. 527             nextSearchIndex = from;
  9. 528
  10. 529         // If next search starts beyond region then it fails
  11. 530         if (nextSearchIndex > to) {
  12. 531             for (int i = 0; i < groups.length; i++)
  13. 532                 groups[i] = -1;
  14. 533             return false;
  15. 534         }
  16. 535         return search(nextSearchIndex);
  17. 536     }
复制代码
这里可以看出,Mather.java用from/to来定义当前region的边界,如果查找越界了,
就返回false,否则就调用search进行查找。

和前两种方式调用match()不同,这里调用的是search,具体怎样实现我在这里就不
理它了,来去都是正则文法匹配,记不住还不能查书吗?

评分

参与人数 1威望 +30 收起 理由
ubuntuhk + 30 支持原创,支持正则表达式:)!

查看全部评分

回复  

使用道具 举报

5#
发表于 4-7-2009 01:17:36 | 只看该作者

回复 #4 key 的帖子

帮顶,不用Java好多年。
回复  

使用道具 举报

6#
 楼主| 发表于 4-7-2009 01:36:58 | 只看该作者
回复  

使用道具 举报

7#
 楼主| 发表于 4-7-2009 09:33:46 | 只看该作者

原创。。。

本帖最近评分记录
ubuntuhk 威望         +30 支持原创,支持正则表达式:)! 4-7-2009 01:50


我写东西很少不是原创的,哈哈。。。你一个个去支持吧,哈哈。。
即使是参考了别人的东西,内容和代码都是我重新写的
没办法,打英文我快,打中文我更快。。。
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+11, 6-3-2025 05:17 , Processed in 0.022354 second(s), 23 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表