简介:
正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表通常被用来检索\替换\分割\匹配那些符合某个模式(规则)的文本。
1、概念:
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
正则表达式的特点是:
1. 灵活性、逻辑性和功能性非常的强;
2. 可以迅速地用极简单的方式达到字符串的复杂控制。
3.如果大规模数据的校验最好不要使用正则,效率非常差。CPU的处理能力会全部耗费在处理这几个正则上。
由于正则表达式主要应用对象是文本,因此它在各种文本编辑器场合都有应用,小到著名编辑器EditPlus,大到Microsoft Word、Visual Studio等大型编辑器,都可以使用正则表达式来处理文本内容。
给定一个正则表达式和另一个字符串,我们可以达到如下的目的:
1. 给定的字符串是否符合正则表达式的过滤逻辑(称作“匹配”);
2. 可以通过正则表达式,从字符串中获取我们想要的特定部分。
正则表达式对字符串的常见操作:
1、匹配 其实使用的就是String类中的matches()方法
2、切割 其实使用的就是String类中的split()方法
3、替换 其实使用的就是String类中的replaceAll()方法
4、获取
2-正则表达式(常见的规则)
字符类
[abc] ---- a、b 或 c(简单类)(某一位上只能是a,b,c)
[^abc] ---- 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] ---- a 到 z 或 A 到 Z,两头的字母包括在内(范围)
[a-d[m-p]] ---- a 到 d 或 m 到 p:[a-dm-p](并集)
[a-z&&[def]] ---- d、e 或 f(交集)
[a-z&&[^bc]] ---- a 到 z,除了 b 和 c:[ad-z](减去)
[a-z&&[^m-p]] ---- a 到 z,而非 m 到 p:[a-lq-z](减去)
预定义字符类
. ---- 任何字符(与行结束符可能匹配也可能不匹配)
\d ---- 数字:[0-9]
\D ---- 非数字: [^0-9]
\s ---- 空白字符:[ \t\n\x0B\f\r]
\S ---- 非空白字符:[^\s]
\w ---- 单词字符:[a-zA-Z_0-9]
\W ---- 非单词字符:[^\w]
边界匹配器
^ ---- 行的开头
$ ---- 行的结尾
\b ---- 单词边界
\B ---- 非单词边界
\A ---- 输入的开头
\G ---- 上一个匹配的结尾
\Z ---- 输入的结尾,仅用于最后的结束符(如果有的话)
\z ---- 输入的结尾
Greedy 数量词
X? ---- X,一次或一次也没有 (举例演示)
X* ---- X,零次或多次
X+ ---- X,一次或多次
X{n} ---- X,恰好 n 次
X{n,} ---- X,至少 n 次
X{n,m} ---- X,至少 n 次,但是不超过 m 次
[注]:上面的规则中,发现"^"既可以作为取非的意思又可以作为行的开头,怎么回事呢?
这是由于则表达式中字符在[ ]里面和不在[ ]中意义会发生一些变化,比如:
\b指单词边界,但是在[ ]中就是指退格符,表示一个符号
^一般指起始位置,而在[ ]的开头指 ''非''的意思,即排除
在[ ]中,字符"-"有不同含义,表示普通字符,或者标志一个区间(在两个有序字符中间).
方法演示:
1 匹配
/*
* matches()方法
*/
public static void testMatch(){
//匹配手机号码
String tel = "13148098167";
String regex = "1[358][0-9]{9}";
//String regex = "1[358]\\d{9}";
boolean b = tel.matches(regex);
System.out.println(tel+" : "+b);
}
2 分割:
/*
*
* split()方法
*
*/
public static void method2(){
//String str = "123tttt456mmmmmm789";//组
//String [] names = str.split("(.)\\1+");
// (.)\\1+意思是:以连续重复的字符为一组分割点,其中()表示分组的意思
for (String name : names) {
System.out.println(name);
}
}
()表示分组:
例如:((A)(B(C))) 就表示大分组中包含这样几个小的分组
(A) -- (B(C)) -- (C)--(B(C))//B不是一个分组,B和(C)才是一个完整分组
3 替换
String str = "zhangsanttttxiaoqiangmmmmmmzhaoliu";
str = str.replaceAll("(.)\\1+", "#"); // 使用 # 替换
// 1代表第一组 1是普通数字 所以加 \\1
// 直接用数字编号 代表组 1 就代表第一组
str = str.replaceAll("(.)\\1+", "$1");
// 使用单个自身替换多个连续的自身,这个$1表示第一个分组,那么为什么使用$1就可以替换全部重复字符呢?
//而不需要$2$3$4......?这是因为无法确定有多少分组
//所以无法使用$2$3代替第二个分组,第三个分组.就使用循环方式实现
String tel = "15856567788";// 158****7788
tel = tel.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
//这里组的数量是确定的,所以可以使用$2表示第二组
Pattern 类:
* 指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建 Matcher 对象,
* 依照正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。
(1)将正则规则进行对象的封装
Pattern p = Pattern.compile("a*b");
(2)通过正则对象的matcher方法将字符串相关联。获取要对字符串操作的匹配器对象Matcher。
Matcher m = p.matcher("aaaaab");
(3)通过Matcher匹配器对象的方法对字符串进行操作。
boolean b = m.matches();
4 获取:
// 获取三个字母组成的单词
import java.util.regex.Matcher;
import java.util.regex.Pattern;
String str = "this is a sad story but i feel happy";
String regex = "[a-z]{3}";
String regex = "\\b[a-z]{3}\\b";//单词边界:\\b
//1、将正则规则进行对象的封装
Pattern p = Pattern.compile(regex);
//2、通过正则对象获取匹配器对象
Matcher m = p.matcher(str);
//3、使用Matcher对象的方法对字符串进行操作。
// 查找find();
// m.find();//只查找一次
// System.err.println(m.group());//获取匹配的子序列
while(m.find()){
System.out.println(m.group());//获取匹配的子序列
System.out.println(m.start()+" : "+m.end());//
}
//
Pattern p = Pattern.compile("\\d{3,5}");
String s = "123-34345-234-00";
Matcher m = p.matcher(s);//注意,matcher是全局匹配.这里明显是不匹配.但是,由于s里面的"123"和p是匹配的,所以他会将这三个数字去掉.下次调用匹配方法的时候,是将剩余的字符串来继续匹配
p(m.matches());
m.reset();//重新设置到最开始..如果这里没有这一步,下面的匹配将受到影响...
p(m.find());//find方法是部分匹配..也就是说,只要找到有匹配的字符段就算匹配...但是,他和matcher方法一样也会将s里面已经匹配的字符去掉....这里匹配的是"123"
p(m.find());//剩余的字符"-34345-234-00"继续匹配.匹配的是"34345"
p(m.find());//剩余字符"-234-00"继续匹配.匹配的是"234"
p(m.find());//剩余字"-00"符继续匹配.已经没有匹配的字符,所以这个方法不匹配.
p(m.lookingAt());//lookingAt方法也是部分匹配,但是他都是从最开始进行匹配...所以每次都是匹配"123"会有陷入死循环的危险
注意:如果是需要获取符合正则条件的数据,一般是用 Pattern
方法:
matches - 永远匹配整个字符串
find - 找匹配的子字串 如果 matches 和 find 一起使用 会影响 find的结果,可以使用reset恢复结果
lookingAt - 每次都是从头开始找,可能陷入死循环,具体见代码
start_end
p(m.start() + "-" + m.end());//输出匹配的子字符串的起始位置和结束位置 0-3
[注]:如果没有匹配 会报错
String_Replacement
//replacement
Pattern.CASE_INSENSITIVE 忽略大小写
用法:Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
replaceAll()
appendReplacement()
appendTail()
分组
//group
Pattern p = Pattern.compile("\\d{3,5}[a-z]{2}");//3-5位的数字,2位的字母
Pattern p = Pattern.compile("(\\d{3,5})([a-z]{2})");//3-5位的数字,2位的字母 小括号代表分组
String s = "123aa-34345bb-234cc-00";
Matcher m = p.matcher(s);
p(m.group());//打印 所有
p(m.group(1));// 打印 数字 为什么是1 因为 数字是第一组
p(m.group(2));// 打印 字母 为什么是2 因为 数字是第二组