转自:http://gstarwd.iteye.com/category/91108?show_full=true
想必很多人都对正则表达式都头疼。今天,我以我的认识,加上网上一些文章,希望用常人都可以理解的表达方式来和大家分享学习经验。
开篇,还是得说说 ^ 和 $ 他们是分别用来匹配字符串的开始和结束,以下分别举例说明:
"^The": 开头一定要有"The"字符串;
"of despair$": 结尾一定要有"of despair" 的字符串;
那么,
"^abc$": 就是要求以abc开头和以abc结尾的字符串,实际上是只有abc匹配。
"notice": 匹配包含notice的字符串。
你可以看见如果你没有用我们提到的两个字符(最后一个例子),就是说 模式(正则表达式) 可以出现在被检验字符串的任何地方,你没有把他锁定到两边。
接着,说说 '*', '+',和 '?',
他们用来表示一个字符可以出现的次数或者顺序. 他们分别表示:
"zero or more"相当于{0,},
"one or more"相当于{1,},
"zero or one."相当于{0,1}, 这里是一些例子:
"ab*": 和ab{0,}同义,匹配以a开头,后面可以接0个或者N个b组成的字符串("a", "ab", "abbb", 等);
"ab+": 和ab{1,}同义,同上条一样,但最少要有一个b存在 ("ab", "abbb", 等.);
"ab?":和ab{0,1}同义,可以没有或者只有一个b;
"a?b+$": 匹配以一个或者0个a再加上一个以上的b结尾的字符串.
要点, '*', '+',和 '?'只管它前面那个字符.
你也可以在大括号里面限制字符出现的个数,比如
"ab{2}": 要求a后面一定要跟两个b(一个也不能少)("abb");
"ab{2,}": 要求a后面一定要有两个或者两个以上b(如"abb", "abbbb", 等.);
"ab{3,5}": 要求a后面可以有2-5个b("abbb", "abbbb", or "abbbbb").
现在我们把一定几个字符放到小括号里,比如:
"a( bc) *": 匹配 a 后面跟0个或者一个"bc";
"a( bc) {1,5}": 一个到5个 "bc."
还有一个字符 '│', 相当于OR 操作:
"hi│hello": 匹配含有"hi" 或者 "hello" 的 字符串;
"(b│cd)ef": 匹配含有 "bef" 或者 "cdef"的字符串;
"(a│b)*c": 匹配含有这样多个(包括0个)a或b,后面跟一个c的字符串;
一个点('.' )可以代表所有的单一字符,不包括"/n"
如果,要匹配包括"/n"在内的所有单个字符,怎么办?
对了,用'[/n.]'这种模式.
"a.[0-9] ": 一个a加一个字符再加一个 0到9的数字
"^.{3}$": 三个任意字符结尾 .
中括号括住的内容只匹配一个单一的字符
"[ab]": 匹配单个的 a 或者 b ( 和 "a│b" 一样);
"[a-d]": 匹配'a' 到'd'的单个字符 (和"a│b│c│d" 还有 "[abcd]"效果一样); 一般我们都用[a-zA-Z]来指定字符为一个大小写英文
"^[a-zA-Z]": 匹配以大小写字母开头的字符串
"[0-9]%": 匹配含有 形如 x% 的字符串
",[a-zA-Z0-9]$": 匹配以逗号再加一个数字或字母结尾的字符串
你也可以把你不想要 得字符列在中括号里,你只需要在总括号里面使用'^' 作为开头 "%[^a-zA-Z]%" 匹配含有两个百分号里面有一个非字母 的字符串.
要点:^用在中括号开头的时候,就表示排除括号里的字符。为了PHP能够解释,你必须在这些字符面前后加'',并且将一些字符转义.
不要忘记在中括号里面的字符是这条规路的例外?在中括号里面, 所有的特殊字符,包括(''), 都将失去他们的特殊性质 "[*/+?{}.]"匹配含有这些字符的字符串.
还有,正如regx的手册告诉我们: "如果列表里含有 ']', 最好把它作为列表里的第一个字符(可能跟在'^'后面). 如果含有'-', 最好把它放在最前面或者最后面, or 或者一个范围的第二个结束点[a-d-0-9]中间的‘-’将有效.
看了上面的例子,你对{n,m}应该理解了吧.要注意的是,n和m都不能为负整数, 而且n总是小于m. 这样,才能 最少匹配n次且最多匹配m次. 如"p{1,5}"将匹配 "pvpppppp"中的前五个p.
下面说说以/开头的
/b 书上说他是用来匹配一个单词边界,就是...比如've/b',可以匹配love里的ve而不匹配very里有ve
/B 正好和上面的/b相反.例子我就不举了
.....突然想起来....可以到http://www.phpv .net /article.php/251 看看其它用/ 开头的语法
好,我们来做个应用:
如何构建一个模式来匹配 货币数量 的输入
构建一个匹配模式去检查输入的信息是否为一个表示money的数字。我们认为一个表示money的数量有四种方式: "10000.00" 和 "10,000.00",或者没有小数部分, "10000" and "10,000". 现在让我们开始构建这个匹配模式:
^[1-9][0-9]*$
这是所变量必须以非0的数字开头.但这也意味着 单一的 "0" 也不能通过测试. 以下是解决的方法:
^(0│[1-9][0-9]*)$
"只有0和不以0开头的数字与之匹配",我们也可以允许一个负号在数字之前:
^(0│-?[1-9][0-9]*)$
这就是: "0 或者 一个以0开头 且可能 有一个负号在前面的数字." 好了,现在让我们别那么严谨,允许以0开头.现在让我们放弃 负号 , 因为我们在表示钱币的时候并不需要用到. 我们现在指定 模式 用来匹配小数部分:
^[0-9]+(/.[0-9]+)?$
这暗示匹配的字符串必须最少以一个阿拉伯数字开头. 但是注意,在上面模式中 "10." 是不匹配的, 只有 "10" 和 "10.2" 才可以. (你知道为什么吗)
^[0-9]+(/.[0-9]{2})?$
我们上面指定小数点后面必须有两位小数.如果你认为这样太苛刻,你可以改成:
^[0-9]+(/.[0-9]{1,2})?$
这将允许小数点后面有一到两个字符. 现在我们加上用来增加可读性的逗号(每隔三位), 我们可以这样表示:
^[0-9]{1,3}(,[0-9]{3})*(/.[0-9]{1,2})?$
不要忘记 '+' 可以被 '*' 替代 如果你想允许空白字符串被输入话 (为什么?). 也不要忘记反斜杆 ’/’ 在php字符串中可能会出现错误 (很普遍的错误).
现在,我们已经可以确认字符串了, 我们现在把所有逗号都去掉 str_replace(",", "", $money) 然后在把类型看成 double然后我们就可以通过他做数学计算了.
再来一个:
构造检查email的正则表达式
在一个完整的email地址中有三个部分:
1. 用户名 (在 '@' 左边的一切),
2.'@',
3. 服务器名(就是剩下那部分).
用户名可以含有大小写字母阿拉伯数字,句号 ('.'), 减号('-'), and 下划线 ('_'). 服务器名字也是符合这个规则,当然下划线除外.
现在, 用户名的开始和结束都不能是句点. 服务器也是这样. 还有你不能有两个连续的句点他们之间至少存在一个字符,好现在我们来看一下怎么为用户名写一个匹配模式:
^[_a-zA-Z0-9-]+$
现在还不能允许句号的存在. 我们把它加上:
^[_a-zA-Z0-9-]+(/.[_a-zA-Z0-9-]+)*$
上面的意思就是说: "以至少一个规范字符(除了.)开头,后面跟着0个或者多个以点开始的字符串."
简单化一点, 我们可以用 eregi()取代 ereg().eregi()对大小写不敏感, 我们就不需要指定两个范围 "a-z" 和 "A-Z" ? 只需要指定一个就可以了:
^[_a-z0-9-]+(/.[_a-z0-9-]+)*$
后面的服务器名字也是一样,但要去掉下划线:
^[a-z0-9-]+(/.[a-z0-9-]+)*$
好. 现在只需要用”@”把两部分连接:
^[_a-z0-9-]+(/.[_a-z0-9-]+)*@[a-z0-9-]+(/.[a-z0-9-]+)*$
这就是完整的email认证匹配模式了,只需要调用
eregi(‘^[_a-z0-9-]+(/.[_a-z0-9-]+)*@[a-z0-9-]+(/.[a-z0-9-]+)*$ ’,$eamil)
就可以得到是否为email了.
正则表达式的其他用法
提取字符串
ereg() and eregi() 有一个特性是允许用户通过正则表达式去提取字符串的一部分(具体用法你可以阅读手册). 比如说,我们想从 path /URL 提取文件名 ? 下面的代码就是你需要:
ereg("([^///]*)$", $pathOrUrl, $regs);
echo $regs[1];
高级的代换
ereg_replace() 和 eregi_replace()也是非常有用的: 假如我们想把所有的间隔负号都替换成逗号:
ereg_replace("[ /n/r/t]+", ",", trim($str));
最后,我把另一串检查EMAIL的正则表达式让看文章的你来分析一下.
"^[-!#$%&/'*+//./0-9=?A-Z^_`a-z{|}~]+'.'@'.'[-!#$%&/'*+///0-9=?A-Z^_`a-z{|}~]+/.'.'[-!#$%&/'*+//./0-9=?A-Z^_`a-z{|}~]+$"
如果能方便的读懂,那这篇文章的目的就达到了.
java核心技术 正则表达式
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* This program displays all URLs in a web page by matching a regular expression that describes the
* <a href=...> HTML tag. Start the program as <br>
* java HrefMatch URL
* @version 1.01 2004-06-04
* @author Cay Horstmann
*/
public class HrefMatch
{
public static void main(String[] args)
{
try
{
// get URL string from command line or use default
String urlString;
if (args.length > 0) urlString = args[0];
else urlString = " http://java.sun.com ";
// open reader for URL
InputStreamReader in = new InputStreamReader(new URL(urlString).openStream());
// read contents into string builder
StringBuilder input = new StringBuilder();
int ch;
while ((ch = in.read()) != -1)
input.append((char) ch);
// search for all occurrences of pattern
String patternString = "<a//s+href//s*=//s*(/"[^/"]*/"|[^//s>])//s*>";
Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(input);
while (matcher.find())
{
int start = matcher.start();
int end = matcher.end();
String match = input.substring(start, end);
System.out.println(match);
}
}
catch (IOException e)
{
e.printStackTrace();
}
catch (PatternSyntaxException e)
{
e.printStackTrace();
}
}
}
字符串处理是许多程序中非常重要的一部分,它们可以用于文本显示,数据表示,查找键和很多目的.在Unix下,用户可以使用正则表达式的强健功能实现
这些目的,从Java1.4起,Java核心API就引入了java.util.regex程序包,它是一种有价值的基础工具,可以用于很多类型的文本处理,如匹配,搜索,提取
和分析结构化内容.
java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包。它包括两个类:Pattern和Matcher.
Pattern是一个正则表达式经编译后的表现模式。 在java中,通过适当命名的Pattern类可以容易确定String是否匹配某种模式.模式可以象匹配某个特
定的String那样简单,也可以很复杂,需要采用分组和字符类,如空白,数字,字母或控制符.因为Java字符串基于统一字符编码(Unicode),正则表达式也
适用于国际化的应用程序.
Pattern类的方法简述
方法 说明
static Pettern compile(String regex,int flag) 编译模式,参数regex表示输入的正则表达式,flag表示模式类型(Pattern.CASE_INSENSITIVE 表示
不区分大小写)
Matcher match(CharSequence input) 获取匹配器,input时输入的待处理的字符串
static boolean matches(String regex, CharSequence input) 快速的匹配调用,直接根据输入的模式regex匹配input
String[] split(CharSequence input,int limit) 分隔字符串input,limit参数可以限制分隔的次数
Matcher 一个Matcher对象是一个状态机器,它依据Pattern对象做为匹配模式对字符串展开匹配检查。首先一个Pattern实例订制了一个所用语法与
PERL的类似的正则表达式经编译后的模式,然后一个Matcher实例在这个给定的Pattern实例的模式控制下进行字符串的匹配工作。
Matcher类的方法简述
方法 说明
boolean matches() 对整个输入字符串进行模式匹配.
boolean lookingAt() 从输入字符串的开始处进行模式匹配
boolean find(int start) 从start处开始匹配模式
int groupCount() 返回匹配后的分组数目
String replaceAll(String replacement) 用给定的replacement全部替代匹配的部分
String repalceFirst(String replacement) 用给定的replacement替代第一次匹配的部分
Matcher appendReplacement(StringBuffer sb,String replacement) 根据模式用replacement替换相应内容,并将匹配的结果添加到sb当前位置之后
StringBuffer appendTail(StringBuffer sb) 将输入序列中匹配之后的末尾字串添加到sb当前位置之后.
正则表达式中常见通配符:
对于单字符串比较而言,使用正则表达式没有什么优势.Regex的真正强大之处在于体现在包括字符类和量词(*,+,?)的更复杂的模式上.
字符类包括:
/d 数字
/D 非数字
/w 单字字符(0-9,A-Z,a-z)
/W 非单字字符
/s 空白(空格符,换行符,回车符,制表符)
/S 非空白
[] 由方括号内的一个字符列表创建的自定义字符类
. 匹配任何单个字符
下面的字符将用于控制将一个子模式应用到匹配次数的过程.
? 重复前面的子模式0次到一次
* 重复前面的子模式0次或多次
+ 重复前面的子模式一次到多次
以下是实例部分:
实例一:
正则式是最简单的能准确匹配一个给定String的模式,模式与要匹配的文本是等价的.静态的Pattern.matches方法用于比较一个String是否匹配一个给
定模式.例程如下:
String data="java";
boolean result=Pattern.matches("java",data);
实例二:
String[] dataArr = { "moon", "mon", "moon", "mono" };
for (String str : dataArr) {
String patternStr="m(o+)n";
boolean result = Pattern.matches(patternStr, str);
if (result) {
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
模式是”m(o+)n”,它表示mn中间的o可以重复一次或多次,因此moon,mon,mooon能匹配成功,而mono在n后多了一个o,和模式匹配不上.
注:
+表示一次或多次;?表示0次或一次;*表示0次或多次.
实例三:
String[] dataArr = { "ban", "ben", "bin", "bon" ,"bun","byn","baen"};
for (String str : dataArr) {
String patternStr="b[aeiou]n";
boolean result = Pattern.matches(patternStr, str);
if (result) {
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
注:方括号中只允许的单个字符,模式“b[aeiou]n”指定,只有以b开头,n结尾,中间是a,e,i,o,u中任意一个的才能匹配上,所以数组的前五个可以匹配,
后两个元素无法匹配.
方括号[]表示只有其中指定的字符才能匹配.
实例四:
String[] dataArr = { "been", "bean", "boon", "buin" ,"bynn"};
for (String str : dataArr) {
String patternStr="b(ee|ea|oo)n";
boolean result = Pattern.matches(patternStr, str);
if (result) {
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
如果需要匹配多个字符,那么[]就不能用上了,这里我们可以用()加上|来代替,()表示一组,|表示或的关系,模式b(ee|ea|oo)n就能匹配been,bean,boon
等.
因此前三个能匹配上,而后两个不能.
实例五:
String[] dataArr = { "1", "10", "101", "1010" ,"100+"};
for (String str : dataArr) {
String patternStr="//d +";
boolean result = Pattern.matches(patternStr, str);
if (result) {
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
注:从前面可以知道,//d表示的是数字,而+表示一次或多次,所以模式//d +就表示一位或多位数字.
因此前四个能匹配上,最后一个因为+号是非数字字符而匹配不上.
实例六:
String[] dataArr = { "a100", "b20", "c30", "df10000" ,"gh0t"};
for (String str : dataArr) {
String patternStr="//w+//d +";
boolean result = Pattern.matches(patternStr, str);
if (result) {
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
模式//w+//d +表示的是以多个单字字符开头,多个数字结尾的字符串,因此前四个能匹配上,最后一个因为数字后还含有单字字符而不能匹配.
实例七:
String str="薪水,职位 姓名;年龄 性别";
String[] dataArr =str.split("[,//s;]");
for (String strTmp : dataArr) {
System.out.println(strTmp);
}
String类的split函数支持正则表达式,上例中模式能匹配”,”,单个空格,”;”中的一个,split函数能把它们中任意一个当作分隔符,将一个字符串劈
分成字符串数组.
实例八:
String str="2007年12月11日";
Pattern p = Pattern.compile("[年月日]");
String[] dataArr =p.split(str);
for (String strTmp : dataArr) {
System.out.println(strTmp);
}
Pattern是一个正则表达式经编译后的表现模式 ,它的split方法能有效劈分字符串.
注意其和String.split()使用上的不同.
实例九:
String str="10元 1000人民币 10000元 100000RMB";
str=str.replaceAll("(//d +)(元|人民币|RMB)", "$1¥");
System.out.println(str);
上例中,模式“(//d +)(元|人民币|RMB)”按括号分成了两组,第一组//d +匹配单个或多个数字,第二组匹配元,人民币,RMB中的任意一个,替换部分$1表
示第一个组匹配的部分不变,其余组替换成¥.
替换后的str为¥10 ¥1000 ¥10000 ¥100000
实例十:
Pattern p = Pattern.compile("m(o+)n",Pattern.CASE_INSENSITIVE);
// 用Pattern类的matcher()方法生成一个Matcher对象
Matcher m = p.matcher("moon mooon Mon mooooon Mooon");
StringBuffer sb = new StringBuffer();
// 使用find()方法查找第一个匹配的对象
boolean result = m.find();
// 使用循环找出模式匹配的内容替换之,再将内容加到sb里
while (result) {
m.appendReplacement(sb, "moon");
result = m.find();
}
// 最后调用appendTail()方法将最后一次匹配后的剩余字符串加到sb里;
m.appendTail(sb);
System.out.println("替换后内容是" + sb.toString());
实例十一:
除了用+表示一次或多次,*表示0次或多次,?表示0次或一次外,还可以用{}来指定精确指定出现的次数,X{2,5}表示X最少出现2次,最多出现5次;X{2,}表
示X最少出现2次,多则不限;X{5}表示X只精确的出现5次.
例程:
String[] dataArr = { "google", "gooogle", "gooooogle", "goooooogle","ggle"};
for (String str : dataArr) {
String patternStr = "g(o{2,5})gle";
boolean result = Pattern.matches(patternStr, str);
if (result) {
System.out.println("字符串" + str + "匹配模式" + patternStr + "成功");
} else {
System.out.println("字符串" + str + "匹配模式" + patternStr + "失败");
}
}
实例十二:
-表示从..到…,如[a-e]等同于[abcde]
String[] dataArr = { "Tan", "Tbn", "Tcn", "Ton","Twn"};
for (String str : dataArr) {
String regex = "T[a-c]n";
boolean result = Pattern.matches(regex, str);
if (result) {
System.out.println("字符串" + str + "匹配模式" + regex + "成功");
} else {
System.out.println("字符串" + str + "匹配模式" + regex + "失败");
}
}
实例十三:不区分大小写匹配.
正则表达式默认都是区分大小写的,使用了Pattern.CASE_INSENSITIVE则不对大小写进行区分.
String patternStr="ab";
Pattern pattern=Pattern.compile(patternStr, Pattern.CASE_INSENSITIVE);
String[] dataArr = { "ab", "Ab", "AB"};
for (String str : dataArr) {
Matcher matcher=pattern.matcher(str);
if(matcher.find()){
System.out.println("字符串" + str + "匹配模式" + patternStr + "成功");
}
}
实例十四:使用正则表达式劈分字符串.
注意这里要把复杂的模式写在前面,否则简单模式会先匹配上.
String input="职务=GM 薪水=50000 , 姓名=职业经理人 ; 性别=男 年龄=45 ";
String patternStr="(//s*,//s*)|(//s*;//s*)|(//s +)";
Pattern pattern=Pattern.compile(patternStr);
String[] dataArr=pattern.split(input);
for (String str : dataArr) {
System.out.println(str);
}
实例十五:解析正则表达式中的文字,//1对应第一个小括号括起来的group1.
String regex="<(//w+)>(//w+)<///1 >";
Pattern pattern=Pattern.compile(regex);
String input="<name>Bill</name><salary>50000</salary><title>GM</title>";
Matcher matcher=pattern.matcher(input);
while(matcher.find()){
System.out.println(matcher.group(2));
}
实例十六:将单词数字混合的字符串的单词部分大写.
String regex="([a-zA-Z]+[0-9]+)";
Pattern pattern=Pattern.compile(regex);
String input="age45 salary500000 50000 title";
Matcher matcher=pattern.matcher(input);
StringBuffer sb=new StringBuffer();
while(matcher.find()){
String replacement=matcher.group(1).toUpperCase();
matcher.appendReplacement(sb, replacement);
}
matcher.appendTail(sb);
System.out.println("替换完的字串为"+sb.toString());
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author ZhuYuanXi
* 正则表达式的使用
*/
public class Test {
/**
* 字符串匹配
* @param expression 正则表达式字符串
* @param text 要进行匹配的字符串
*/
private static void matchingText(String expression, String text) {
Pattern p = Pattern.compile(expression); // 正则表达式
Matcher m = p.matcher(text); // 操作的字符串
boolean b = m.matches();
System.out.println(b);
}
/**
* 字符串替换
* @param expression 正则表达式字符串
* @param text 要进行替换操作的字符串
* @param str 要替换的字符串
*/
private static void replaceText(String expression, String text, String str) {
Pattern p = Pattern.compile(expression); // 正则表达式
Matcher m = p.matcher(text); // 操作的字符串
String s = m.replaceAll(str);
System.out.println(s);
}
/**
* 字符串查找
* @param expression 正则表达式字符串
* @param text 要进行查找操作的字符串
* @param str 要查找的字符串
*/
private static void findText(String expression, String text, String str) {
Pattern p = Pattern.compile(expression); // 正则表达式
Matcher m = p.matcher(text); // 操作的字符串
StringBuffer sb = new StringBuffer();
int i = 0;
while (m.find()) {
m.appendReplacement(sb, str);
i++;
}
m.appendTail(sb);
System.out.println(sb.toString());
System.out.println(i);
}
/**
* 字符串分割
* @param expression 正则表达式字符串
* @param text 要进行分割操作的字符串
*/
private static void splitText(String expression, String text) {
Pattern p = Pattern.compile(expression); // 正则表达式
String[] a = p.split(text);
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
/**
* @param args
*/
public static void main(String[] args) {
matchingText("^card_([_0-9a-zA-Z]+[_0-9a-zA-Z-/]*[_0-9a-zA-Z]+)/?.shtml$", "card_1020000000.shtml");
// 字符串匹配,这是不符合的
matchingText("a*b", "baaaaab");
// 字符串匹配,这是符合的
matchingText("a*b", "aaaaab");
// 字符串匹配,通用匹配
matchingText("^([_0-9a-zA-Z]+[_0-9a-zA-Z-/]*[_0-9a-zA-Z]+)/?", "aaaaab");
// 字符串替换
replaceText("ab", "aaaaab", "d");
replaceText("a*b", "aaaaab", "d");
replaceText("a*b", "caaaaab", "d");
// 字符串查找
findText("cat", "one cat two cats in the yard", "dog");
findText("(fds){2,}", "dsa da fdsfds aaafdsafds aaf", "dog");
// 字符串分割
splitText("a+", "caaaaaat");
splitText("a+", "c aa aaaa t");
splitText(" +", "c aa aaaa t");
splitText("//+", "dsafasdfdsafsda+dsagfasdfa+sdafds");
}
}
正则表达式用来指定字符串模式。当你需要定位匹配某种模式的字符串时就可以使用正则表达式。例如,我们下面的一个例程就是在一个HTML文件中通过查找字符串模式<a href="...">来定位所有的超链接。
当然,为了指定一种模式,使用...这种记号是不够精确的。你需要精确地指定什么样的字符
排列是一个合法的匹配。当描述某种模式时,你需要使用一种特殊的语法。
这里有一个简单例子。正则表达式
[Jj]ava.+
匹配下列形式的任何字符串:
学点Java正则表达式 |
TCP存活定时器(TCP Keepalive Timer) |
TCP持续定时器(TCP Persist Timer) |
uClinux在S3C44B0X上的移植 |
- 首字母是J或j
- 后续的三个字母是ava
- 字符串的剩余部分由一个或多个任意字符组成
例如,字符串“javaness”匹配这个特殊的正则表达式,但是字符串“Core Java”却不匹配。
如你所见,你需要了解一点语法来理解正则表达式的含意。幸运的是对于大多数的用途,使用少量的简单构造(straightforward constructs)就已足够。
- 字符类是可选自符的集合,用‘[’封装,比如[Jj],[0-9],[A-Za-z]或[^0-9]。这里的-表示范围(Unicode落在两个边界之间的所有字符),^表示求补(指定字符外的所有字符)。
- 有许多预定以的字符类,像/d(数字)或/p{Sc}(Unicode货币符号),见表12-8和12-9。
- 大多数字符与它们自身匹配,像上例中的ava字符。
- 符号.匹配任何字符(可能行终止符(line terminators)除外,这依赖于标识设置(flag settings))
- /用作转义符,比如/.匹配一个句点,//匹配一个反斜杠。
- ^和$分别匹配行头和行尾
- 如果X和Y都是正则表达式,则XY表示“X的匹配后面跟着Y的匹配”。X|Y表示“任何X或Y的匹配”
- 可以将量词(quantifier)用到表达式中,X+ 表示X重复1次或多次,X* 表示X重复0次或多次,X? 表示X重复0次或1次
- 默认地,一个量词总是与使总体成功匹配的最长的可能重复匹配。可以加上后缀?(称为reluctant或stingy 匹配,用以匹配最小的重复数),和+(称为possessive或贪婪匹配,用以即使在总体匹配失败的情况下也匹配最大的重复数)来更改这种属性。
- 可以使用分组来定义子表达式。将分组封装在()中,如([+-]?)([0-9]+)。然后你可以让模式匹配符(the pattern matcher)返回每个分组的匹配,或者使用/n来回引分组(refer back to a group with /n),其中n是组号(以/1起始)
这里有一个稍微有点复杂却又很有用的正则表达式--它用来描述十进制和十六进制的整数。
[+-]?[0-9]+|0[Xx][0-9A-Fa-f]+
不幸的是,在使用正则表达式的各种程序和库之间,它的语法还没有完全标准化。对基本的构造上已达成了共识,但在细节方面有许多令人“抓狂”的区别(many maddening differences)。Java的正则表达式类使用了与Perl语言类似的语法,但也不尽相同。表12-8显示了Java语法的所有正则表达式构造。要了解更多关于正则表达式的信息,请参考Pattern类的API文档,或者Jeffrey E. F. Friedl的著作《Mastering Regular Expressions》(O'Reilly and Associates, 1997)(刚去第二书店查了一下,东南大学出版社已经引入了其第二版,影印)
表12-8 正则表达式语法
语法 解释
字符
c 字符c
/unnnn, /xnn, /0n, /0nn, /0nnn 带有十六或八进制值的代码单元
/cc 控制字符中出现的相应字符c
字符类
[C1C2. . .] C1、C2……中的任何字符。Ci可以是字符,字符范围(C1-C2)或者字符类。
[^. . .] 字符类的补集
[ . . . && . . .] 两个字符类的交集
预定义字符类
. 除行终止符外的任何字符(如果DOTALL标志置位,则表示任何字符)
/d 数字[0-9]
/D 非数字[^0-9]
/s 空白字符[/t/n/r/f/x0B]
/S 非空白字符
/w 单词字符[a-zA-Z0-9_]
/W 非单词字符
/p{name} 一个指定的字符类,见表12-9
/P{name} 指定字符类的补集
边界匹配符
^ $ 输入的开头和结尾(在多行模式(multiline mode)下是行的开头和结尾)
/b 单词边界
/B 非单词边界
/A 输入的开头
/z 输入的结尾
/Z 除最后行终止符之外的输入结尾
/G 上个匹配的结尾
量词
X? 可选的X(即X可能出现,也可能不出现)
X* X,可以重复0次或多次
X+ X,可以重复1次或多次
X{n} X{n,} X{n,m} X重复n次,至少重复n次,重复n到m次
量词后缀
? 设默认(贪婪)匹配为reluctant匹配
+ 设默认(贪婪)匹配为possessive匹配
集合操作
XY X的匹配后面跟着Y的匹配
X|Y X或Y的匹配
分组
(X) 匹配X并且在一个自动计数的分组中捕获它
/n 与第n个分组的匹配
转义
/c 字符c(必须不是字母)
/Q.../E 逐字地引用...
(?...) 特殊构造,看Pattern类的API
正则表达式的最简单使用是测试一个特殊的字符串是否与之匹配。这里有一个Java写的测试程序。首先从表示正则表达式的字符串构造一个Pattern对象。然后从该模式获得一个Matcher对象,并且调用它的matches()方法:
表12.9 预定义的字符类名(Predefined Character Class Names)
Lower 小写的ASII字符[a-z]
Upper 大写的ASCII字符[A-Z]
Alpha ASCII字母[A-Za-z]
Digit ASCII 数字 [0-9]
Alnum ASCII 字母或数字[A-Za-z0-9]
Xdigit 十六进制数字[0-9A-Fa-f]
Print or Graph 可打印的ASCII字符[/x21-/x7E]
Punct 非字母或数字ASCII [/p{Print}&&/P{Alnum}]
ASCII 所有ASCII字符 [/x00-/x7F]
Cntrl ASCII控制字符[/x00-/x1F]
Blank 空格符或制表符[ /t]
Space 空白符 [ /t/n/r/f/0x0B]
javaLowerCase 取决于Character.isLowerCase()的小写字符
javaUpperCase 取决于Character.isUpperCase()的大写字符
javaWhitespace 取决于Character.isWhitespace()的空白符
javaMirrored 取决于Character.isMirrored()的Mirrored(?)
InBlock 这里的Block是unicode字符的块名,用空格隔开,比如BasicLatin 或 Mongolian。块名列表 参考http://www.unicode.org
Category 或InCategory 这里的Category是Unicode字符的种类名,比如L(字母)或者Sc(货币符号)。种类 名列表参考http://www.unicode.org
matcher的输入可以是实现CharSequence接口的任何类对象,像String,StringBuilder或CharBuffer。
当编译模式时,可以设置一个或多个标志,例如
下面 六 个标志都是支持的:
通过调用 String group(int groupIndex),你可以简单地得到匹配的字符串。
第0个分组代表所有的分组,第一个实际分组的索引是1。调用groupCount来获得总的分组数。
使用开放圆括号来安排嵌套分组。例如,给定模式((1?[0-9]):([0-5][0-9]))[ap]m,并且输入11:59am,Matcher对象报告下列分组
((1?[0-9]):([0-5][0-9]))[ap]m
并输入
11:59am
matcher报告下列分组
分组索引 起始 结束 字符串
0 0 7 11;59am
1 0 5 11:59
2 0 2 11
3 3 5 59
例12-9提示输入一个模式和一个欲匹配的字符串。它将输出输入的字符串是否匹配模式。如果输入匹配包含分组的模式,程序将会使用圆括号来打印分组边界,如((11):(59))am
Example 12-9. RegexTest.java
1. import java.util.*;
2. import java.util.regex.*;
3.
4. /**
5. This program tests regular expression matching.
6. Enter a pattern and strings to match, or hit Cancel
7. to exit. If the pattern contains groups, the group
8. boundaries are displayed in the match.
9. */
10. public class RegExTest
11. {
12. public static void main(String[] args)
13. {
14. Scanner in = new Scanner(System.in);
15. System.out.println("Enter pattern: ");
16. String patternString = in.nextLine();
17.
18. Pattern pattern = null;
19. try
20. {
21. pattern = Pattern.compile(patternString);
22. }
23. catch (PatternSyntaxException e)
24. {
25. System.out.println("Pattern syntax error");
26. System.exit(1);
27. }
28.
29. while (true)
30. {
31. System.out.println("Enter string to match: ");
32. String input = in.nextLine();
33. if (input == null || input.equals("")) return;
34. Matcher matcher = pattern.matcher(input);
35. if (matcher.matches())
36. {
37. System.out.println("Match");
38. int g = matcher.groupCount();
39. if (g > 0)
40. {
41. for (int i = 0; i < input.length(); i++)
42. {
43. for (int j = 1; j <= g; j++)
44. if (i == matcher.start(j))
45. System.out.print('(');
46. System.out.print(input.charAt(i));
47. for (int j = 1; j <= g; j++)
48. if (i + 1 == matcher.end(j))
49. System.out.print(')');
50. }
51. System.out.println();
52. }
53. }
54. else
55. System.out.println("No match");
56. }
57. }
58. }
通常地,你不希望匹配整个输入到某个正则表达式,而是希望在输入中找出一个或多个匹配的子字符串。使用Matcher类的find方法来寻找下一个匹配。如果它返回True,再使用start和end方法找出匹配的范围。
例12-10用到了这种机制。它在一个网页中定位所有的超文本引用并打印它们。为运行程序,在命令行提供一个URL,比如
java HrefMatch http://www.horstmann.com
Example 12-10. HrefMatch.java
1. import java.io.*;
2. import java.net.*;
3. import java.util.regex.*;
4.
5. /**
6. This program displays all URLs in a web page by
7. matching a regular expression that describes the
8. <a href=...> HTML tag. Start the program as
9. java HrefMatch URL
10. */
11. public class HrefMatch
12. {
13. public static void main(String[] args)
14. {
15. try
16. {
17. // get URL string from command line or use default
18. String urlString;
19. if (args.length > 0) urlString = args[0];
20. else urlString = "http://java.sun.com ";
21.
22. // open reader for URL
23. InputStreamReader in = new InputStreamReader(new URL(urlString).openStream());
24.
25. // read contents into string buffer
26. StringBuilder input = new StringBuilder();
27. int ch;
28. while ((ch = in.read()) != -1) input.append((char) ch);
29.
30. // search for all occurrences of pattern
31. String patternString = "<a//s+href//s*=//s*(/"[^/"]*/"|[^//s>])//s*>";
32. Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
33. Matcher matcher = pattern.matcher(input);
34.
35. while (matcher.find())
36. {
37. int start = matcher.start();
38. int end = matcher.end();
39. String match = input.substring(start, end);
40. System.out.println(match);
41. }
42. }
43. catch (IOException e)
44. {
45. e.printStackTrace();
46. }
47. catch (PatternSyntaxException e)
48. {
49. e.printStackTrace();
50. }
51. }
52. }
Matcher类的replaceAll方法 用一个替换字符串 代替 出现的所有正则表达式的匹配。比如,下列指令用#替换所有数字序列
replaceFirst方法只替换模式的第一次出现。
最后讲一点,Pattern类有一个split方法,它类似于字符串tokenizer。它使用正则表达式匹配作边界,将输入分离成字符串数组。比如,下面的指令将输入分离成记号(token),
类
java.util.regex.Pattern 1.4
方法
static Pattern compile(String expression)
static Pattern compile(String expression, int flags)
编译正则表达式字符串到pattern对象用以匹配的快速处理
参数:
expression 正则表达式
flags 下列标志中的一个或多个 CASE_INSENSITIVE, UNICODE_CASE, MULTILINE, UNIX_LINES, DOTALL, and CANON_EQ
Matcher matcher(CharSequence input)
返回一个matcher对象,它可以用来在一个输入中定位模式匹配
String[] split(CharSequence input)
String[] split(CharSequence input, int limit)
将输入字符串分离成记号,并由pattern来指定分隔符的形式。返回记号数组。分隔符并不是记号的一部分。
参数:
input 分离成记号的字符串
limit 生成的最大字符串数。
类
java.util.regex.Matcher 1.4
方法
boolean matches()
返回输入是否与模式匹配
boolean lookingAt()
如果输入的起始匹配模式则返回True
boolean find()
boolean find(int start)
尝试查找下一个匹配,并在找到匹配时返回True
参数:
start 开始搜索的索引
int start()
int end()
返回当前匹配的起始位置和结尾后位置
String group()
返回当前匹配
int groupCount()
返回输入模式中的分组数
int start(int groupIndex)
int end(int groupIndex)
返回一个给定分组当前匹配中的起始位置和结尾后位置
参数:
groupIndex分组索引(从1开始),0表示整个匹配
String group(int groupIndex)
返回匹配一个给定分组的字符串
参数:
groupIndex
分组索引(从1开始),0表示整个匹配
String replaceAll(String replacement)
String replaceFirst(String replacement)
返回从matcher输入得到的字符串,但已经用替换表达式替换所有或第一个匹配
参数:
replacement 替换字符串
Matcher reset()
Matcher reset(CharSequence input)
复位mather状态。
如果你曾经用过Perl或任何其他内建正则表达式支持的语言,你一定知道用正则表达式处理文本和匹配模式是多么简单。如果你不熟悉这个术语,那么“正则表达式”(Regular Expression)就是一个字符构成的串,它定义了一个用来搜索匹配字符串的模式。
许多语言,包括Perl、PHP、Python、JavaScript和JScript,都支持用正则表达式处理文本,一些文本编辑器用正则表达式实现高级“搜索-替换”功能。那么Java又怎样呢?本文写作时,一个包含了用正则表达式进行文本处理的Java规范需求(Specification Request)已经得到认可,你可以期待在JDK的下一版本中看到它。 |
然而,如果现在就需要使用正则表达式,又该怎么办呢?你可以从Apache.org下载源代码开放的Jakarta-ORO库。本文接下来的内容先简要地介绍正则表达式的入门知识,然后以Jakarta-ORO API为例介绍如何使用正则表达式。 |
一、正则表达式基础知识 |
我们先从简单的开始。假设你要搜索一个包含字符“cat”的字符串,搜索用的正则表达式就是“cat”。如果搜索对大小写不敏感,单词“catalog”、“Catherine”、“sophisticated”都可以匹配。也就是说: |
![]() |
1.1 句点符号 |
假设你在玩英文拼字游戏,想要找出三个字母的单词,而且这些单词必须以“t”字母开头,以“n”字母结束。另外,假设有一本英文字典,你可以用正则表达式搜索它的全部内容。要构造出这个正则表达式,你可以使用一个通配符——句点符号“.” 。这样,完整的表达式就是 “t.n”,它匹配“tan”、“ten”、“tin”和“ton”,还匹配“t#n”、“tpn”甚至“t n”,还有其他许多无意义的组合。这是因为句点符号匹配所有字符,包括空格、Tab字符甚至换行符: |
![]() |
1.2 方括号符号 |
为了解决句点符号匹配范围过于广泛这一问题,你可以在方括号(“[]”)里面指定看来有意义的字符。此时,只有方括号里面指定的字符才参与匹配。也就是说,正则表达式“t[aeio]n”只匹配“tan”、“Ten”、“tin”和“ton”。但“Toon”不匹配,因为在方括号之内你只能匹配单个字符: |
![]() |
1.3 “或”符号 |
如果除了上面匹配的所有单词之外,你还想要匹配“toon”,那么,你可以使用“|”操作符。“|”操作符的基本意义就是“或”运算。要匹配“toon”,使用“t(a|e|i|o|oo)n”正则表达式。这里不能使用方扩号 ,因为方括号只 允许匹配单 个字符;这里必须使用圆括号“()”。圆括号还可以用来分组,具体请参见后面介绍。 |
![]() |
1.4 表示匹配次数的符号 |
表一显示了表示匹配次数的符号,这些符号用来确定紧靠该符号左边的符号出现的次数: |
|
假设我们要在文本文件中搜索美国的社会安全号码。这个号码的格式是999-99-9999。用来匹配它的正则表达式如图一所示。在正则表达式中,连字符(“-”)有着特殊的意义,它表示一个范围,比如从0到9。因此,匹配社会安全号码中的连字符号时,它的前面要加上一个转义字符“/”。 |
|
图一:匹配所有123-12-1234形式的社会安全号码 |
假设进行搜索的时候,你希望连字符号可以出现,也可以不出现——即,999-99-9999和999999999都属于正确的格式。这时,你可以在连字符号后面加上“?”数量限定符号,如图二所示: |
|
图二:匹配所有123-12-1234和123121234形式的社会安全号码 |
下面我们再来看另外一个例子。美国汽车牌照的一种格式是四个数字加上二个字母。它的正则表达式前面是数字部分“[0-9]{4}”,再加上字母部分“[A-Z]{2}”。图三显示了完整的正则表达式。 |
|
图三:匹配典型的美国汽车牌照号码,如8836KV |
1.5 “否”符号 |
“^”符号称为“否”符号。如果用在方括号内,“^”表示不想要匹配的字符。例如,图四的正则表达式匹配所有单词,但以“X”字母开头的单词除外。 |
|
图四:匹配所有单词,但“X”开头的除外 |
1.6 圆括号和空白符号 |
假设要从格式为“June 26, 1951”的生日日期中提取出月份部分,用来匹配该日期的正则表达式可以如图五所示: |
|
图五:匹配所有Moth DD,YYYY格式的日期 |
新出现的“/s”符号是空白符号,匹配所有的空白字符,包括Tab字符。如果字符串正确匹配,接下来如何提取出月份部分呢?只需在月份周围加上一个圆括号创建一个组,然后用ORO API(本文后面详细讨论)提取出它的值。修改后的正则表达式如图六所示: |
|
图六:匹配所有Month DD,YYYY格式的日期,定义月份值为第一个组 |
1.7 其它符号 |
为简便起见,你可以使用一些为常见正则表达式创建的快捷符号。如表二所示: |
表二:常用符号 |
|
例如,在前面社会安全号码的例子中,所有出现“[0-9]”的地方我们都可以使用“/d”。修改后的正则表达式如图七所示: |
|
图七:匹配所有123-12-1234格式的社会安全号码 |
二、Jakarta-ORO库 |
有许多源代码开放的正则表达式库可供Java程序员使用,而且它们中的许多支持Perl 5兼容的正则表达式语法。我在这里选用的是Jakarta-ORO正则表达式库,它是最全面的正则表达式API之一,而且它与Perl 5正则表达式完全兼容。另外,它也是优化得最好的API之一。 |
Jakarta-ORO库以前叫做OROMatcher,Daniel Savarese大方地把它赠送给了Jakarta Project。你可以按照本文最后参考资源的说明下载它。 |
我首先将简要介绍使用Jakarta-ORO库时你必须创建和访问的对象,然后介绍如何使用Jakarta-ORO API。 |
▲ PatternCompiler对象 |
首先,创建一个Perl5Compiler类的实例,并把它赋值给PatternCompiler接口对象。Perl5Compiler是PatternCompiler接口的一个实现,允许你把正则表达式编译成用来匹配的Pattern对象。 |
![]() |
▲ Pattern对象 |
要把正则表达式编译成Pattern对象,调用compiler对象的compile()方法,并在调用参数中指定正则表达式。例如,你可以按照下面这种方式编译正则表达式“t[aeio]n”: |
![]() |
默认情况下,编译器创建一个大小写敏感的模式(pattern)。因此,上面代码编译得到的模式只匹配“tin”、“tan”、 “ten”和“ton”,但不匹配“Tin”和“taN”。要创建一个大小写不敏感的模式,你应该在调用编译器的时候指定一个额外的参数: |
![]() |
创建好Pattern对象之后,你就可以通过PatternMatcher类用该Pattern对象进行模式匹配。 |
▲ PatternMatcher对象 |
PatternMatcher对象根据Pattern对象和字符串进行匹配检查。你要实例化一个 Perl5Matcher类并把结果赋值给PatternMatcher接口。Perl5Matcher类是PatternMatcher接口的一个实现,它根据Perl 5正则表达式语法进行模式匹配: |
![]() |
使用PatternMatcher对象,你可以用多个方法进行匹配操作,这些方法的第一个参数都是需要根据正则表达式进行匹配的字符串: |
· boolean matches(String input, Pattern pattern):当输入字符串和正则表达式要精确匹配时使用。换句话说,正则表达式必须完整地描述输入字符串。 |
· boolean matchesPrefix(String input, Pattern pattern):当正则表达式匹配输入字符串起始部分时使用。 |
· boolean contains(String input, Pattern pattern):当正则表达式要匹配输入字符串的一部分时使用(即,它必须是一个子串)。 |
另外,在上面三个方法调用中,你还可以用PatternMatcherInput对象作为参数替代String对象;这时,你可以从字符串中最后一次匹配的位置开始继续进行匹配。当字符串可能有多个子串匹配给定的正则表达式时,用PatternMatcherInput对象作为参数就很有用了。用PatternMatcherInput对象作为参数替代String时,上述三个方法的语法如下: |
· boolean matches(PatternMatcherInput input, Pattern pattern) |
· boolean matchesPrefix(PatternMatcherInput input, Pattern pattern) |
· boolean contains(PatternMatcherInput input, Pattern pattern) |
三、应用实例 |
下面我们来看看Jakarta-ORO库的一些应用实例。 |
3.1 日志文件处理 |
任务:分析一个Web服务器日志文件,确定每一个用户花在网站上的时间。在典型的BEA WebLogic日志文件中,日志记录的格式如下: |
![]() |
分析这个日志记录,可以发现,要从这个日志文件提取的内容有两项:IP地址和页面访问时间。你可以用分组符号(圆括号)从日志记录提取出IP地址和时间标记。 |
首先我们来看看IP地址。IP地址有4个字节构成,每一个字节的值在0到255之间,各个字节通过一个句点分隔。因此,IP地址中的每一个字节有至少一个、最多三个数字。图八显示了为IP地址编写的正则表达式: |
|
图八:匹配IP地址 |
IP地址中的句点字符必须进行转义处理(前面加上“/”),因为IP地址中的句点具有它本来的含义,而不是采用正则表达式语法中的特殊含义。句点在正则表达式中的特殊含义本文前面已经介绍。 |
日志记录的时间部分由一对方括号包围。你可以按照如下思路提取出方括号里面的所有内容:首先搜索起始方括号字符(“[”),提取出所有不超过结束方括号字符(“]”)的内容,向前寻找直至找到结束方括号字符。图九显示了这部分的正则表达式。 |
|
图九:匹配至少一个字符,直至找到“]” |
现在,把上述两个正则表达式加上分组符号(圆括号)后合并成单个表达式,这样就可以从日志记录提取出IP地址和时间。注意,为了匹配“- -”(但不提取它),正则表达式中间加入了“/s-/s-/s”。完整的正则表达式如图十所示。 |
|
图十:匹配IP地址和时间标记 |
现在正则表达式已经编写完毕,接下来可以编写使用正则表达式库的Java代码了。 |
为使用Jakarta-ORO库,首先创建正则表达式字符串和待分析的日志记录字符串: |
![]() |
这里使用的正则表达式与图十的正则表达式差不多完全相同,但有一点例外:在Java中,你必须对每一个向前的斜杠(“/”)进行转义处理。图十不是Java的表示形式,所以我们要在每个“/”前面加上一个“/”以免出现编译错误。遗憾的是,转义处理过程很容易出现错误,所以应该小心谨慎。你可以首先输入未经转义处理的正则表达式,然后从左到右依次把每一个“/”替换成“//”。如果要复检,你可以试着把它输出到屏幕上。 |
初始化字符串之后,实例化PatternCompiler对象,用PatternCompiler编译正则表达式创建一个Pattern对象: |
![]() |
现在,创建PatternMatcher对象,调用PatternMatcher接口的contain()方法检查匹配情况: |
![]() |
接下来,利用PatternMatcher接口返回的MatchResult对象,输出匹配的组。由于logEntry字符串包含匹配的内容,你可以看到类如下面的输出: |
![]() |
3.2 HTML处理实例一 |
下面一个任务是分析HTML页面内FONT标记的所有属性。HTML页面内典型的FONT标记如下所示: |
![]() |
程序将按照如下形式,输出每一个FONT标记的属性: |
![]() |
在这种情况下,我建议你使用两个正则表达式。第一个如图十一所示,它从字体标记提取出“"face="Arial, Serif" size="+2" color="red"”。 |
|
图十一:匹配FONT标记的所有属性 |
第二个正则表达式如图十二所示,它把各个属性分割成名字-值对。 |
|
图十二:匹配单个属性,并把它分割成名字-值对 |
分割结果为: |
![]() |
现在我们来看看完成这个任务的Java代码。首先创建两个正则表达式字符串,用Perl5Compiler把它们编译成 Pattern对象。编译正则表达式的时候,指定Perl5Compiler.CASE_INSENSITIVE_MASK选项,使得匹配操作不区分大小写。 |
接下来,创建一个执行匹配操作的Perl5Matcher对象。 |
![]() |
假设有一个String类型的变量html,它代表了HTML文件中的一行内容。如果html字符串包含FONT标记,匹配器将返回true。此时,你可以用匹配器对象返回的MatchResult对象获得第一个组,它包含了FONT的所有属性: |
![]() |
接下来创建一个PatternMatcherInput对象。这个对象允许你从最后一次匹配的位置开始继续进行匹配操作,因此,它很适合于提取FONT标记内属性的名字-值对。创建PatternMatcherInput对象,以参数形式传入待匹配的字符串。然后,用匹配器实例提取出每一个FONT的属性。这通过指定PatternMatcherInput对象(而不是字符串对象)为参数,反复地调用 PatternMatcher对象的contains()方法完成。PatternMatcherInput对象之中的每一次迭代将把它内部的指针向前移动,下一次检测将从前一次匹配位置的后面开始。 |
本例的输出结果如下: |
![]() |
3.3 HTML处理实例二 |
下面我们来看看另一个处理HTML的例子。这一次,我们假定Web服务器从widgets.acme.com移到了newserver.acme.com。现在你要修改一些页面中的链接: |
![]() |
执行这个搜索的正则表达式如图十三所示: |
|
图十三:匹配修改前的链接 |
如果能够匹配这个正则表达式,你可以用下面的内容替换图十三的链接: |
![]() |
注意#字符的后面加上了$1。Perl正则表达式语法用$1、$2等表示已经匹配且提取出来的组。图十三的表达式把所有作为一个组匹配和提取出来的内容附加到链接的后面。 |
现在,返回Java。就象前面我们所做的那样,你必须创建测试字符串,创建把正则表达式编译到Pattern对象所必需的对象,以及创建一个PatternMatcher对象:![]() |
接下来,用com.oroinc.text.regex包Util类的substitute()静态方法进行替换,输出结果字符串: |
![]() |
Util.substitute()方法的语法如下: |
![]() |
这个调用的前两个参数是以前创建的PatternMatcher和Pattern对象。第三个参数是一个 Substiution对象,它决定了替换操作如何进行。本例使用的是Perl5Substitution对象,它能够进行Perl5风格的替换。第四个参数是想要进行替换操作的字符串,最后一个参数允许指定是否替换模式的所有匹配子串(Util.SUBSTITUTE_ALL),或只替换指定的次数。 |
【结束语】 在这篇文章中,我为你介绍了正则表达式的强大功能。只要正确运用,正则表达式能够在字符串提取和文本修改中起到很大的作用。另外,我还介绍了如何在Java程序中通过Jakarta-ORO库利用正则表达式。至于最终采用老式的字符串处理方式(使用StringTokenizer,charAt,和substring),还是采用正则表达式,这就有待你自己决定了。 |