什么是正则表达式?
正则表达式,又称规则表达式,(Regular Expression,在代码中常简写为regex、regexp或RE),是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。
许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开来的,后来在广泛运用于Scala 、PHP、C# 、Java、C++ 、Objective-c、Perl 、Swift、VBScript 、Javascript、Ruby 以及Python等等。正则表达式通常缩写成“regex”,单数有regexp、regex,复数有regexps、regexes、regexen
正则表达式的诞生:
正则表达式的“鼻祖”或许可一直追溯到科学家对人类神经系统工作原理的早期研究。美国新泽西州的Warren McCulloch和出生在美国底特律的Walter Pitts这两位神经生理方面的科学家,研究出了一种用数学方式来描述神经网络的新方法,他们创造性地将神经系统中的神经元描述成了小而简单的自动控制元,从而作出了一项伟大的工作革新。
在1951 年,一位名叫Stephen Kleene的数学科学家,他在Warren McCulloch和Walter Pitts早期工作的基础之上,发表了一篇题目是《神经网事件的表示法》的论文,利用称之为正则集合的数学符号来描述此模型,引入了正则表达式的概念。正则表达式被作为用来描述其称之为“正则集的代数”的一种表达式,因而采用了“正则表达式”这个术语。
之后一段时间,人们发现可以将这一工作成果应用于其他方面。1968年,Unix之父Ken Thompson就把这一成果应用于计算搜索算法的一些早期研究。Ken Thompson将此符号系统引入编辑器QED,然后是Unix上的编辑器ed,并最终引入grep。Jeffrey Friedl 在其著作《Mastering Regular Expressions (2nd edition)》(中文版译作:精通正则表达式,已出到第三版)中对此作了进一步阐述讲解,如果你希望更多了解正则表达式理论和历史,推荐你看看这本书。
自此以后,正则表达式被广泛地应用到各种UNIX或类似于UNIX的工具中,如大家熟知的Perl。Perl的正则表达式源自于Henry Spencer编写的regex,之后已演化成了pcre(Perl兼容正则表达式Perl Compatible Regular Expressions),pcre是一个由Philip Hazel开发的、为很多现代工具所使用的库。正则表达式的第一个实用应用程序即为Unix中的 qed 编辑器。
然后,正则表达式在各种计算机语言或各种应用领域得到了广大的应用和发展,演变成为计算机技术森林中的一只形神美丽且声音动听的百灵鸟。
以上是关于正则表达式的起源和发展的历史描述,如今正则表达式在基于文本的编辑器和搜索工具中依然占据着一个非常重要的地位。
在最近的六十年中,正则表达式逐渐从模糊而深奥的数学概念,发展成为在计算机各类工具和软件包应用中的主要功能。不仅仅众多UNIX工具支持正则表达式,近二十年来,在WINDOWS的阵营下,正则表达式的思想和应用在大部分 Windows 开发者工具包中得到支持和嵌入应用。从正则表达式在Microsoft Visual Basic 6 或 Microsoft VBScript到.NET Framework中的探索和发展,WINDOWS系列产品对正则表达式的支持发展到无与伦比的高度,几乎所有 Microsoft 开发者和所有.NET语言都可以使用正则表达式。如果你是一位接触计算机语言的工作者,那么你会在主流操作系统(*nix[Linux, Unix等]、Windows、HP、BeOS等)、主流的开发语言(delphi、Scala、PHP、C#、Java、C++、Objective-c、Swift、VB、Javascript、Ruby以及Python等)、数以亿万计的各种应用软件中,都可以看到正则表达式优美的舞姿
Java对正则表达式的支持:
String类中,很多方法是专门用来支持正则:
split
package RegularExpression;
import org.junit.jupiter.api.Test;
public class testRegExp {
@Test
void test01(){
String s = "this is a book,小明今年21岁,今年2022年";
//split方法
String[] arr = s.split(" ");
for (String s3 : arr) {
System.out.println(s3);
}
// this
//
// is
// a
// book,小明今年21岁,今年2022年
for (String s3 : s.split("\\s+")) {
System.out.println(s3);
}
// this
// is
// a
// book,小明今年21岁,今年2022年
}
}
replaceAll
package RegularExpression;
import org.junit.jupiter.api.Test;
public class testRegExp {
@Test
void test01(){
String s = "this is a book,小明今年21岁,今年2022年";
//Java中replace不支持正则
String s1 = s.replace("\\d+","18");
System.out.println(s1);//this is a book,小明今年21岁,今年2022年
//replaceAll支持正则
String s2 = s.replaceAll("\\d+","18");
System.out.println(s2);//this is a book,小明今年18岁,今年18年
}
}
replaceFirst
package RegularExpression;
import org.junit.jupiter.api.Test;
public class testRegExp {
@Test
void test01(){
String s = "this is a book,小明今年21岁,今年2022年";
//replaceFirst,只替换正则匹配中第一个符号
String s3 = s.replaceFirst("\\d+", "20");
System.out.println(s3);//this is a book,小明今年20岁,今年2022年
}
}
matches
package RegularExpression;
import org.junit.jupiter.api.Test;
public class testRegExp {
@Test
void test02(){
//matches方法判断字符串是否符合某种规则
String s = "10086";
System.out.println(s.matches("\\d+"));//true
}
}
元字符
. //匹配所有符号,换行符(\n)除外
System.out.println("123".matches("..."));//true
System.out.println("a".matches("."));//true
System.out.println("A".matches("."));//true
System.out.println("$".matches("."));//true
System.out.println("+".matches("."));//true
System.out.println("_".matches("."));//true
System.out.println("\\".matches("."));//true
System.out.println("\n".matches("."));//false
批注:"\"中 \其本身具有含义,加一个\也就是"\\"将\转化成一个普通的\
\d //匹配所有的数字(0123456789)
System.out.println("123".matches("\\d\\d\\d"));//true
System.out.println("a12".matches("\\d\\d\\d"));//false
\w //匹配所有的有效符号(虽然java中&、中文是有效符号,但正则不认)
System.out.println("Aa_1".matches("\\w\\w\\w\\w"));//true
System.out.println("中国".matches("\\w\\w"));//false
System.out.println("$".matches("\\w"));//false
\s //匹配空白位(空格 \t)
System.out.println(" ".matches("\\s"));//true
System.out.println("\t".matches("\\s"));//true
[] //匹配列举括号中的某一个
// [] 匹配列举括号中的某一个
System.out.println("2".matches("[123]"));//true
System.out.println("2".matches("[135]"));//false
System.out.println("abc".matches("[123agh]bc"));//true
//-表示多少到多少
System.out.println("1".matches("[1-9]"));//true
System.out.println("d".matches("[a-z]"));//true
System.out.println("A".matches("[a-z]"));//false
//第一种因为ASK码中Z—a中还有内容,不连贯,这样写会多出几个符号
System.out.println("[".matches("[A-z]"));//true
System.out.println("[".matches("[A-Za-z]"));//false
//"[0-9A-Za-z_]等价与"\\w",表示匹配所有的有效符号
System.out.println("Aa_1".matches("\\w\\w\\w\\w"));//true
System.out.println("Aa_1".matches("[0-9A-Za-z_][0-9A-Za-z_][0-9A-Za-z_][0-9A-Za-z_]"));//true
^正则内容 //以什么什么开头
String s = "123456789";
System.out.println(s.replaceAll("^1\\d{3}","以1开头跟3位数字"));
//以1开头跟3位数字56789
^[ ] //以括号中某一个字符开头
System.out.println("12deg44".matches("^[1ae3].*"));//true
正则内容$ //以什么什么结尾
System.out.println(s.replaceAll("^1\\d{3}.*9$","以1开头跟3位数字加一些任意符号再以9结尾"));
//以1开头跟3位数字加一些任意符号再以9结尾
反义符:
\D //匹配非数字
// \D 匹配非数字
System.out.println("1".matches("\\D"));//false
System.out.println("Aa_".matches("\\D\\D\\D"));//true
\W //匹配特殊符号
// \W 匹配特殊符号
System.out.println("Aa_1".matches("\\W\\W\\W\\W"));//false
System.out.println("+".matches("\\W"));//true
\S //匹配非空白位
// \S 匹配非空白位
System.out.println(" ".matches("\\S"));//false
[^ ] //列举取反,表示当前这个位置,不能以括号中的某个字符开头
// [^ ] 列举取反,表示当前这个位置,不能以括号中的某个字符开头
System.out.println("2adg45".matches("[^234].*"));//false
System.out.println("2adg45".matches("2adg[^234].*"));//false
System.out.println("2adg645".matches("2adg[^234].*"));//true
位数问题:
* //0到多
System.out.println("hello word!".matches("h.*"));//true
System.out.println("h".matches("h.*"));//true
+ //1到多
System.out.println("hello word!".matches("h.+"));//true
System.out.println("h".matches("h.+"));//false
? //0或者1
System.out.println("1".matches("\\d?\\d"));//true
System.out.println("111".matches("\\d?\\d\\d"));//true
{m} //m个
System.out.println("1ls8fh6".matches("\\w{5}"));//false
{m,} //至少m个
System.out.println("1ls8fh6".matches("\\w{5,}"));//true
{m,n} //m~n区间范围内
System.out.println("1ls8fh6".matches("\\w{1,7}"));//true
转义符:
在正则中,通过\将有特殊含义的符号转换为普通符号
System.out.println("\\d");//\d
System.out.println("\\d".matches("\\\\d"));//true
分组:
java.util.regex:Pattern类、Matche类
| 符号的使用:
在匹配时,有多种可能的词组(如果是单个字母,直接使用[]即可)。
我们可以使用 | 分离词组来提供选择项
| 符号必须搭配()来使用
System.out.println("xxxxx@openlab.com".matches("\\w{4,20}@openlab\\.(com|org|gmail|cn|gov)"));//true
System.out.println("xxxxx@openlab.cn".matches("\\w{4,20}@openlab\\.(com|org|gmail|cn|gov)"));//true
System.out.println("xxxxx@openlab.org".matches("\\w{4,20}@openlab\\.(com|org|gmail|cn|gov)"));//true
System.out.println("xxxxx@openlab.gmail".matches("\\w{4,20}@openlab\\.(com|org|gmail|cn|gov)"));//true
System.out.println("xxxxx@openlab.gov".matches("\\w{4,20}@openlab\\.(com|org|gmail|cn|gov)"));//true
分组:分组是正则中的二次筛选
将匹配好的正则表达式,进行括号的饰选,括号中的内容,就可以做二次筛选
//分组:分组是正则中的二次筛选
//将匹配好的正则表达式,进行括号的饰选,括号中的内容,就可以做二次筛选
@Test
void test08(){
String s ="this is a book,小明今年21岁,今年2022年";
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.print(matcher.group()+" ");//21 2022
}
}
@Test
void test09(){
String s ="<img src=\"https://s1.hdslb.com/bfs/static/jinkela/long/images/eva.png\" class=\"gg-pic\" data-v-1f3401b8>";
Pattern pattern = Pattern.compile("<img (src)=\"(.*?)\".*>");
Matcher matcher = pattern.matcher(s);
Matcher matcher1 = pattern.matcher(s);
Matcher matcher2 = pattern.matcher(s);
while (matcher.find()){
System.out.print(matcher.group()+" ");//21 2022
}
//<img src="https://s1.hdslb.com/bfs/static/jinkela/long/images/eva.png" class="gg-pic" data-v-1f3401b8>
System.out.println();
while (matcher1.find()){
System.out.print(matcher1.group(1)+" ");//21 2022
}
//src
System.out.println();
while (matcher2.find()){
System.out.print(matcher2.group(2)+" ");//21 2022
}
//https://s1.hdslb.com/bfs/static/jinkela/long/images/eva.png
}
贪婪模式和懒惰模式
贪婪模式:会尽可能多的匹配能够匹配的数据
懒惰模式(非贪婪模式):会尽可能少的匹配能够匹配的数据
这两种模式都是针对多位的匹配
* // 0~多
+ // 1~多
? // 0个或1个
{m,} // 至少m个
如上四种情况下,会出现贪婪模式和懒惰模式
绝大多数编程默认采用的是贪婪模式,包括java、python等等编程
将贪婪模式转换为懒惰模式,只需要在多位匹配加上 ?号
@Test
void test010() {
String s = "<img src=\"https://s1.hdslb.com/bfs/static/jinkela/long/images/eva.png\" class=\"gg-pic\" data-v-1f3401b8>";
Pattern pattern = Pattern.compile("<img src=\"(.*)\".*>");
Pattern pattern1 = Pattern.compile("<img src=\"(.*?)\".*>");
Matcher matcher = pattern.matcher(s);
Matcher matcher1 = pattern1.matcher(s);
// 贪婪模式和懒惰模式
//贪婪模式:会尽可能多的匹配能够匹配的数据
//懒惰模式(非贪婪模式):会尽可能少的匹配能够匹配的数据
//将贪婪模式转换为懒惰模式,只需要在多位匹配加上 ?号
while (matcher.find()) {
System.out.print(matcher.group(1) + " ");//21 2022
}
//https://s1.hdslb.com/bfs/static/jinkela/long/images/eva.png" class="gg-pic
System.out.println();
while (matcher1.find()) {
System.out.print(matcher1.group(1) + " ");//21 2022
}
//https://s1.hdslb.com/bfs/static/jinkela/long/images/eva.png
}