1.概述
正则表达式:符合一定规则的表达式。
作用:专门用于操作字符串
特点:用于一些特定的符号来表示一些代码操作,简化书写
好处:可以简化对字符串的复杂操作
弊端:符号定义越多,正则越长,阅读性极差
2.常用字符介绍
1)预定义字符
\d :匹配数字,相当于[0-9]
\D:匹配非数字,相当于[^0-9]
\w:匹配单词字符,相当于[a-zA-Z0-9_]
\W:匹配非单词字符,相当于[^a-zA-Z0-9_]
\s :匹配空白字符,匹配一个空格,匹配多个空格可以在\s后面加数量词,例如字符串"a b c d"可以用"\w\s+\w\s+\w\s+\w"来匹配
\S :匹配非空白字符
. :匹配除换行符以外的任意字符
2)字符组:匹配括号内的字符
例如:[abc]匹配abc中任一字符
[^abc]匹配abc以外的任一字符
3)数量词
* :重复0次或多次
例如:"bbbbb"可以用"b*"匹配整个字符串
? :重复0次或1次
例如:"bbbb"可以用"b?"匹配单个字符,一个一个b取出
+ :重复1次或多次
例如:"bbbbb"可以用"b+"匹配整个字符串
注:*和+的区别在于+表示至少出现一次,而*也可以表示1次也不出现
{n} :重复n次
例如:"aaa,bb,ccc,dddd,eee"用"\w{3}"匹配aaa和ccc和eee,重复的次数是确定的
{n,} :重复n次及以上
例如:"aa,bbb,cccc,d,"用正则"\w{3,}"可以匹配bbb和cccc,重复的次数至少为n次,用下限没有上限
{n,m}:重复n到m次
例如:"aa,b,ccc,dddd"用正则"\w{1,2}"可以匹配aa和b,重复的次数为n到m次,上限下限都是确定的
4)限定词
懒惰限定词?
用于范围数量词后面去重复的最少次数进行匹配
*? :重复任意次,但尽可能少重复 。如 "acbadb" 正则 "a.*b" 会取到"acbadb" ,但加了限定符后,会匹配尽可能少的字符 ,结果就是"acb"和"adb"
?? :重复0次或1次,但尽可能少重复。如 "aaacb" 正则 "a.??b" 只会取到最后的三个字符"acb"
+? :重复1次或更多次,但尽可能少重复。与上面一样,只是至少要重复1次
{n,m}?:重复n到m次,但尽可能少重复。如 "aaaaaaaa" 正则 "a{0,m}?" 因为最少是0次所以取到结果为空
{n,}? :重复n次以上,但尽可能少重复。如 "aaaaaaa" 正则 "a{1,}?" 最少是1次所以取到结果为 "a"
注意:*和?都可以取到0次,如果匹配类似于"bbbb"时,用正则"b??"或者"b*?"时,取得的结果为空
+与?刚好相反,是用重复最多次数进行匹配
例如:"jhd333jed43233siu3343dijjhhdg$$h,,,"用正则"\\d{3,5}?"取得的结果是"333"、"432"、"334"
如果是"\\d{3,5}+"取得的结果是"333"、"43233"、和"3343"
5)边界匹配器
^ :匹配行的开头
$ :匹配行的结尾
注意:^和$分别用于匹配一行的开头和结尾,以^开头并以$结尾的正则表达式用于匹配整个字符串是否符合规则,而不是取出符合规则的子串
例如:与正则表达式"^a{3}$"匹配的字符串必须是"aaa",而正则"a{3}"则可以取出"bdhaaanndddaaa"中的"aaa"
\b:单词边界
不会消耗任何字符只匹配一个位置,常用于匹配单词边界。
如果我想从字符串中"This is Regex, ok?"匹配单独的单词两个字母的单词,正则就要写成 "\b\\w{2}\b",
\b 不会匹配is两边的字符,但它会识别is两边是否为单词的边界 。又如 "er\b" 可以匹配 "never" 中的 "er",但不能匹配 "verb" 中的 "er"。
\B:非单词边界
"er\B" 能匹配 "verb" 中的 "er",但不能匹配 "never" 中的 "er"。
6)捕获组与非捕获组
捕获组
(pattern):
匹配pattern并捕获结果,自动设置组号。例如正则"(abc)+d"匹配abcd或者abcabcd
(?<name>pattern)或(?'name'pattern):
匹配pattern并捕获结果,设置name为组名
\num:
对捕获组的反向引用,其中 num 是一个正整数。
例如:正则"(.)\\1+"可以用来匹配叠词,其中\1引用的是组(.)中的内容
\k<name>或\k'name':
对命名捕获组的反向引用,组名为name。
例如上面的例子可以改为"(?<test>.)\\k<test>+",\k<test>引用的是组test中的内容
捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C)))中,存在四个这样的组:
1 ((A)(B(C)))
2 \A
3 (B(C))
4 (C)
\0始终代表整个表达式。在替换中常用$取得组的内容
非捕获组只匹配结果,但不捕获结果,也不会分配组号,当然也不能在表达式和程序中做进一步处理。
(?:pattern) :匹配pattern,但不捕获匹配结果。
例如:"industr(?:y|ies)"匹配"industry"或"industries"
(?=pattern) :零宽度正向预查,匹配pattern前面的位置,不捕获匹配结果。
例如:正则"windows (?=xp|7)"匹配"windows xp"或者"windows 7"的"windows"
(?<=pattern):零宽度正向预查,匹配pattern后面的位置,不捕获匹配结果。
例如:正则"(?<=[a-zA-Z)\\d+"匹配前面是字母的数字字符串如"w4indows,7wind8owshk"中的"4"和"8"
(?!pattern) :零宽度负向预查,匹配后面不是pattern的位置,不捕获匹配结果。
例如:"123abc" 正则 "\d{3}(?!\d)"匹配3位数字后非数字的结果,可取得"123"
(?<!pattern) :零宽度负向预查,匹配前面不是pattern的位置,不捕获匹配结果。
例如:正则"(?<!w)\d+*"匹配数字前面没有w的字符串如"w4indows7windowshk"中的"7"
注意:(?:pattern)与(pattern)不同之处只是在于不捕获结果。
后面的的四个非捕获组用于匹配pattern(或者不匹配pattern)位置之前(或之后)的内容。匹配的结果不包括pattern。
写正则表达式的时候要注意,在正则表达式中有特殊含义的字符要作为通同字符来匹配时必须转义,如.在正则表达式中代表任意字符,如果单纯的匹配.要转义
Java中操作正则的主要是Pattern类和Matcher类
1)Pattern类常用方法介绍
Pattern类没有对外提供构造方法
static Pattern compile(String regex); 将给定的正则表达式编译到模式中
Macther matcher(String str); 创建匹配给定字符串与此模式的匹配器
static boolean matches(String regex, String str); 编译给定正则表达式并尝试将给定字符串与其匹配
String[] split(String str); 围绕此模式的匹配拆分给定字符串
2)Matcher类常用方法介绍
boolean matches(); 尝试将字符串与正则表达式匹配
boolean find(); 尝试查找与该模式匹配的输入序列的下一个子序列
boolean find(int start); 尝试从指定索引查找与该模式匹配的输入序列的下一个子序列
String group(); 返回由以前匹配操作所匹配的输入子序列
int start(); 返回以前匹配的初始索引
int end(); 返回最后匹配字符之后的偏移量
String replaceAll(String replacement); 替换模式与给定替换字符串相匹配的输入序列的每个子序列
String replaceFirst(String replacement); 替换模式与给定替换字符串匹配的输入序列的第一个子序列
然后用Macther matcher(String str)传入要匹配的字符串获得匹配器,最后调用Matcher类的方法进行相应的操作
1)判断
判断字符串是否符合某个规则
示例代码:
/*
* 对QQ号进行验证
* 要求:5-15位数字,第一位不能为0
*/
public class RegexDemo1 {
public static void main(String[] args) {
String qq = "34356546dddd54745";
checkQQ_1(qq);
checkQQ_2(qq);
}
//方式一:非正则表达式
public static void checkQQ_1(String qqString){
if(!qqString.startsWith("0")){
if(qqString.length()>=5 && qqString.length()<=15 ){
try {
//包装类将数字字符串转换成基本数据类型,如果不是字符串会抛出异常
long qqNum = Long.parseLong(qqString);
System.out.println("qq:"+qqNum);
} catch (NumberFormatException e) {
System.out.println("包含不合法字符!");
}
}else{
System.out.println("长度不合法!");
}
}else{
System.out.println("不能以0开头!");
}
}
//方式二:用正则表达式进行验证
public static void checkQQ_2(String qqString){
String regex = "[1-9][0-9]{4,14}";//正则表达式
Pattern p = Pattern.compile(regex);//将正则表达式编译到模板中
Matcher m = p.matcher(qqString);//通过模板对象获得匹配器,并将字符串传给匹配器
if(m.matches())//判断给定字符是否与正则表达式匹配
System.out.println("qq:"+qqString);
else{
System.out.println(qqString+"---不合法");
}
}
}
2)拆分
按照一定的规则将字符串拆分,返回一个字符串数组
示例代码:
public class Demo1 {
public static void split(){
//根据空格来切割
String str1 = " abcd ekjd eijud lkjidfji ";
// String regex1 = " +"; //匹配任意长度的空格
String regex1 = "\\s+"; //匹配任意长度的空格
String[] arr = str1.split(regex1);
System.out.println(str1+"根据空格来切割----");
for(String temp: arr)
System.out.println(8+temp+8);
//根据反斜扛来切割
String str2 = "c:\\a\\abc\\test.txt";
String regex2 = "\\\\";
String[] arr2 = str2.split(regex2);
System.out.println(str2+"根据反斜扛来切割----");
for(String temp: arr2)
System.out.println(temp);
//根据叠词来切割
String str3 = "hiaahowbbbaremmmmmyoukk";
String regex3 = "(.)\\1+";
String[] arr3 = str3.split(regex3);
System.out.println(str3+"根据叠词来切割----");
for(String temp: arr3)
System.out.println(temp);
//按.来切割
String str4 = "hey.there.is.that.ok";
String regex4 = "\\."; //.必须转义
String[] arr4 = str4.split(regex4);
System.out.println(str4+"根据.来切割----");
for(String temp: arr4)
System.out.println(temp);
}
}
注:如果按照空格切割,字符串以空格开头,返回的字符串数组中第一个字符串是空串3)获取
获取字符串中符合规则的子串
示例代码:
/*
* 将字符串中所有3个字母的单词取出
*/
public class RegexDemo1 {
public static void main(String[] args) {
String str = "hey,there ? I have something for you.";
String regex = "\\b\\w{3}\\b";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
while(m.find()){ //判断是否还有满足条件的子字符串
System.out.println(m.group());//返回满足条件的子串
}
}
}
4)替换
将满足条件的子串替换为传入的替代字符串,返回一个新的字符串
示例代码:
/*
* 将叠词替换成单个字符
*/
public class RegexDemo1 {
public static void main(String[] args) {
String str = "aabbbccddddeeeeeefffffff";
String regex = "(.)\\1+";//匹配叠词的正则表达式
Pattern p = Pattern.compile(regex);//将正则表达式编译到模板中
Matcher m = p.matcher(str);//传入字符串并获得匹配器
String newStr = m.replaceAll("$1");//将所有满足条件的子串替换成相应的单个字符
System.out.println(newStr);
}
}
5.String类对正则表达式的支持
String作为操作字符串的类也提供一些方法支持正则表达式,直接调用String类提供的方法要简单一些,不过功能有限
boolean matches(String regex);判断
String[] split(String regex);切割
String replaceAll(String regex, String replacement);替换
String replaceFirst(String regex, String replacement);
示例代码:
/*
* String类操作正则表达式
*/
public class RegexDemo1 {
public static void main(String[] args) {
//判断字符串是否为数字字符串
String str1 = "677888";
System.out.println(str1.matches("\\d+"));
//根据数字切割
String str2 = "abcd123haha567hey43784heihei";
String[] arr = str2.split("\\d+");
System.out.println(Arrays.toString(arr));
//将空格替换成#
String str3 = "hello , how are you ?";
System.out.println(str3.replaceAll("\\s+", "#"));
}
}
6.练习
练习1:
/*
练习:
需求:将下列字符串转成:我要学编程
"我我...我..我要...要...要要....学学....学学学......编编编...程...程程...."
思路:
将已有字符串变成另一个字符串。使用替换功能。
1、可以先将 . 去掉。
2、再将多个重复的内容变成单个内容。
*/
public class RegexDemo1 {
public static void main(String[] args) {
String str = "我我...我..我要...要...要要....学学....学学学......编编编...程...程程....";
//将所有的.替换成空串
String str2 = str.replaceAll("\\.+", "");
//将重复字符替换单个字符
String str3 = str2.replaceAll("(.)\\1+", "$1");
System.out.println(str3);
}
}
练习2:
/*
需求:
将ip地址进行地址段顺序的排序。
192.68.1.254 102.49.23.013 10.10.10.10 2.2.2.2 8.109.90.301
思路:
还按照字符串自然顺序,只要让他们每一段都是3位即可。
1、按照每一段需要的最多的0进行补齐,那么每一段就会至少保证有3位。
2、将每一段只保留3位。这样,所有的ip地址都是每一段3位。
*/
public class RegexDemo1 {
public static void main(String[] args) {
String str1 = "192.68.1.254 102.49.23.013 10.10.10.10 2.2.2.2 8.109.90.301";
String regex = "(\\d+)";
//补0,保证每一段至少有3位
String str2 = str1.replaceAll(regex, "00$1");
//每段保留后3位
String str3 = str2.replaceAll("0*(\\d{3})", "$1");
//按空格切割
String[] arr = str3.split("\\s+");
//将字符串存入TreeSet中,使其按照字符串的自然顺序排序
Set<String> ts = new TreeSet<String>();
for(int i=0; i<arr.length; i++)
ts.add(arr[i]);
for(String str: ts){
//去掉前面多余的0
System.out.println(str.replaceAll("0*(\\d+)", "$1"));
}
}
}
练习3:
//需求:对邮件地址进行校验。
public class RegexDemo1 {
public static void main(String[] args) {
String mailAddress="123a8098kjh78@163.com.cn";
String regex = "\\w+@[a-zA-Z0-9]+\\.com(\\.cn)?";//比较精确
String regex2 = "\\w+@\\w(\\.\\w)+";//不够精确
System.out.println(mailAddress.matches(regex));
}
}
练习4:
/*
网络爬虫(蜘蛛)
实际上是一个功能,用于搜集网络上的指定信息
需求:可用于收集邮箱,qq号等之类的信息。
应用:如通过关键字搜索blog,实际就是使用的“蜘蛛”,通过查找关键字获取相关的blog
*/
public class RegexDemo1 {
public static void main(String[] args) {
try {
//将网络文件地址封装到URL对象中
URL url = new URL("http://www.douban.com/group/topic/43622294/");
//获得URL连接对象
URLConnection urlc= url.openConnection();
//获得网络文件的输入流
InputStream is = urlc.getInputStream();
//匹配邮箱地址的正则表达式
String mailRegex = "\\w+@[a-zA-Z0-9]+\\.com(\\.cn)?";
//将正则表达式编译到模板中
Pattern p = Pattern.compile(mailRegex);
//将字节流封装成字符流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
Matcher m = null;
String line = "";
while((line=br.readLine())!=null){
m = p.matcher(line);
//找到符合条件的字符串输出
while(m.find()){
System.out.println(m.group());
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}