正则表达式是作用于字符串的一种校验语法,通过特定字符(0-9,A-Z,a-z等)组合而形成的一种具有校验功能的"校验字符串"
1 语法规则
1.1 匹配一个字符(字母大写是取反)
\d匹配数字(0-9)
\w匹配数字(0-9),字母(A-Z,a-z),下划线(_)
\s空白符(空格,换行符,制表符)
\n换行符
.匹配任意一个字符(不包括换行符\n) //常用[\s\S]表示所有字符
"2".matches("\\d"); //结果为true
"测".matches("\\D"); //结果为true
"2".matches("\\w"); //结果为true
"A".matches("\\w"); //结果为true
"a".matches("\\w"); //结果为true
"_".matches("\\w"); //结果为true
"测".matches("\\W"); //结果为true
" ".matches("\\s"); //结果为true
"测".matches("."); //结果为true
"\n".matches("."); //结果为false
"\n".matches("[\\s\\S]"); //结果为true
1.2 自定义匹配一个字符([^regex]表示取反)
将自定义regex放在[ ]中,只要符合其中的一个字符即可
-为连接符,[A-Z]表示A~Z中的一个
匹配汉字采用unicode码 //匹配所有汉字[\u4e00-\u9fa5]
"f".matches("[a-z]"); //结果为true
"f".matches("[^a-z]"); //结果为false
"F".matches("[^a-z]"); //结果为true
"2".matches("[^a-z]"); //结果为true
"正".matches("[\u6b63]"); //结果为true
"则".matches("[\u4e00\u9fa5]"); //结果为false
"表".matches("[\u4e00-\u9fa5]"); //结果为true
1,3 量词:规定前"一个"表达式出现的次数
{n}匹配n次
{m,}至少匹配m次
{m,n}至少匹配m次,至多匹配n次
?匹配0次或1次,相当于{0,1}
+至少匹配一次,相当于{1,}
*匹配0次或任意次,相当于{0,}
"123".matches("\\d{3}"); //结果为true
"123".matches("\\d{2}"); //结果为false
"123".matches("\\d{2,}"); //结果为true
"123".matches("\\d{2,4}"); //结果为true
"1".matches("\\d?"); //结果为true
"12345".matches("\\d+"); //结果为true
"12345".matches("\\d*"); //结果为true
+和*匹配模式都是"贪婪模式(即尽可能多地匹配)"
只要在它们后面加上一个?就可以变为"非贪婪模式(即尽可能少地匹配)"
例: "<h1>一号字体</h1>"用"<.*>"匹配到1处"<h1>一号字体</h1>"
"<h1>一号字体</h1>"用"<.*?>"匹配到2处"<h1>一号字体</h1>"
1.4 零度断言
先解释一下名词:"零度断言"分为"零度"和"断言"
零度:就是指没有宽度,即向前或向后不占字符
断言:指明在特定内容之前或之后并满足相应规则的内容,类比生活中常说的"我断定什么",例如"我断定他身后站着一个人"
断言符号:①exp1(?=exp2) 匹配exp1,但同时exp1后面的内容要满足exp2
②exp1(?!exp2) 匹配exp1,但同时exp1后面的内容不满足exp2
③(?<=exp2)exp1 匹配exp1,但同时exp1前面的内容要满足exp2
④(?<!exp2)exp1 匹配exp1,但同时exp1前面的内容不满足exp2
"1regex456apple789".split("[a-z]{5}(?=\\d{3})"); //返回数组[1, 456, 789]
"1regex56apple789".split("[a-z]{5}(?!\\d{3})"); //返回数组[1, 56apple789]
"1regex456apple789".split("(?<=\\d{3})[a-z]{5}"); //返回数组[1regex456, 789]
"1regex456apple789".split("(?<!\\d{3})[a-z]{5}"); //返回数组[1, 456apple789]
1.5 捕获组
把被"子表达式"匹配到的数据保存在内存中,并按数字编号形成"数字捕获组"或按显式命名形成"命名捕获组",方便后面引用(正则表达式内部引用,正则表达式外部引用)
1.5.1 数字捕获组
分组规则:从整个正则表达式从左往右找,(第n次出现即捕获组编号就是n,同时和与之对应的)以及两者包含的表达式共同形成n号捕获组,第0组则表示整个正则表达式
捕获组 | 正则表达式 | 匹配内容 |
0 | (\d{4})-(\d{2}-(\d\d)) | 2022-08-21 |
1 | (\d{4}) | 2022 |
2 | (\d{2}-(\d\d)) | 08-21 |
3 | (\d\d) | 21 |
Pattern pattern = Pattern.compile("(\\d{4})-(\\d{2}-(\\d\\d))");
Matcher matcher = pattern.matcher("2022-08-21");
while (matcher.find()) {
System.out.println(matcher.group(0));
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
System.out.println(matcher.group(3));
}
//2022-08-21
//2022
//08-21
//21
1.5.2 命名捕获组
语法规则:(?<name>regex)
例: (?<hour>(0[1-9]|1\d|2[0-3])):(?<minute>([0-5]\d)):(?<second>([0-5]\d))
捕获组 | 正则表达式 | 匹配内容 |
0 | ((0[1-9]|1\\d|2[0-3])):(([0-5]\\d)):(([0-5]\\d)) | 16:14:22 |
hour | ((0[1-9]|1\\d|2[0-3])) | 16 |
munite | (([0-5]\\d)) | 14 |
second | (([0-5]\\d)) | 22 |
Pattern pattern = Pattern.compile("(?<hour>(0[1-9]|1\\d|2[0-3])):(?<minute>([0-5]\\d)):(?<second>([0-5]\\d))");
Matcher matcher = pattern.matcher("16:14:22");
while (matcher.find()) {
System.out.println(matcher.group(0));
System.out.println(matcher.group("hour"));
System.out.println(matcher.group("minute"));
System.out.println(matcher.group("second"));
}
//16:14:22
//16
//14
//22
1.5.3 非捕获组
博主个人认为叫"失效捕获组"更为贴切,因为它会让原本的捕获组失效
语法规则:(?:regex)
例: (?:\d{4})-(\d{2}-(\d\d))
捕获组 | 正则表达式 | 匹配内容 |
0 | (\d{4})-(\d{2}-(\d\d)) | 2022-08-21 |
1 | (\d{2}-(\d\d)) | 08-21 |
2 | (\d\d) | 21 |
Pattern pattern = Pattern.compile("(?:\\d{4})-(\\d{2}-(\\d\\d))");
Matcher matcher = pattern.matcher("2022-08-21");
while (matcher.find()) {
System.out.println(matcher.group(0));
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
}
//2022-08-21
//08-21
//21
1.5.4 捕获组的引用
捕获组引用的是捕获的"内容",即匹配结果,而不是子表达式本身
1.5.4(1) 外部引用
通过Matcher对象的group(int)或group(name)方法可以获取对应的捕获组,并返回捕获组匹配后的数据
1.5.4(2) 内部引用
在正则表达式内部进行的引用也可以叫做"反向引用"
数字捕获组反向引用\n
命名捕获组反向引用\k<name>或\k'name'
"33a55".matches("(\\d)\\1[a-z]+(\\d)\\1"); //返回false
"33a53".matches("(\\d)\\1[a-z]+(\\d)\\1"); //返回true
//regex中两个\1指的都是编号为1的捕获组
反向引用使用场景:
例: 查找一串字母中成对出现的字母
Pattern pattern = Pattern.compile("([a-z])\\1");
Matcher matcher = pattern.matcher("aaabbcdlkkkkkjbcc");
while (matcher.find()) {
System.out.println(matcher.group());
}
//aa
//bb
//kk
//kk
//cc
例: 号码中是否有6位连续相同的数字
Pattern pattern = Pattern.compile("(\\d)(?=\\1{5})");
Matcher matcher = pattern.matcher("25235235234");
System.out.println(matcher.find()); //不存在
Pattern pattern = Pattern.compile("(\\d)(?=\\1{5})");
Matcher matcher = pattern.matcher("26666667891");
System.out.println(matcher.find()); //存在
1.5.4(3) 正则替换
数字捕获组用$n指代,命名捕获组用${name}指代
"123abc456def789".replaceAll("(\\d+)([a-z]+)", "$1换"); //123换456换789
"123abc456def789".replaceAll("(?<one>\\d+)(?<two>[a-z]+)", "${two}换"); //abc换def换789
1.6 修饰符
JS环境的修饰符不写在正则表达式里,而写在表达式之外,指定额外的匹配策略
1.6.1 字母大小写
JAVA环境: (?i)之后的表达式字母不区分大小写,(?i)不算捕获组
JS环境: 表达式外添加i
"ABC".matches("(?i)abc"); //返回true
"aBC".matches("a(?i)bc"); //返回true
"abc".matches("a((?i)b)c"); //返回true
1.6.2 全局匹配
JS环境: 表达式外添加g修饰符可以查找字符串中所有的匹配项,不加g则只查找第一次匹配项
'a1bk3u4f'.match(/\d/); //返回["1"]
'a1bk3u4f'.match(/\d/g); //返回["1","3","4"]
1.6.3 多行匹配
JS环境: 表达式外添加m修饰符可以匹配多行
1.7 其它符号
1.7.1 分隔符
符合|前的表达式或符合|后的表达式,通常将|前后的表达式约束在( )中
"D123".matches("[A-Z]\\d{3}|\\d{4}"); //"D123"或"1234"符合
"D1234".matches("[A-Z](\\d{3}|\\d{4})"); //"D123"或"D1234"符合
1.7.2 分组
写在( )中的表达式称为子表达式,子表达式是一个整体(即一个分组),其后的量词是对这个整体起作用
"regexx".matches("regex{2}"); //结果为true
"regexregex".matches("(regex){2}"); //结果为true
1.7.3 定位符
^表示开头和$表示结尾,通常将^和$配合使用
例: "<h1>一号字体</h1>"用"<.*?>"匹配到2处"<h1>一号字体</h1>"
"<h1>一号字体</h1>"用"^<.*?>$"匹配到1处"<h1>一号字体</h1>"
\b表示单词边界,\B表示非单词边界
例: "this is a island"用"is"匹配到3处"this is a island"
"this is a island"用"\bis"匹配到2处"this is a island"
"this is a island"用"is\b"匹配到2处"this is a island"
"this is a island"用"\bis\b"匹配到1处"this is a island"
1.7.4 特殊字符
如果想匹配特殊字符($ ( ) * + . [ ? \ ^ { |),需要在特殊字符前加上转义符\
例: 想匹配?,因为?是特殊字符,因此要写成\?,而\在字符串中也代表转义符,因此写法应该是\\?
"?".matches("\?"); //写法错误
"?".matches("\\?"); //结果为true
例: 同理,想匹配\,应该写成\\,如果正则参数写在字符串中,则应该写成\\\\
"\\".matches("\\"); //写法错误
"\\".matches("\\\\"); //结果为true
2 使用环境
2.1 JAVA环境
str.matches(regex) str完全匹配regex 本质是Pattern.matches(regex,str)
str.replaceFirst(regex,str2) 将str中第一次满足regex的内容替换为str2 本质是Matcher.replaceFirst(replacement)
str.replaceAll(regex) 将str中所有满足regex的内容替换为str2 本质是Matcher.replaceAll(replacement)
str.split(regex) 以regex为分隔,返回String[ ] //①如果原字符串为”空字符串”,返回的数组长度为1,数组第一个元素为”空字符串” ②如果字符串第一个字符满足regex分隔,那么返回的数组第一个元素是”空字符串” 本质是Pattern.compile(regex).split(str)
"23456f".matches("\\d+"); //返回false
"12ab33db56cf".replaceFirst("\\d+", "换"); //返回"换ab33db56cf"
"12ab33db56cf".replaceAll("\\d+", "换"); //返回"换ab换db换cf"
Arrays.toString("12ab33db56cf333".split("\\d+")); //返回[, ab, db, cf]
2.2 JavaScript环境
创建正则表达式对象RegExp: 将正则写在/regex/中,修饰符写在/regex/外
var reg = /regex/gim;
reg.test(str) 字符串中是否有符合正则的内容(有一个即可,无需完全匹配),返回boolean
// 如果想"完全匹配",可以加上定位符^和$,例/^\d{3}$/
var reg = /\d/;
reg.test('阿发123'); //返回true
str.search(reg) 搜索字符串中第一次符合reg的位置(从0开始),没有返回-1
'abecbbf'.search(/BB/i); //返回4
str.match(reg) 搜索字符串中符合reg的内容,并以数组的形式返回,没有就返回null
'a1bk3u4f'.match(/\d/); //返回["1"]
'a1bk3u4f'.match(/\d/g); //返回["1","3","4"]
str.replace(reg,str2) 查找str中符合reg的内容,并用str2替换
'我是湖北的,湖北汉族人'.replace(/湖北/,'中国'); //返回"我是中国的,湖北汉族人"
'我是湖北的,湖北汉族人'.replace(/湖北/g,'中国'); //返回"我是中国的,中国汉族人"
str.split(reg) 以str中符合reg的内容为分割,返回数组
'1ab23df65sf23'.split(/\d+/); //返回[ "", "ab", "df", "sf", "" ]
str.trim() 去掉str首尾空格
3 用法示例
账户金额 ^\d{1,3}(,\d{3})*.\d{2}$ 例:5,231,247.08
电话号码 ^(13[0-9]|14[01456789]|15[012356789]|16[0256789]|17[0-9]|18[0-9]|19[012356789])\d{8}$ 例:15176767856