学习连接: http://www.ibeifeng.com/ http://bbs.langsin.com/viewthread.php?tid=8092&extra=&page=1
RegularExpressions
正则表达式
字符串的处理利器
正则表达式是在Unix上流行起来的
peol?perl语言对正则表达式的处理好
太有用了
IP地址是否正确
可以作为大多数网站的验证
从网页里揪出email地址
可以发垃圾邮件
从网页中揪出链接等
就像迅雷抓取页面似的
字符串里有个方法java.lang.String
boolean matches(String regex)
regex是正则表达式,正则表达式本身也是一个字符串,只不过是一个特殊的字符串
<!--[endif]-->
运行结果:
true
三个点是什么?
一个点代表一个字符
<!--[endif]-->
<!--[endif]-->
数字都变成—了
d代表一位数字
用两个//代表一个/
Pattern,我们大多数要参考这个类
static Pattern compile(String regex)
什么意思呢?
将正则表达式编译一下,编译之后放到Pattern模式里边
Pattern所代表的就是字符串所要匹配的模式,而这个模式本身需要编译一下的
为什么要编译呢?
如果你不编译,每次现场编译的时候速度就会慢一点
你首先将这个模式编译好了,再拿这个模式去匹配字符串的时候,匹配起来的速度就会快一些
[a-z]匹配一位字母
[a-z]{3}匹配具有三位字母的字符串,并且这三个字符必须是a到z之间的
public Matcher matcher(CharSequence input)
matcher意思是说我要去匹配某一个字符串
CharSequence字符序列
字符串本身实现了CharSequence接口
这里又是多态,父类引用指向子类对象
Matcher叫匹配器
实际上背后的内部原理是这样的
他会创建一个有限状态的自动机
简单理解就是p是一种模式
这种模式匹配那个字符串
在匹配过程中可能会产生多个结果
产生的结果保留在Matcher对象m里
打印出结果
System.out.println(m.matches())
Pattern p = pattern.compile(“[a-z]{3}”);
Matcher m = p.matcher(“fgh”);
System.out.println(m.matches())
三句写成一句就是
System.out.println(“fgh”.matches(“[a-z]{3}”))
字符串 正则表达式
为什么不写成一句呢?
三句的效率高些,并且Pattern和Matcher提供了其他很多很重要的功能
初步认识MetaCharacters . * + ?
meta的源数据意思
. 是一个字符的意思
* 表0个或多个
+ 表一个或多个
? 表一个或0个
在正则表达式里也有正常的字符,并不是所有的都是非正常的
X{n} X正好出现n次
X{n,} X至少出现n次
X{n,m} X至少出现n次,最多出现m次
p("192.168.0.aaa".matches("//d{1,3}//.//d{1,3}//.//d{1,3}//.//d{1,3}"));
数字出现1到3次
.是特殊字符,用//.表示
[ ]表示范围
p("".matches("a?")); 返回结果是true
""空串表示a出现0次或一次,在英文里叫zero length 零宽度匹配
p("".matches("a*")) 同理
范围
[abc]
[]表达的意思是去[]中字符的某一个就可以了,匹配一个字符,不管有多长
p("a".matches("[abc]"));表示请你取abc中的一个看有没有和a匹配的
p("a".matches("[^abc]"));表示取除了abc以外的其他字符都可以
p("A".matches("[a-zA-Z]"));表示取a-z或者A-Z
p("A".matches("[a-z]|[A-Z]"));和上面的一样
p("A".matches("[a-z[A-Z]]"));和上面的一样
p("R".matches("[A-Z&&[RFG]]"));表示A-Z 中的并且要RFG三个中的
取并集
认识其他MetaCharacters /s /w /d /
/s表示空白字符,空白字符包含什么了?[ /t /n /x0B /f /r]
空格 tab键 换行 制表符 回车
/w表示[a-zA-Z_0-9]构成单词的字符
有时候需要取用户名,可以用这种正则表达式取出来
<!--[endif]-->
如果写成一个反斜杠/就会报错,因为/和后面的“构成转义字符
p("//".matches("//"));报错
正则表达式本身要匹配一个表达式的话需要两个反斜杠
用字符串把一个正则表达式表现出来的时候
每一个反斜线都要用两个反斜杠替代
如果写两个//
p("//".matches("//"));中的后一反斜杠会和“匹配成转义字符,这样就必须用四个/
p("//".matches(""));
POSIX是Unix操作系统的一套标准,用的不是特别多
边界匹配
p("hello sir".matches("^h.*"));表示开头第一个字母为h,.一个字符,后面跟着0个或多个字符
^ 是什么意思?
位于[]第一字符意思是取反,在[]外表示一行的起始位置
p("hello sir".matches(".*ir$"));
$以什么结尾,这里表示亿ir结尾
/b是单词的边界
什么是单词边界
空白字符,换行,各种各样的特殊字符等等都算边界字符
p("hello sir".matches("^h[a-z]{1,3}o//b.*"));//true
p("hellosir".matches("^h[a-z]{1,3}o//b.*"));//false
找文档里的空白行,怎么找呢?
p(" /n".matches("^[//s&&[^//n]]*//n$"));
开头是空白符,并且不是换行符,出现0次或1次,最后以换行符结束
测试:
p("aaa 8888c".matches(".*//d{4}."));//true
p("aaa 8888c".matches(".*//b//d{4}."));//true
p("asdfasdfsafsf@dsdfsdf.com".matches("[//w[.-]]+@[//w[.-]]+//.[//w]+"));
这是一般的匹配email的方法
Pattern p = Pattern.compile("//d{3,5}");
String s = "123-34345-234-00";
Matcher m = p.matcher(s);//用那个模板匹配字符串
p(m.matches());//匹配整个字符串
m.reset();//吃进去还吐出来
p(m.find());//表示找一个和那个模式相匹配的子串
p(m.start() + "-" + m.end());
p(m.find());//当找到一个子串后,他会把这个子串去掉,然后从剩下的字符串里再找
p(m.start() + "-" + m.end());//end是找到的最后一个位置的后一个位置
p(m.find());
p(m.start() + "-" + m.end());
p(m.find());
//p(m.start() + "-" + m.end());字符串只有能被找到才能输出起始位置,否则报错
p(m.lookingAt());//意思是每次找的时候从第一个字符的位置开始找
p(m.lookingAt());//每次都是从开头位置找
p(m.lookingAt());
p(m.lookingAt());
字符串的替换replacement
m.group()输出匹配到的那个子串
static Pattern compile(String regex, int flags)
flags意思是可以指定一些常量用来规定编译时的规则
Pattern p = Pattern.compile("java",Pattern.CASE_INSENSITIVE);
忽略大小写
将找到的字符串,奇数位替换成大写,偶数位替换成小写
Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher("java Java JAVa JaVa IloveJAVA you hateJava afasdfasdf");
StringBuffer buf = new StringBuffer();
int i=0;
while(m.find()) {
i++;
if(i%2 == 0) {
.appendReplacement(buf, "java");//意思是添加一个用来做替换的,替换的找得这个位置
} else {
m.appendReplacement(buf, "JAVA");
}
}
m.appendTail(buf);//这个是为了防止最后那段字符串丢掉
p(buf);
分组
group
Pattern p = Pattern.compile("//d{3,5}[a-z]{2}");
String s = "123aa-34345bb-234cc-00";
Matcher m = p.matcher(s);
while(m.find()) {
p(m.group());
}
结果:
把符合上面那个正则表达式的子串中的数字拿出来?
怎么拿?
用循环太麻烦。。
这里用分组来实现
分组用小括号构成的
分了几组就看有几对小括号
Pattern p = Pattern.compile("(d{3,5})(-z]{2})");
其实算上整个大组是分了2组
p(m.group(1);意思是符合第一组的子串
怎么确定那个是第一组呢
只要数每对小括号的左小括号就可以了
以后我们揪email的时候,只想把@前的名字揪出来,该怎么办呢?
用分组的办法
**************************************************************************************************************************************************************
抓取网页中的email地址
如果你学的扎实就很容易就写出来了,如果没写出来,放假复习俄罗斯方块的小游戏
蜘蛛程序
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("D://share//courseware//1043633.html"));
String line = "";
while((line=br.readLine()) != null) {
parse(line);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void parse(String line) {
Pattern p = Pattern.compile("[//w[.-]]+@[//w[.-]]+//.[//w]+");
Matcher m = p.matcher(line);
while(m.find()) {
System.out.println(m.group());
}
}
}
**************************************************************************************************************************************************************
代码统计小程序
当拿到文件以后,检测文件的后缀名
如何检验每个文件的后缀名?
专业的程序员应该将comment == true 写成 true == comment
他们的区别是什么?
用readLine方法,取出一行后本身已经将换行符去掉了
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("D://share//JavaProjects//TankWar1.9.11//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();//避免要是一行开头是以tal键开始的怎么办
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) {
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();
}
}
}
}
}
**************************************************************************************************************************************************************
以上基本上可以解决一般的正则表达式了
但是正则表达式还有一些深层次的写法
要是见了不认识也是一种遗憾
//qulifiers修饰符 限定符
Greedy quantifiers 贪婪的
Reluctant quantifiers 勉强的,不情愿的
Possessive quantifiers 独占性的
Pattern p = Pattern.compile("(.{3,10} ) [0-9]");
String s = "aaaa5bbbb6";
Matcher m = p.matcher(s);
if(m.find())
p(m.start() + "-" + m.end());
else
p("not match!");
3到10个字符后面跟一个数字
Pattern p = Pattern.compile("(.{3,10}? ) [0-9]");
显示的结果是0-5
greedy的意思是说 Pattern p = Pattern.compile("(.{3,10} ) [0-9]");
当他看到.{3,10}的时候,他最多是10个字符
那么他二话不说先吞10字符再说
干活比较贪婪,不是挑最少的,而是挑最多的直接吞下去
吞下的10个字符和正则表达式.{3,10}做匹配
这里匹配不上
匹配不上就往外吐一个,并把这个6作为正则表达式的数字的话真好匹配[0-9]
那么剩下的aaaa5bbbb也真好匹配.{3,10}
Reluctant意思是 Pattern p = Pattern.compile("(.{3,10}? ) [0-9]");
上来不是吃最多的,而是最少的
上来先吃3个匹配.{3,10}
后面的不是数字
然后再吞一个
后面的是数字了,匹配[0-9]
到此为止
Possessive quantifiers意思是 Pattern p = Pattern.compile("(.{3,10}+ ) [0-9]");
和贪婪的有点类似
直接将字符都吞进来
但是不忘外吐
这样就没有能匹配了
这里输出 not match
换成字符String s = "aaaa5bbbb6";就可以匹配了
0-11
**************************************************************************************************************************************************************
补充
可能会在论坛里遇到的写法
可以不会写但要能看懂别人写的
Pattern p = Pattern.compile(".{3}");
String s = "444a66b";
Matcher m = p.matcher(s);
while(m.find()) {
p(m.group());
}
结果是
44a
66b
non-capturing
Pattern p = Pattern.compile(".{3}(?=a)");
String s = "444a66b";
Matcher m = p.matcher(s);
while(m.find()) {
p(m.group());
}
.{3}(?=a)什么意思呢?
小括号正常情况下是一个组,这个组是用来匹配字符串的
如果是以括号里?打头的他不是来匹配字符串的
所以叫非捕获字符串
这里表示抓取字符串里前三个是字符紧跟一个a
但是a不被捕获
也就是不输出a
最后结果是444
(?=a).{3} 写成这样的结果是
a66
比较诡异
(?!a).{3} 意思是前面不能是a的
输出结果是
444
66b
.{3} (?!a) 意思是
结果是44a
66b
他首先找444后面是a
那么他继续往后找,也就是往后推一个
44a
**************************************************************************************************************************************************************
back refenrences
向前引用
Pattern p = Pattern.compile("(//d//d)//1");
String s = "1212";
Matcher m = p.matcher(s);
p(m.matches());
//d//d找两个数字
//1的意思是后面找到的结果必须和我前面第一个group找到的一样
Pattern p = Pattern.compile("(//d(//d))//2");
String s = "122";
Matcher m = p.matcher(s);
p(m.matches());
**************************************************************************************************************************************************************
flags的简写
//Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
简写
p("Java".matches("(?i)(java)"));
?i这里叫非捕获组non-capturing
**************************************************************************************************************************************************************
关于正则表达式的,用不着专门买书来研究,有不懂得就上网问
code
import java.util.regex.Matcher; import java.util.regex.Pattern; public class Test { public static void main(String[] args) { //简单认识正则表达式的概念 /* p("abc".matches("...")); p("a8729a".replaceAll("//d", "-")); Pattern p = Pattern.compile("[a-z]{3}"); Matcher m = p.matcher("fgh"); p(m.matches()); p("fgha".matches("[a-z]{3}")); */ //初步认识. * + ? /* p("a".matches(".")); p("aa".matches("aa")); p("aaaa".matches("a*")); p("aaaa".matches("a+")); p("".matches("a*")); p("aaaa".matches("a?")); p("".matches("a?")); p("a".matches("a?")); p("214523145234532".matches("//d{3,100}")); p("192.168.0.aaa".matches("//d{1,3}//.//d{1,3}//.//d{1,3}//.//d{1,3}")); p("192".matches("[0-2][0-9][0-9]")); */ //范围 /* p("a".matches("[abc]")); p("a".matches("[^abc]")); p("A".matches("[a-zA-Z]")); p("A".matches("[a-z]|[A-Z]")); p("A".matches("[a-z[A-Z]]")); p("R".matches("[A-Z&&[RFG]]")); */ //认识/s /w /d / /* p(" /n/r/t".matches("//s{4}")); p(" ".matches("//S")); p("a_8".matches("//w{3}")); p("abc888&^%".matches("[a-z]{1,3}//d+[&^#%]+")); p("//".matches("")); */ //POSIX Style //p("a".matches("//p{Lower}")); //boundary /* p("hello sir".matches("^h.*")); p("hello sir".matches(".*ir$")); p("hello sir".matches("^h[a-z]{1,3}o//b.*")); p("hellosir".matches("^h[a-z]{1,3}o//b.*")); //whilte lines p(" /n".matches("^[//s&&[^//n]]*//n$")); p("aaa 8888c".matches(".*//d{4}.")); p("aaa 8888c".matches(".*//b//d{4}.")); p("aaa8888c".matches(".*//d{4}.")); p("aaa8888c".matches(".*//b//d{4}.")); */ //email //p("asdfasdfsafsf@dsdfsdf.com".matches("[//w[.-]]+@[//w[.-]]+//.[//w]+")); //matches find lookingAt /* Pattern p = Pattern.compile("//d{3,5}"); String s = "123-34345-234-00"; Matcher m = p.matcher(s); p(m.matches()); m.reset(); p(m.find()); p(m.start() + "-" + m.end()); p(m.find()); p(m.start() + "-" + m.end()); p(m.find()); p(m.start() + "-" + m.end()); p(m.find()); //p(m.start() + "-" + m.end()); p(m.lookingAt()); p(m.lookingAt()); p(m.lookingAt()); p(m.lookingAt()); */ //replacement /* Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE); Matcher m = p.matcher("java Java JAVa JaVa IloveJAVA you hateJava afasdfasdf"); StringBuffer buf = new StringBuffer(); int i=0; while(m.find()) { i++; if(i%2 == 0) { m.appendReplacement(buf, "java"); } else { m.appendReplacement(buf, "JAVA"); } } m.appendTail(buf); p(buf); */ //group /* Pattern p = Pattern.compile("(//d{3,5})([a-z]{2})"); String s = "123aa-34345bb-234cc-00"; Matcher m = p.matcher(s); while(m.find()) { p(m.group()); } */ //qulifiers /* Pattern p = Pattern.compile(".{3,10}+[0-9]"); String s = "aaaa5bbbb68"; Matcher m = p.matcher(s); if(m.find()) p(m.start() + "-" + m.end()); else p("not match!"); */ //non-capturing groups /* Pattern p = Pattern.compile(".{3}(?=a)"); String s = "444a66b"; Matcher m = p.matcher(s); while(m.find()) { p(m.group()); } */ //back refenrences /* Pattern p = Pattern.compile("(//d(//d))//2"); String s = "122"; Matcher m = p.matcher(s); p(m.matches()); */ //flags的简写 //Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE); p("Java".matches("(?i)(java)")); } public static void p(Object o) { System.out.println(o); } }