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方法,容许利用正则表达式分割字符串,非常方便。
以前我们也许需要这样写:
while (st.hasMoreTokens()){
System.out.println(st.nextToken());
}
现在只需要这样:
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:
System.out.println(source.replaceAll("(\\d+)-(\\d+)","$2-$1");
结果为
04-172