“正则表达式”(Regular Expression)就是一个字符构成的串,它定义了一个用来搜索匹配字符串的模式.许多语言,包括Perl、PHP、Python、JavaScript和JScript,都支持用正则表达式处理文本,一些文本编辑器用正则表达式实现高级“搜索-替换”功能.在JDK1.3及之前的JDK版本中并没有包含正则表达式的类,如果要在Java中使用正则表达式必须使用第三方提供的正则表达式库,最有名的就是Jakarta-ORO,Jakarta-ORO库以前叫做OROMatcher.使用的时候首先要创建一个实现了PatternCompiler接口的实例变量以创建一个“模式编译器”,Jakarta-ORO中实现了这个接口的类就是Perl5Compiler,这个类做到了与Perl5的正则表达式完全兼容.Jakarta-ORO的使用是非常简便的,而且效率非常高,支持的正则表达式语法也是非常全的,唯一的缺点就是它不是JDK中的标准包.
从JDK1.4开始提供了支持正则表达式API,它们位于java.util.regex包中,由于已经有了标准API,所以本书将会用java.util.regex进行正则表达式的相关操作.
1.基础知识
1.1 句点符号
假设你在玩英文拼字游戏,想要找出三个字母的单词,而且这些单词必须以“t”字母开头,以“n”字母结束.另外,假设有一本英文字典,你可以用正则表达式搜索它的全部内容.要构造出这个正则表达式,你可以使用一个通配符——句点符号“.”,这样,完整的表达式就是“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 表示匹配次数的符号
下表显示了正则表达式的语法:
表 1.1 正则表达式语法
. 匹配任何单个字符.例如正则表达式“b.g”能匹配如下字符串:“big”、“bug”、“b g”,但是不匹配“buug”.
$ 匹配行结束符.例如正则表达式“EJB$”能够匹配字符串“I like EJB”的末尾,但是不能匹配字符串“J2EE Without EJBs!”.
^ 匹配一行的开始.例如正则表达式“^Spring”能够匹配字符串“Spring is a J2EE framework”的开始,但是不能匹配“I use Spring in my project”.
* 匹配0至多个在它之前的字符.例如正则表达式“zo*”能匹配“z”以及“zoo”;正则表达式“.*”意味着能够匹配任意字符串.
/ 转义符,用来将元字符当作普通的字符来进行匹配.例如正则表达式/$被用来匹配美元符号,而不是行尾;正则表达式/.用来匹配点字符,而不是通配符.(也可以用\\)
[] 匹配括号中的任何一个字符.例如正则表达式“b[aui]g”匹配bug、big和bug,但是不匹配beg.可以在括号中使用连字符“-”来指定字符的区间来简化表示,例如正则表达式[0-9]可以匹配任何数字字符,这样正则表达式“a[]c”就可以匹配“a0c”、“a1c”、“a2c”等字符串;还可以制定多个区间,例如“[A-Za-z]”可以匹配任何大小写字母.还有一个相配合使用的元字符“^”,用在这里并不像前边的那个“^”一样表示匹配行开始,而是表示“排除”,要想匹配除了指定区间之外的字符,就可以在左边的括号和第一个字符之间使用^字符,例如“[^163A-Z]”将能偶匹配了1、6、3和所有大写字母之外的任何字符.
( ) 将 () 之间括起来的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域,这个元字符在字符串提取的时候非常有用.
| 将两个匹配条件进行逻辑“或”运算.'z|food' 能匹配 "z" 或 "food".'(z|f)ood' 则匹配 "zood" 或 "food".
+ 匹配前面的子表达式一次或多次.例如正则表达式9+匹配9、99、999等.
? 匹配前面的子表达式零次或一次.例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" .此元字符还有另外一个用途,就是表示非贪婪模式匹配,后边将有介绍
{n} 匹配确定的 n 次.例如,“e{2}”不能匹配“bed”中的“d”,但是能匹配“seed”中的两个“e”.
{n,} 至少匹配n次.例如,“e{2,}”不能匹配“bed”中的“e”,但能匹配“seeeeeeeed”中的所有“e”.
{n,m}最少匹配 n 次且最多匹配 m 次.“e{1,3}”将匹配“seeeeeeeed”中的前三个“e”.
假设我们要在文本文件中搜索美国的社会安全号码.这个号码的格式是999-99-9999.用来匹配它的正则表达式如图一所示.在正则表达式中,连字符(“-”)有着特殊的意义,它表示一个范围,比如从0到9.因此,匹配社会安全号码中的连字符号时,它的前面要加上一个转义字符“\”.
假设进行搜索的时候,你希望连字符号可以出现,也可以不出现——即,999-99-9999和999999999都属于正确的格式.这时,你可以在连字符号后面加上“?”数量限定符号.
美国汽车牌照的一种格式是四个数字加上二个字母.它的正则表达式前面是数字部分“[0-9]{4}”,再加上字母部分“[A-Z]{2}”.
1.5 “否”符号
“^”符号称为“否”符号.如果用在方括号内,“^”表示不想要匹配的字符.例如,图四的正则表达式匹配所有单词,但以“X”字母开头的单词除外.
1.6 圆括号和空白符号
“\s”符号是空白符号,匹配所有的空白字符,包括Tab字符.如果字符串正确匹配,接下来如何提取出月份部分呢?只需在月份周围加上一个圆括号创建一个组,然后用ORO API提取出它的值.
1.7 其它符号
为简便起见,你可以使用一些为常见正则表达式创建的快捷符号.如以下所示:
-
\t:制表符,等同于\u0009
-
\n:换行符,等同于\u000A
-
\d:代表一个数字,等同于[0-9]
-
\D:代表非数字,等同于[^0-9]
-
\s:代表换行符、Tab制表符等空白字符
-
\S:代表非空白字符
-
\w:字母字符,等同于[a-zA-Z_0-9]
-
\W:非字母字符,等同于[^\w]
使用上述的正则表达式,则需要使用Matcher类和Pattern类.
2. Pattern类
一个 Pattern 实例订制了一个所用语法与 PERL 类似的正则表达式经编译后的模式,然后一个 Matcher 实例在这个给定的 Pattern 实例的模式控制下进行字符串的匹配工作.
一个正则表达式,也就是一串有特定意义的字符,必须首先要编译成为一个 Pattern 类的实例,这个 Pattern 对象将会使用 matcher() 方法来生成一个 Matcher 实例,接着便可以使用该 Matcher 实例以编译的正则表达式为基础对目标字符串进行匹配工作,多个 Matcher 是可以共用一个 Pattern 对象的.
示例:
import java.util.regex.*; public class MatcherTest { public static void main(String[] args) throws Exception { // 生成 Pattern 对象并且编译一个简单的正则表达式 "Kelvin" Pattern p = Pattern.compile("Kevin"); // 用 Pattern 类的 matcher() 方法生成一个 Matcher 对象 Matcher m = p.matcher("Kelvin Li and Kelvin Chan are both working in Kelvin Chen's KelvinSoftShop company"); StringBuffer sb = new StringBuffer(); int i=0; // 使用 find() 方法查找第一个匹配的对象 boolean result = m.find(); // 使用循环将句子里所有的 "kelvin" 找出并替换再将内容加到 sb 里 while(result) { i++; m.appendReplacement(sb,"Kevin"); System.out.println("第" + i + "次匹配后sb的内容是:" + sb); // 继续查找下一个匹配对象 result = m.find(); } // 最后调用 appendTail() 方法将最后一次匹配后的剩余字符串加到 sb 里 m.appendTail(sb); System.out.println("调用m.appendTail(sb)后sb的最终内容是:" + sb.toString()); } }
结果:
Kevin has seen《LEON》seveal times,because it is a good film. 凯文已经看过《这个杀手不太冷》几次了,因为它是一部好电影. 名词:凯文.
3. Matcher类
一个 Matcher 实例是被用来对目标字符串进行基于既有模式(也就是一个给定的 Pattern 所编译的正则表达式)进行匹配查找的,所有往 Matcher 的输入都是通过 CharSequence 接口提供的,这样做的目的在于可以支持对从多元化的数据源所提供的数据进行匹配工作.
我们分别来看看各方法的使用:
3.1 matches() / lookingAt() / find()
一个 Matcher 对象是由一个 Pattern 对象调用其 matcher() 方法而生成的,一旦该 Matcher 对象生成,它就可以进行三种不同的匹配查找操作:
① matches() 方法只有整个目标字符串完全匹配时才返回真值
② lookingAt() 方法将检测目标字符串是否以匹配的子串起始
③ find() 方法尝试在目标字符串里查找下一个匹配子串
注:以上三个方法都将返回一个布尔值来表明成功与否
3.2 replaceAll() / appendReplacement() / appendTail()
Matcher 类同时提供了四个将匹配子串替换成指定字符串的方法:
① replaceAll() //将目标字符串里与既有模式相匹配的子串全部替换为指定的字
② replaceFirst() //将目标字符串里第一个与既有模式相匹配的子串替换为指定的字符串
③ appendReplacement()
④ appendTail()
appendReplacement(StringBuffer sb,String replacement) 将当前匹配子串替换为指定字符串,并且将替换后的子串以及其之前到上次匹配子串之后的字符串段添加到一个 StringBuffer 对象里. appendTail(StringBuffer sb) 则将最后一次匹配工作后剩余的字符串添加到一个 StringBuffer 对象里.
例如,有字符串"fatcatfatcatfat",假设既有正则表达式模式为 "cat",第一次匹配后调用 appendReplacement(sb,"dog"),那么这时 StringBuffer sb 的内容为 "fatdog",也就是 "fatcat" 中的 "cat" 被替换为 "dog" 并且与匹配子串前的内容加到 "sb" 里,而第二次匹配后调用 appendReplacement(sb,那么sb的内容就变为 "fatdogfatdog",如果最后再调用一次 appendTail(sb),那么 sb 最终的内容将是 "fatdogfatdogfat".
示例:
// 该例将把句子里的 "Kelvin" 改为 "Kevin" import java.util.regex.*; public class MatcherTest { public static void main(String[] args) throws Exception { // 生成 Pattern 对象并且编译一个简单的正则表达式 "Kelvin" Pattern p = Pattern.compile("Kevin"); // 用 Pattern 类的 matcher() 方法生成一个 Matcher 对象 Matcher m = p.matcher("Kelvin Li and Kelvin Chan are both working in Kelvin Chen's KelvinSoftShop company"); StringBuffer sb = new StringBuffer(); int i=0; // 使用 find() 方法查找第一个匹配的对象 boolean result = m.find(); // 使用循环将句子里所有的 "kelvin" 找出并替换再将内容加到 sb 里 while(result) { i++; m.appendReplacement(sb,"Kevin"); System.out.println("第" + i + "次匹配后sb的内容是:" + sb); // 继续查找下一个匹配对象 result = m.find(); } // 最后调用 appendTail() 方法将最后一次匹配后的剩余字符串加到 sb 里 m.appendTail(sb); System.out.println("调用m.appendTail(sb)后sb的最终内容是:" + sb.toString()); } }
结果:
第1次匹配后sb的内容是:Kevin 第2次匹配后sb的内容是:Kevin Li and Kevin 第3次匹配后sb的内容是:Kevin Li and Kevin Chan are both working in Kevin 第4次匹配后sb的内容是:Kevin Li and Kevin Chan are both working in Kevin Chen's Kevin 调用m.appendTail(sb)后sb的最终内容是:Kevin Li and Kevin Chan are both working in Kevin Chen's
3.3 group() / group(int group) / groupCount()
该系列方法与我们在上篇介绍的 Jakarta-ORO 中的 MatchResult .group() 方法类似,都是要返回与组匹配的子串内容,下面代码将很好解释其用法:
示例:
import java.util.regex.*; public class GroupTest { public static void main(String[] args) throws Exception { Pattern p = Pattern.compile("(ca)(t)"); Matcher m = p.matcher("one cat,two cats in the yard"); StringBuffer sb = new StringBuffer(); boolean result = m.find(); System.out.println("该次查找获得匹配组的数量为:"+m.groupCount()); for(int i = 1; i <= m.groupCount(); i++) { System.out.println("第" + i + "组的子串内容为: " + m.group(i)); } } }
结果:
该次查找获得匹配组的数量为:2 第1组的子串内容为:ca 第2组的子串内容为:t
4.正则应用
匹配Email的示例:
import java.util.regex.*; public class Email { public static void main(String[] args) throws Exception { String input = args[0]; // 检测输入的 Email 地址是否以非法符号 "." 或 "@" 作为起始字符 Pattern p = Pattern.compile("^\\.|^\\@"); Matcher m = p.matcher(input); if (m.find()) { System.err.println("EMAIL地址不能以'.'或'@'作为起始字符"); } // 检测是否以 "www." 为起始 p = Pattern.compile("^www\\."); m = p.matcher(input); if (m.find()) { System.out.println("EMAIL地址不能以'www.'起始"); } // 检测是否包含非法字符 p = Pattern.compile("[^A-Za-z0-9\\.\\@_\\-~#]+"); m = p.matcher(input); StringBuffer sb = new StringBuffer(); boolean result = m.find(); boolean deletedIllegalChars = false; while(result) { // 如果找到了非法字符那么就设下标记 deletedIllegalChars = true; // 如果里面包含非法字符如冒号双引号等,那么就把他们消去,加到 SB 里面 m.appendReplacement(sb,""); result = m.find(); } m.appendTail(sb); input = sb.toString(); if (deletedIllegalChars) { System.out.println("输入的EMAIL地址里包含有冒号、逗号等非法字符,请修改"); System.out.println("您现在的输入为: "+args[0]); System.out.println("修改后合法的地址应类似: "+input); } } }
5.正则总结
在开发中如果想要使用正则,基本上都是直接使用String类,很少直接去使用Pattern类和Matcher类,因为String类的功能已经非常强大了.
例如:
String str = "aaaaaaaaa" ;
System.out.println(str.matches("\\w{6,15}")) ;
示例:
public class StringRegexDemo { public static void main(String[] args) { String str = "aa@aa.com.cn" ; String regex = "\\w+@\\w+.(com|net.cn)" ; System.out.println(str.matches(regex)) ; } }
参考:
http://www.jb51.cc/article/p-dgscxyyn-yn.html
http://blog.163.com/huangpengfeimaple@126/blog/static/6118133720127710859836/
20150503
JAVA学习笔记系列
--------------------------------------------
联系方式
--------------------------------------------
Weibo: ARESXIONG
E-Mail: aresxdy@gmail.com
------------------------------------------------ 原文链接:https://www.f2er.com/regex/360420.html