正则表达式入门
正则表达式入门
正则表达式入门
一.正则表达式元字符
1.转义元字符
\
将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符
1.如果我们想匹配一些特殊符号本身,比如 . * + ( ) $ / \ ? [ ] ^ { } | 我们需要使用转义符\
如果不转义,它将有特殊含义,因此使用转义符\可以将它们的意义变为本身
2.d代表字母d,但是\d就代表0-9的一个数字了,这也是转义符的作用,将某个符号改变它原来的意思,这样的类型也不是很多,而且好记,下面会讲到
注意:Java语言环境中两个\\才代表一个\
2.特殊元字符
[ ]代表匹配的为可选项,即该括号里面的不一定全匹配,无顺序的概念,该括号里面代表的仅仅是可选择的符号列表
()标记一个子表达式的开始和结束位置,该括号里面要全部匹配才可以,并且有顺序概念。子表达式可以获取供以后使用
例如,[adc]可匹配a、d、c、ad、ac、adc、dc、ca等(但ca这种的实际是c单独匹配,a单独匹配)
但(abc)只能匹配abc
[] 可接受的字符列表
[adc] 可匹配a、d、c、ad、ac、adc等
表示可匹配 a | c这三个符号
但是 | 在()里代表 或者的意思,特例
[^] 不可接受的字符列表
( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用
- 连字符
例如a-z代表小写的26个字母,A-Z代表26个大写字母,0-9代表10个数字
\d 匹配任意一个数字
\D 匹配一个任意非数字字符
\w 匹配一个数字或者字母(大小写均可)或者 _
\W 匹配一个非数字非字母(包括大小写)非 _ 的字符
. 匹配一个除\n以外的任意字符
| 指明两项之间的一个选择。要匹配 |,请使用 \|
3.限定元字符
这里子表达式可以是单个字符,也可以是[]括号括起来的,也可以是()括起来的,两种还是有区别的,例如(abc){3}代表abc要出现3次才匹配,而[abc]{3}代表前面的abc任意出现三次即可
{n} n 是一个非负整数。匹配前面子表达式确定的 n 次
以ab开头,且必须跟3个c才可以
{n,} n 是一个非负整数。至少匹配前面子表达式n 次
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次
* 匹配前面的子表达式零次或多次
+ 匹配前面的子表达式一次或多次
? 匹配前面的子表达式零次或一次
非贪婪
当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串
示例1
abc 是匹配ab开头,c可以0次或者多次,如图*
但是加一个?,变成非贪婪,让*取最小值,0次
示例2
abc+匹配ab开头,c可以1次或者多次
但是加一个?,让+取最小值,也就是1次
4.定位元字符
\b 匹配一个单词边界,也就是指单词和空格间的位置
\B 匹配非单词边界 (非空格才可以)
^ 在[]外用,代表必须开始的位置
^abc必须以abc开始
$ 代表结束的位置
$abc必须以abc结尾
^abc$ 必须是abc,不能包含任何的其他
5.非打印元字符
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]
\f 匹配一个换页符。等价于 \x0c 和 \cL
\n 匹配一个换行符。等价于 \x0a 和 \cJ
\r 匹配一个回车符。等价于 \x0d 和 \cM
6.修饰符
正则表达式 - 修饰符(标记)
标记也称为修饰符,正则表达式的标记用于指定额外的匹配策略
标记不写在正则表达式里,标记位于表达式之外,格式如下:
/pattern/flags
i ignore - 不区分大小写
将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别
g global
- 全局匹配 查找所有的匹配项
m multi line
- 多行匹配 使边界字符 ^ 和 $ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾
s
特殊字符圆点 . 中包含换行符 \n 默认情况下的圆点 . 是匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, . 中包含换行符 \n
二.正则表达式基础
1.捕获分组
非命名分组
用圆括号 () 将所有选择项括起来,相邻的选择项之间用 | 分隔
() 表示捕获分组,() 会把每个分组里的匹配的值保存起来, 多个匹配值可以通过数字 n 来查看(n 是一个数字,表示第 n 个捕获组的内容)
捕获的含义就是我们保留了分组内容,可以通过分组序号引用
(\d\d)(\d\d)表示匹配4个连续的数字,并且第一个数字和第二个是第一组,第三四是第二组!组号从1开始
public class Regex01 {
public static void main(String[] args) {
String content = "ABCab123@45";
String regStr = "(ab|cd)(12|34)";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()){
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
}
}
}
命名分组
public class Regex01 {
public static void main(String[] args) {
String content = "ABCab123@cd34";
String regStr = "(?<g1>ab|cd)(?<g2>12|34)";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()){
System.out.println(matcher.group("g1"));
System.out.println(matcher.group("g2"));
}
}
}
2.非捕获分组
?= ?<= ?! ?<! ?:主要涉及这五种
直接看例子
exp1 (?:exp2)
查找exp1连接exp2的匹配
exp1(?=exp2):查找 exp2 前面的 exp1
查找!前面的juman
(?<!exp2)exp1:查找前面不是 exp2 的 exp1
(?<=exp2)exp1:查找 exp2 后面的 exp1
exp1(?!exp2):查找后面不是 exp2 的 exp1
3.反向引用
()捕获分组后,可以在这个括号后面使用,如果在正则表达式内部,那么用\分组号
引用,如果在正则表达式外部,用$分组号
引用
这里的反向其实也没有实际意义,其实就是引用分组的概念
(\d\d)\1\1代表我首先匹配两个数字,并且后面的4个数字要和我相同,也即匹配3组连续相同的数字,\1代表引用分组1
public class EncryptPhoneNumber extends UDF implements Serializable {
/**
* 重载evaluate方法 实现函数的业务逻辑
* @param phoNum 入参:未加密手机号
* @return 返回:加密后的手机号字符串
*/
public String evaluate(String phoNum){
String encryptPhoNum = null;
//手机号不为空 并且为11位
if (StringUtils.isNotEmpty(phoNum) && phoNum.trim().length() == 11 ) {
//判断数据是否满足中国大陆手机号码规范
String regex = "^1[3-9]\\d{9}$";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(phoNum);
if (m.matches()) {//进入这里都是符合手机号规则的
//使用正则替换 返回加密后数据
encryptPhoNum = phoNum.trim().replaceAll("(\\d{3})\\d{4}(\\d{4})","$1****$2");
}else{
//不符合手机号规则 数据直接原封不动返回
encryptPhoNum = phoNum;
}
}else{
//不符合11位 数据直接原封不动返回
encryptPhoNum = phoNum;
}
return encryptPhoNum;
}
}
三.Java使用正则表达式
1.基本使用
用Pattern.compile(regStr);来确定正则表达式
用pattern.matcher(content);来匹配内容
public class Regex01 {
public static void main(String[] args) {
String content = "ABCab123@cd34";
String regStr = "(ab|cd)(12|34)";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()){
System.out.println(matcher.group(0));
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
}
}
}
**matcher.find()**代表找到了符合匹配的东西
接下来是重点,我们debug一下
发现group数组是一个初始值为-1,大小为20的数组!
当我们找到了第一个符合要求的匹配时,数组变了,变了前6位
变的是什么呢?我们找到的第一个匹配结果是ab12,这四个字符对应的开始索引是3,结束索引是6,我们于是明白了,group第一位(下标为0)是找到的匹配结果的开始索引,第二位是结束索引+1,于是不难看出,第三位是第一组开始的索引,第四位是第一组结束的索引+1,第五位是第二组开始的索引,第六位是第二组结束的索引+1
当我们运行到第二个结果cd34的时候,发现这个数组竟然发生了覆盖!!
也就是说group数组只会匹配一个符合条件的结果,前两位索引正是这个结果的开始和结束索引+1,后面的就是具体到每个组了!!
2.String类结合正则
替换
s.replaceAll
public class Regex01 {
public static void main(String[] args) {
String s = "12345678910";
String s1 = s.trim().replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
System.out.println(s1);
}
}
判断
s.matches
public class Regex02 {
public static void main(String[] args) {
String s = "12345678910";
boolean b = s.matches("^123\\d{8}");
System.out.println(b);
}
}
分割
s.split
数字字符分割
public class Regex03 {
public static void main(String[] args) {
String s = "12345-678!gh-910";
String[] split = s.split("\\-|\\!");
for (String s1 : split) {
System.out.println(s1);
}
}
}
更多推荐
所有评论(0)