Java中的正则表达式-包含embedded flags(转)

 

JDK 1.4提供了内建的正则表达式支持,相应的,String类也提供了许多与正则表达式有关的方法,例如matches、replaceAll和split方法,为日常应用提供了许多便利。
在工作中,我发现,掌握一些不常用的技巧,往往可以极大地提高效率,以下是我总结的一点经验:

1.合理利用embedded flag。
某些时候,我们会遇到这样的情况:
要求匹配一个字符串,例如abcdefg,其中abc必须为小写,def大小写无所谓,g为小写。
此类问题,通常的做法是这样生成一个Pattern:

Pattern p = Pattern.compile("abcdefg", Pattern.CASE_INSENSITIVE);然而,此时这样做不能满足我们的要求,此时可借助embedded flag。

查阅JDK文档,在Special construts(non-capturing)中可以看到:
(?idmsux-idmsux)       Nothing, but turns match flags on - off
(?idmsux-idmsux:X)        X, as a non-capturing group with the given flags on - off

其中,各个字母的含义为
embedded                      flags construction flags meanings
i                Pattern.CASE_INSENSITIVE Enables case-insensitive matching.
d               Pattern.UNIX_LINES Enables Unix lines mode.
m              Pattern.MULTILINE Enables multi line mode.
s               Pattern.DOTALL Enables "." to match line terminators.
u               Pattern.UNICODE_CASE Enables Unicode-aware case folding.
x               Pattern.COMMENTS Permits white space and comments in the pattern.
---             Pattern.CANON_EQ Enables canonical equivalence.


如此一来,我们就可写出符合要求的Pattern字符串:

Pattern p = Pattern.compile("abc(?i)def(?-i)g");

在这个Pattern中,我们在def子串之前设置了CASE_INSENSITIVE为on,在之后将其设置为off,保证了abcg都为小写,而def可以为大小写。

有人可能会认为,不需要那么复杂,直接写

Pattern p = Pattern.compile("abc[dD][eE][fF]g");

也可以解决问题。

确实,在这种情况下,两种办法的效果是一样。然而,有时候我们并不能事先确定字符串的内容(例如,需要从HTML代码中提取一个结点,也就是一对tag之间的内容,而tag是运行时动态决定的),此时只能借助match flag。

如果只修改了一次match flag,那么它的作用范围将从此处开始,一直到延伸到Pattern末尾,也就是说下面两个Pattern:

Pattern p1 = Pattern.compile("(?is)abcdefg");

Pattern p2 = Pattern.compile("abcdefg", PATTERN.CASE_INSENSITIVE | Pattern.DOTALL);

它们的作用是一样的。

match flag可以组合使用,例如(?is)表示大小写不敏感,同时.可以用来匹配换行符;同时,match flag也可以与外部的int flag同时使用,例如:

Pattern pattern = Pattern.compile( "(?-i:[A-Z])[A-Z]*" , Pattern.CASE_INSENSITIVE);

代表第一个字符为大写英文字母的单词,需要注意的是,此时第一个字符处于non-capturing group中,因此在group count时要格外注意。

一般情况下,我们都是调用String类的matches方法验证字符串的合法性,遇上复杂的情况,合理使用match flag会让我们事半功倍。

2.利用split
在引入split之前,如果需要分割字符串,必须使用StringTokenizer类,使用非常麻烦,而且缺乏灵活性(因为不容许分割的字符串有多种选择),JDK1.4引入了split方法,容许利用正则表达式分割字符串,非常方便。

以前我们也许需要这样写:

StringTokenizer st = new StringTokenizer("this is a test");
while (st.hasMoreTokens()){
    System.out.println(st.nextToken());
}

 

现在只需要这样:

String[] words = "this is a test".split("\\s");
for(string/ s : words){ 
System.
out.println(s);
}

 

3.关于replaceAll
JDK1.4为String引入了replaceAll方法,可以方便地进行字符串替换

String source = "abcdefg";

String result = source.replaceAll("bcd", "BCD");

System.out.println("result is: " + result);

得到的结果是
aBCDefg

需要注意的是,replaceAll的两个参数,都是正则表达式,按照JDK的文档

str.replaceAll(regex, repl)等价于

Pattern.compile(regex).matcher(str).replaceAll(repl)
某些情况下,我们需要在替换的同时用到back reference,也就是说,被替换的内容是不确定的,而替换的结果又是与这些被替换内容相关的,此时就会发现使用正则表达式的好处了。
例如,某段文本中出现许多“班级-学号”(例如,172-04)的字符串,需要更改为“学号-班级”(04-172)的格式,我们可以在替换结果中利用$符号对被替换内容中的部分进行back reference:

String source = "172-04";
System.out.println(source.replaceAll(
"(\\d+)-(\\d+)","$2-$1");

 

结果为
04-172 

 

posted @ 2010-01-23 09:39  Fskjb  阅读(956)  评论(0编辑  收藏  举报
年年行好运