正则表达式(regular expression):对文字进行模糊匹配的语言,用一些特殊的符号(元字符)来代表某种特征的一组字符以及指定匹配的次数,含有员字符的文本不再表示其特定的文本内容,而是一种由正则表达式指定的文本模式,它可以匹配所有符合这一模式的文本串。类似于操作系统的文件通配符:?、*。?表示任意的一个字符(如data_?:data_1,data_a等)。*表示0个或以上的字符(如data_*:data_、data_1、data_ab、data_2dkj等)。
一、正则表达式Java源码
正则表达式是jdk1.4提出,在软件包java.util.regex中实现。在jdk6中该软件包中包含了5个文件。
1、final class ASCII:Utility class that implements the standard C ctype functionality.
该类定义了元字符的值:
static final int UPPER = 0x00000100;
static final int LOWER = 0x00000200;
static final int DIGIT = 0x00000400;
static final int SPACE = 0x00000800;
static final int PUNCT = 0x00001000;
static final int CNTRL = 0x00002000;
static final int BLANK = 0x00004000;
static final int HEX = 0x00008000;
static final int UNDER = 0x00010000;
static final int ASCII = 0x0000FF00;
static final int ALPHA = (UPPER|LOWER);
static final int ALNUM = (UPPER|LOWER|DIGIT);
static final int GRAPH = (PUNCT|UPPER|LOWER|DIGIT);
static final int WORD = (UPPER|LOWER|UNDER|DIGIT);
static final int XDIGIT = (HEX);
和所有的C类型的字符的整形数组ctype:
private static final int[] ctype = new int[] {
CNTRL, /* 00 (NUL) */
CNTRL, /* 01 (SOH) */
CNTRL, /* 02 (STX) */
CNTRL, /* 03 (ETX) */
CNTRL, /* 04 (EOT) */
CNTRL, /* 05 (ENQ) */
CNTRL, /* 06 (ACK) */
CNTRL, /* 07 (BEL) */
CNTRL, /* 08 (BS) */
SPACE+CNTRL+BLANK, /* 09 (HT) */
SPACE+CNTRL, /* 0A (LF) */
SPACE+CNTRL, /* 0B (VT) */
SPACE+CNTRL, /* 0C (FF) */
SPACE+CNTRL, /* 0D (CR) */
CNTRL, /* 0E (SI) */
CNTRL, /* 0F (SO) */
CNTRL, /* 10 (DLE) */
CNTRL, /* 11 (DC1) */
CNTRL, /* 12 (DC2) */
CNTRL, /* 13 (DC3) */
CNTRL, /* 14 (DC4) */
CNTRL, /* 15 (NAK) */
CNTRL, /* 16 (SYN) */
CNTRL, /* 17 (ETB) */
CNTRL, /* 18 (CAN) */
CNTRL, /* 19 (EM) */
CNTRL, /* 1A (SUB) */
CNTRL, /* 1B (ESC) */
CNTRL, /* 1C (FS) */
CNTRL, /* 1D (GS) */
CNTRL, /* 1E (RS) */
CNTRL, /* 1F (US) */
SPACE+BLANK, /* 20 SPACE */
PUNCT, /* 21 ! */
PUNCT, /* 22 " */
PUNCT, /* 23 # */
PUNCT, /* 24 $ */
PUNCT, /* 25 % */
PUNCT, /* 26 & */
PUNCT, /* 27 ' */
PUNCT, /* 28 ( */
PUNCT, /* 29 ) */
PUNCT, /* 2A * */
PUNCT, /* 2B + */
PUNCT, /* 2C , */
PUNCT, /* 2D - */
PUNCT, /* 2E . */
PUNCT, /* 2F / */
DIGIT+HEX+0, /* 30 0 */
DIGIT+HEX+1, /* 31 1 */
DIGIT+HEX+2, /* 32 2 */
DIGIT+HEX+3, /* 33 3 */
DIGIT+HEX+4, /* 34 4 */
DIGIT+HEX+5, /* 35 5 */
DIGIT+HEX+6, /* 36 6 */
DIGIT+HEX+7, /* 37 7 */
DIGIT+HEX+8, /* 38 8 */
DIGIT+HEX+9, /* 39 9 */
PUNCT, /* 3A : */
PUNCT, /* 3B ; */
PUNCT, /* 3C < */
PUNCT, /* 3D = */
PUNCT, /* 3E > */
PUNCT, /* 3F ? */
PUNCT, /* 40 @ */
UPPER+HEX+10, /* 41 A */
UPPER+HEX+11, /* 42 B */
UPPER+HEX+12, /* 43 C */
UPPER+HEX+13, /* 44 D */
UPPER+HEX+14, /* 45 E */
UPPER+HEX+15, /* 46 F */
UPPER+16, /* 47 G */
UPPER+17, /* 48 H */
UPPER+18, /* 49 I */
UPPER+19, /* 4A J */
UPPER+20, /* 4B K */
UPPER+21, /* 4C L */
UPPER+22, /* 4D M */
UPPER+23, /* 4E N */
UPPER+24, /* 4F O */
UPPER+25, /* 50 P */
UPPER+26, /* 51 Q */
UPPER+27, /* 52 R */
UPPER+28, /* 53 S */
UPPER+29, /* 54 T */
UPPER+30, /* 55 U */
UPPER+31, /* 56 V */
UPPER+32, /* 57 W */
UPPER+33, /* 58 X */
UPPER+34, /* 59 Y */
UPPER+35, /* 5A Z */
PUNCT, /* 5B [ */
PUNCT, /* 5C \ */
PUNCT, /* 5D ] */
PUNCT, /* 5E ^ */
PUNCT|UNDER, /* 5F _ */
PUNCT, /* 60 ` */
LOWER+HEX+10, /* 61 a */
LOWER+HEX+11, /* 62 b */
LOWER+HEX+12, /* 63 c */
LOWER+HEX+13, /* 64 d */
LOWER+HEX+14, /* 65 e */
LOWER+HEX+15, /* 66 f */
LOWER+16, /* 67 g */
LOWER+17, /* 68 h */
LOWER+18, /* 69 i */
LOWER+19, /* 6A j */
LOWER+20, /* 6B k */
LOWER+21, /* 6C l */
LOWER+22, /* 6D m */
LOWER+23, /* 6E n */
LOWER+24, /* 6F o */
LOWER+25, /* 70 p */
LOWER+26, /* 71 q */
LOWER+27, /* 72 r */
LOWER+28, /* 73 s */
LOWER+29, /* 74 t */
LOWER+30, /* 75 u */
LOWER+31, /* 76 v */
LOWER+32, /* 77 w */
LOWER+33, /* 78 x */
LOWER+34, /* 79 y */
LOWER+35, /* 7A z */
PUNCT, /* 7B { */
PUNCT, /* 7C | */
PUNCT, /* 7D } */
PUNCT, /* 7E ~ */
CNTRL, /* 7F (DEL) */
};
ASCII类还定义了一些用于判断是否为特定的元字符类型或者转换为特定的元字符类型:
eg:
static int toUpper(int ch) {
return isLower(ch) ? (ch - 0x20) : ch;
}
和
static int getType(int ch) {
return ((ch & 0xFFFFFF80) == 0 ? ctype[ch] : 0);
}
static boolean isType(int ch, int type) {
return (getType(ch) & type) != 0;
}
其中isType和getType是基本方法,其他方法很多都是调用isType方法来进行实现的。
eg:static boolean isAlpha(int ch) {
return isType(ch, ALPHA);
}
2、public final class Pattern implements java.io.Serializable:A compiled representation of a regular expression。正则表达式的模式定义类。是正则表达式的核心工具类。主要方法:
1)、compile():Compiles the given regular expression into a pattern.即通过方法给定的字符串参数获取一特定模式Pattern。是一个静态方法,常用Pattern p=Pattern.compile(String pattern)实例化Pattern。
2)、public String[] split(CharSequence input, int limit):将传入的字符序列和分隔符对字符序列进行分割,并返回一个字符串数组。
3)、matches():编译给定正则表达式并尝试将给定输入与其匹配.
4)、public Matcher matcher(CharSequence input): 创建匹配给定输入与此模式的匹配器.
在Pattern类中还定义许多继承于Node的静态类。
3、public interface MatchResult:this interface return The index of the first character matched。该接口定义了7个方法。
4、public final class Matcher implements MatchResult:该接口不但实现了MatchResult接口定义的7个方法,还实现了许多其他常用的方法。在该类中方法的方法的实现需要Pattern类的方法。该类中的方法主要实现的是在模式匹配后的索引、替换和相关的区域操作等。
5、PatternSyntaxException:定义了一个继承于IllegalArgumentException的异常类来处理模式语法异常。
源码有利于编程能力的提高,同时了解机制的具体实现,增强系统设计思维的提升,应多多查看源码。同时,快速入门时可以选择源码和文档一起使用,在使用正则表达式时,主要参见的是Pattern类的相关文档。
二、正则表达式的使用:
正则表达式的使用非常广,主要可以使用在这几方面(当然,对于那些高手来说用处是大大的超过了下面我有限的陈列):
1、字符序列查询,包含子序列的查询和将特定模式的字符序列查询后并提取出来两种常用手段。
查询:String str="abc efg ABC";
String regEx="a|f"; //表示a或f
Pattern p=Pattern.compile(regEx);
Matcher m=p.matcher(str);
boolean rs=m.find();
如果str中有regEx,那么rs为true,否则为flase。如果想在查找时忽略大小写,则可以写成Pattern p=Pattern.compile(regEx,Pattern.CASE_INSENSITIVE);
提取:String regEx=".+\(.+)$";
String str="c:\dir1\dir2\name.txt";
Pattern p=Pattern.compile(regEx);
Matcher m=p.matcher(str);
boolean rs=m.find();
for(int i=1;i<=m.groupCount();i++){
System.out.println(m.group(i));
}
以上的执行结果为name.txt,提取的字符串储存在m.group(i)中,其中i最大值为m.groupCount();
2、字符串分割:可将字符序列通过特定分隔符划分为多个字符串。常用split方法返回一个字符串数组。可用于站点和路径分析,信息分割(如二维码名片解码后提取姓名)。
分割:String regEx="::";
Pattern p=Pattern.compile(regEx);
String[] r=p.split("xd::abc::cde");
执行后,r就是{"xd","abc","cde"},其实分割时还有跟简单的方法:
String str="xd::abc::cde";
String[] r=str.split("::");
3、字符串替换或删除:将在字符序列中的查询模式匹配处进行替换或删除。多用Matcher类的replaceFirst或replaceAll来进行替换,替换方法中的字符串参数为替换的字符串。删除时替换方法replaceFirst或replaceAll的参数为空字符串。
String regEx="a+"; //表示一个或多个a
Pattern p=Pattern.compile(regEx);
Matcher m=p.matcher("aaabbced a ccdeaa");
String s=m.replaceAll("A");
结果为"Abbced A ccdeA"
如果写成空串,既可达到删除的功能,比如:
String s=m.replaceAll("");
结果为"bbced ccde"
三、实例:两个经典的正则表达式的运用:java代码行统计、文件中的邮箱地址抓取
1、下面是一个利用正则表达式计算文件夹下的java文件的代码行数(可检测出空行和注释)。
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class CodeCounter {
static long normalLines = 0;
static long commentLines = 0;
static long whiteLines = 0;
public static void main(String[] args) {
File f = new File("E:\\eclipsejeeworkspace\\RegExp\\src"); //父文件夹
File[] codeFiles = f.listFiles(); //找出父文件夹下的文件
for(File child : codeFiles){
if(child.getName().matches(".*\\.java$")) { //找出其中的Java文件
parse(child);
}
}
System.out.println("normalLines:" + normalLines);
System.out.println("commentLines:" + commentLines);
System.out.println("whiteLines:" + whiteLines);
}
private static void parse(File f) {
BufferedReader br = null;
boolean comment = false;
try {
br = new BufferedReader(new FileReader(f));
String line = "";
while((line = br.readLine()) != null) {
line = line.trim();
if(line.matches("^[\\s&&[^\\n]]*$")) { //空行
whiteLines ++;
} else if (line.startsWith("/*") && !line.endsWith("*/")) {
commentLines ++;
comment = true;
} else if (line.startsWith("/*") && line.endsWith("*/")) {
commentLines ++;
} else if (true == comment) { //true==comment的效率比comment==true的效率高
commentLines ++;
if(line.endsWith("*/")) {
comment = false;
}
} else if (line.startsWith("//")) {
commentLines ++;
} else {
normalLines ++;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br != null) {
try {
br.close();
br = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2、邮箱地址抓取:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EmailSpider {
public static void main(String[] args) {
try {
BufferedReader br = new BufferedReader(new FileReader("E:\\eclipsejeeworkspace\\RegExp\\src\\test.htm"));
//BufferedReader一次可以读取一行数据
String line = "";
while((line=br.readLine()) != null) {
parse(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void parse(String line) {
Pattern p = Pattern.compile("[\\w[.-]]+@[\\w[.-]]+\\.[\\w]+");//Email地址匹配
Matcher m = p.matcher(line);
while(m.find()) {
System.out.println(m.group());
}
}
}
四、附上jdk6的中Pattern中的元字符
构造 匹配 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|