FreeOZ论坛

标题: Java文本解析小记——小key出品 [打印本页]

作者: key    时间: 30-6-2009 16:18
标题: Java文本解析小记——小key出品
曾几何时我们小心翼翼地用BufferedReader.readLine()一行一行地读入文本,
用StringTokenizer.nextToken()一个个地取出字句,进行手动的分析。

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

本文的目的就是和大家一起探讨一下Java文本解析的应用。请明白,这里不是教程,
我只是按自己的喜好,写一些自己的个人观点,欢迎批评。
作者: key    时间: 30-6-2009 17:09
标题: 正则三步曲
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类的全部了。
作者: key    时间: 30-6-2009 17:50
标题: 渐入佳境: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就已经没啥可说的了。
作者: key    时间: 30-6-2009 18:15
标题: 三种匹配方式
在上面的例子中,我们已经初步用过.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,具体怎样实现我在这里就不
理它了,来去都是正则文法匹配,记不住还不能查书吗?
作者: 刘叔    时间: 4-7-2009 01:17
标题: 回复 #4 key 的帖子
帮顶,不用Java好多年。
作者: key    时间: 4-7-2009 01:36
原帖由 liuhuey 于 4-7-2009 00:17 发表
帮顶,不用Java好多年。


多谢
作者: key    时间: 4-7-2009 09:33
标题: 原创。。。
本帖最近评分记录
ubuntuhk 威望         +30 支持原创,支持正则表达式:)! 4-7-2009 01:50


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




欢迎光临 FreeOZ论坛 (https://www.freeoz.org/bbs/) Powered by Discuz! X3.2