引言
RegularExpressions(正则表达式)非常强大,可以极大的减轻我们日常写程序时处理字符串的负担。这两天学习了一下正则表达式,做了一些学习笔记,每一个知识点后面都有详细的例子,分享给大家。文章最后是我写的一个简单的分离网页中的邮件地址的蜘蛛程序,供大家一起交流学习。
1. 用途
正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。
2. 使用Pattern和Matcher
涉及到的类为:java.util.regex.Pattern、java.util.regex.Matcher和java.lang.String
//编译一个正则表达式模式,匹配时按照其语法规则,编译之后匹配效率高
Pattern pa=Pattern.compile("[\\w]+");
//生成一个匹配器对象
Matcher m=pa.matcher("abcd1234");
Matcher m=pa.matcher("abcd1234");
//matches:
用模式匹配整个字符串
m.matches();
//find:
用模式匹配符合要求的子串(从当前位置开始匹配)
m.find();
//group:返回匹配成功的子串
m.group();
//lookingAt:每次从头开始,用模式匹配符合要求的子串
m.lookingAt();
3. 次数符号
包括:X*,X+,X?,X{n},X{n,},X{n,m}
“X*” :X出现0次或多次
“X+”:X出现1次或多次
“X?”:X出现0次或1次
“X{n}”:X正好出现n次
“X{n,}”:X至少出现n次
“X{n,m}”:X至少出现n次不多于m次
System.out.print("aaaa".matches("a{4}"));
System.out.print("127.23.0.aaa".matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"));
System.out.print("214523145234532".matches("\\d{3,100}"));
输出:
true; false; true;
4. 范围符号
“[ …]”:表示从中括号以内的字符集合里中取一个字符;
“^”:表示取反
“[^ …]”:表示从中括号以内的字符集合的补集中取一个字符
System.out.print("a".matches("[^bacdefg]"));
//下面三种都是或者的意思:or
System.out.print("a".matches("[a-zA-Z]"));
System.out.print("a".matches("[a-z]|[A-Z]"));
System.out.print("a".matches("[a-z[A-Z]]"));
//求两部分的交集,然后再取值
System.out.print("a".matches("[a-z&&[A-Z]]"));
System.out.print("a".matches("[a-z&&[^A-Z]]"));
输出:false; true; true; true; false; false;
5. 转意字符
在RegularExpressions里面一个"\"(反斜杠本身)在代码层面需要"\\\\"表示 。因为在字符串层面表示一个"\"(反斜杠本身)需要"\\"表示,字符串变成RegularExpressions时 ,"\"同样表示转意字符,即在字符串和RegularExpressions里面 "\"都是转意字符,如果真实需要一个"\"(反斜杠本身)要用一个"\"转意另外一个"\"才能表示。
System.out.print("\\".matches("\\\\"));
输出:true;
6. 边界符号
“^”没在中括号里面时,表示一行的第一个字符;$表示一行的最后一个字符。
System.out.print("hello sir".matches("^h[a-zA-Z[ ]]*r$"));
System.out.print("hello sir".matches(".+o\\s[a-z]{1,3}r$"));
//检验一篇文章是否为空白行,一行开头即换行不是空白行
System.out.print(" \n".matches("^[\\s&&[^\\n]].*\\n$"));
输出:true; true; true;
7. 替换方法
实现把匹配的字符串全部替换为特定的字符串 。
Pattern pa=Pattern.compile("java",Pattern.CASE_INSENSITIVE);
Matcher m=pa.matcher("Java JAVA i lOVE java YOU hate JaVa ,dO You jAvA 01234567890");
//sb用来装替换的之后的字符串
StringBuffer sb=new StringBuffer();
while(m.find())
{
//这里面还可以实现更加精确的控制,比如说奇数和偶数替换成不同的
m.appendReplacement(sb,"JAJA");
}
//把余下的没有匹配成功的字符串添加到末尾
m.appendTail(sb);
p(sb);
8. 分组符号
用一对“()”表示分组。RegularExpressions从左数起,"("出现的次序即为组号 。
Pattern pa=Pattern.compile("^[1-3](\\w{1,3})([0-9]*([A-Z]$))");
//1组:(\\w{1,3}) 结果:sd0
//2组:([0-9]*([A-Z]$)) 结果:1234567890H
//3组:([A-Z]$) 结果:H
Matcher m=pa.matcher("1sd01234567890H");
p(m.find());
p(m.group(2));
9. 配合Find使用的函数
Pattern pa=Pattern.compile("[\\w]+");
Matcher m=pa.matcher("abcd1234");
m.find();
使用Find方法后,可以使用group()、start()和end()方法。
group:返回匹配成功的子串;
如果找到了子串可以使用start()和end()方法查看子串在母串中的位子。
start:返回找到的子串的开始位置;
end:返回找到的子串的最后一个字符的后一个位置;
10. 匹配模式
Greedy quantifiers为贪婪的匹配方式,默认为这种方式 ;
例如:[A-Z]{5,10}它每次总是先吞10字符看能不能匹配,不能的话再吐一个再匹配 ;
Greedy quantifiers写法:[A-Z]{5,10},a+ ;
Reluctant quantifiers和Greedy quantifiers相反,总是先吞最少个数的 ;
Reluctant quantifiers写法:[A-Z]{5,10}?,a+?(后面都有一个问号);
11. 可分离网页中的邮件地址的简单蜘蛛程序
本程序仅为单机使用,即把网页下载处理。
/**
* 利用正则表达式,分离出网址中的邮箱地址
* @author 忘川
*/
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EmailSpider
{
public static void main(String[] args) throws UnsupportedEncodingException, IOException
{
System.out.print("请输入文件路径或文件夹路径:");
Scanner scan = new Scanner(System.in);
getFiles(scan.nextLine());
for (Iterator<File> i = _allFiles.iterator(); i.hasNext();)
{
File file = i.next();
String name = file.getName();
//从后缀为.html和.htm的文件中寻找邮件地址,过滤无用的文件
if (file.getName().matches(".+\\.html$") || file.getName().matches(".+\\.htm$"))
{
spider(file);
}
}
BufferedWriter bWriter = new BufferedWriter(new FileWriter(".\\email.txt"));
for (Iterator<Map.Entry<Integer, String>> i = email.entrySet().iterator(); i.hasNext();)
{
Map.Entry<Integer, String> kv = i.next();
bWriter.write(kv.getKey() + "\t" + kv.getValue() + "\r\n");
}
bWriter.flush();
bWriter.close();
}
private static List<File> _allFiles = new ArrayList<File>();
private static Map<Integer, String> email = new HashMap<Integer, String>();
private static int Number = 0;
private static void spider(File file) throws FileNotFoundException, UnsupportedEncodingException, IOException
{
InputStreamReader inre = new InputStreamReader(new FileInputStream(file), "GBK");
BufferedReader bReader = new BufferedReader(inre);
String line = "";
//编译一个正则表达式
Pattern pa = Pattern.compile("[\\w[-.]]+@[\\w]+\\.[a-zA-Z[.]]+");
Matcher m = null;
while ((line = bReader.readLine()) != null)
{
//生成一个匹配器
m = pa.matcher(line);
//寻找匹配的子串
while (m.find())
{
//返回匹配成功(即符合模式)子串
String adress = m.group();
//去掉email后面的“.”,发生的概率1/100左右
while (adress.endsWith("."))
{
adress = adress.substring(0, adress.length() - 1);
}
//去掉重复的email地址
if (!email.containsValue(adress))
{
email.put(Number++, adress);
}
}
}
bReader.close();
}
private static void getFiles(String path)
{
File file = new File(path);
if (file.isFile())
{
_allFiles.add(file);
return;
} else
{
File[] list = file.listFiles();
for (File f : list)
{
getFiles(f.getPath());
}
}
}
}
部分结果:
序号 邮件地址
435 szsl8@sina.com
436 yaxu520@163.com
437 xhngweigrass@yahoo.com.cn
438 915650881@qq.com
439 aji301314@163.com
440 197008946@qq.com
441 294470767@qq.com
442 imei598@163.com
443 16717890@QQ.COM
444 618168737@qq.com
445 lubirdhui@163.com
446 benmaofc7496@163.com
447 yp867107@163.com
测试了几个网页,从其中分离处理1000多个邮件地址,以上为部分结果。公布的邮箱地址经过手动脱敏处理,仅用来说明程序的性能,不涉及个人隐私。垃圾邮寄每日困扰这我们的生活,希望大家谨慎在网页上留下邮箱地址。