前情引入
- 什么是正则表达式?
正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本—— 来自百度的解释。
说得简单一点,正则表达式就是一套模式,一套对应规则。就像我们使用的字符集一样,有对应关系。字符集是用来与我们使用的语言文字对应的,正则表达式则是用来与字符对应的。
比如在ASCII码中,97对应的是小写字母"a",在正则表达式中,"\w" 就对应任何一个单词字符(数字、字母和下划线) - 正则表达式有什么用?
有了这套对应关系之后,我们就可以很方便的对字符串(或称文本)进行各种操作,检索、替换等等。正则表达式被广泛的应用在计算机的各个领域,主流的开发语言(java、c、python、c++、js……)和数以亿万记的软件中,都可以看到正则表达式的身影。
正则表达式和java语言本身没有关系
入门初体验
在java中,java.util.regex包下的类都是和正则相关的类,我们来简单的体验一下
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegularTest02
{
public static void main(String[] args)
{
regularTest();
}
public static void regularTest()
{
Pattern pattern = Pattern.compile("\\w");//被编译后的表达式对象
Matcher matcher = pattern.matcher("abc123##");//需要匹配的对象(就是需要操作的字符串)
boolean isMatch = matcher.matches();//matches这个方法是尝试匹配整个字符串
//下面这种写法和上面的那种写法效果是一样的,只是上面的写法,可以将表达式对象和需要匹配的对象复用
boolean isMatch1 = Pattern.matches("\\w", "a");//也是尝试匹配整个字符串
System.out.println(isMatch);
System.out.println(isMatch1);
}
}
13行:表示获取一个被编译之后的正则表达式对象
14行:表示需要匹配的对象,也就是要对那个字符串进行操作
16行:matches方法是尝试匹配整个目标字符串,如果匹配成功,则返回true,否则返回false
如果是初学者,可能对于上面的内容不是很理解,但是java这种面向对象的语言,有这个好处:我们不需要知道为什么要这么做,而只需要知道我们如果要完成一个功能,需要怎么做(对于初学者而言)
我们需要怎么做?读文档、查资料,看哪个类、哪个方法能帮我们完成功能。现在我已经告诉你了,你只需要像上面这么做,就能达到我们想要的目的,所以,你照着上面这么写就好了。
需要注意一点的是,在java中,反斜杠 “\” 具有特殊含义:转义,所以想要表示一个普通的反斜杠,一个反斜杠是不行的,要用两个反斜杠,前面那个反斜杠的作用就是转义,将后面这个反斜杠转义为一个普通的反斜杠。如果里是在理解不了的话,你就记死结论:java中两个反斜杠表示一个普通的反斜杠。
顺便提一嘴,在正则表达式中,反斜杠一样具有特殊含义,所以要在java中使用正则表达式表示一个普通的反斜杠,需要用四个反斜杠表示一个普通的反斜杠,后面会讲到。
现在我们来推测一下程序的运行结果,前面我已经说了,"\w" 就对代表何一个单词字符(数字、字母和下划线),那么控制台应该打印true还是false呢?如果将14行中的 “abc123##” 换成 “abc” 呢?如果换成 “a” 或其他的单词字符呢?可以自己去测试一下,再来看结果分析。
前两个都是false,因为 “\w” 只代表一个单词字符,而前两个目标字符串里都不止一个字符。如果换成 “a” 或其它的单词字符,肯定就是true了。
正式认识
前面我们简单的使用了正则表达式,下面我们来系统的认识一下正则的语法规则。简单的正则表达式大致由两部分组成:匹配内容和匹配次数,复杂的还有其它一些东西。
上面我们说的,"\w" 代表任意一个单词字符,但是如果我们要代表两个单词字符呢?三个或者更多个呢?
三个常用的表示次数的符号:
- ? :表示匹配前一个字符零次或一次
- +:表示匹配前一个字符一次或多次
- * :表示匹配前一个字符任意次(零次或多次)
代码测试,还是上面那几行代码
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegularTest02
{
public static void main(String[] args)
{
regularTest();
}
public static void regularTest()
{
System.out.println(Pattern.matches("\\w?","zbc"));
System.out.println(Pattern.matches("\\w+","zbc"));
System.out.println(Pattern.matches("\\w*","zbc"));
System.out.println(Pattern.matches("\\w?",""));
System.out.println(Pattern.matches("\\w+",""));
System.out.println(Pattern.matches("\\w*",""));
}
}
上面程序的打印结果是什么呢?
13行:false,因为 “?” 只代表前一个字符零次或一次,它的前一个字符是什么? 是 “\w” ,表示任意一个单词字符。所以整体意思表示匹配任意一个单词字符零次或一次。但是下面的需要匹配的字符串对象中,却有三个字符,所以肯定不匹配。
14行:true,因为 “+” 代表匹配前一个字符一次或多次,前一个字符是任意一个单词字符,所以,只要不为空,多少个单词字符拼在一起都可以匹配成功,当然是true了。
后面的尝试自己分析一下,另外,自己还可以多试一些边,加深影响。注意考虑三个表示次数的区别去改。
其它表示次数的形式
- {n,m}:匹配前一个字符n次到m次
- {n,}:匹配前一个字符n次到无限次
- {n}:匹配前一个字符n次
这三个,可以明确的表示一个次数边界,感觉上会用得多一些。但实际上用得并不多(就我个人经验而言),因为在对一些文本或字符串进行操作的时候,我们往往是不清楚个数的。
我就不测试了,大家感兴趣的话可以去测试一下,将之前的 “?”、"+"、"*" 改成上面的就可以了。
了解了表示匹配次数的语法之后,我们来认识一下表示匹配其它内容的规则
常用的匹配内容规则
- \w:表示匹配任意一个单词字符(单词、字符和下划线)
- \W:表示匹配任意一个非单词字符
- \d:表示匹配任意一个十进制数字
- \D:表示匹配任意一个非十进制数字
- \s:表示匹配任意一个空白字符(包括空格、制表符、换页符等等)
- \S:表示匹配任意一个非空白字符,也就是可见字符
- . : (英文状态下的点.) 表示匹配一个除换行符之外的其它任意字符
这三对都是相对应的,记住一个,另一个也就知道了。当然,还有很多表示表示匹配其它的规则,大家先熟悉和掌握这些基本的、常用的,再往了解其它的,有个循序渐进的过程。
代码测试,知道了这些匹配内容的规则,再结合前面的匹配次数,我们就可以换更多的花样来测试了。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegularTest02
{
public static void main(String[] args)
{
regularTest();
}
public static void regularTest()
{
System.out.println(Pattern.matches("\\d?","zbc"));
System.out.println(Pattern.matches("\\d+","zbc"));
System.out.println(Pattern.matches("\\D*","zbc"));
System.out.println(Pattern.matches("\\s?",""));
System.out.println(Pattern.matches("\\S+",""));
System.out.println(Pattern.matches("\\S*",""));
System.out.println(Pattern.matches(".","a"));
System.out.println(Pattern.matches(".?","abc"));
System.out.println(Pattern.matches(".+","abc"));
}
}
结果我就不带领大家分析了,自己慢慢分析,应该是可以得出结果的。如果分析错了,结合控制台的打印,应该也能很快反应过来的。
下面,我们来认识一下正则表达式中另一类神奇的存在:元字符。其实我们在之前已经用过了, 只是我们不知道它是元字符。例如:" "、 “{}”、 “*” ……这些都是元字符。什么?我之前用得好好的,现在你告诉我它是元字符,我不能接受!!
元字符是什么意思呢?就是具有特殊含义的字符。说到特殊含义,那什么是没有特殊含义的字符呢?说到现在,我们一直都在说匹配任意xxxx,那我就想匹配一个 “w” ,一个 “s” 呢?这咋办。
其实很简单
System.out.println(Pattern.matches("w","w"));
System.out.println(Pattern.matches("w?","www"));
System.out.println(Pattern.matches("s+","sss"));
System.out.println(Pattern.matches("s*","ass"));
如果是普通字符,单写一个,就代表匹配一个该字符。
现在我们再回头来看元字符,是不是具有特殊含义?是的吧,
常用的元字符
- . : 表示匹配一个除换行符之外的其它任意字符
- ? :表示匹配前一个字符零次或一次
- + :表示匹配前一个字符一次或多次
- * :表示匹配前一个字符任意次(零次或多次)
- \:加在元字符前面可以消除其特殊功能,加在一些普通字符前面可以使其具有特殊含义
- {}:成对出现,表示匹配中括号中指定的次数
- []:成对出现,表示一个字符集,或者是一个范围,匹配其中一个。有些元字符在其中没有特殊含义
- ():表示一个整体,也可以表示分组(子组),结合匹配对象进行使用
- ^:脱字符,表示除xxx之外。还表示从字符串的头开始匹配
- |:对两个正则表达式进行或操作,这个符号优先级别很低
有一些是老熟人了吧,我就不说了哦。
1. \
“\” 好像挺有意思的,加在元字符前面可以消除其特殊功能,加在一些普通字符前面可以使其具有特殊含义,是什么意思呢?我们都知道了,"+" 表示匹配前一个字符一次或多次,那我就想匹配 “+” 本身怎么办?
//这样写肯定是不行的
System.out.println(Pattern.matches("+","++"));
那怎么办呢?这时候就要用到 “\” 了,让 “\” 把 “+” 这个元字符的特殊含义给消除了,所以就这么写:
//注意要用双反斜杠,前面已经解释过了
System.out.println(Pattern.matches("\\+","++"));
System.out.println(Pattern.matches("\\+?","++"));
要匹配其它的元字符也是类似,但是有个问题是:我就想匹配一个反斜杠,这咋办?
其实思路还是一样的,反斜杠不是具有特殊含义吗?那我就再用一个反斜杠,来消除它的特殊含义,只不过在java中,反斜杠也具有特殊含义,所以我们得用两个反斜杠表示字符串中一个普通的反斜杠,所以我们得用四个反斜杠来表示正则表达式中一个普通的反斜杠,每两个反斜杠,表示正则表达式中一个具有特殊含义的反斜杠,然后再用反斜杠消除特殊后面一个反斜杠的特殊含义。
System.out.println(Pattern.matches("\\\\","\\"));
System.out.println(Pattern.matches("\\\\?","\\\\"));
System.out.println(Pattern.matches("\\\\*","\\\\\\"));
“\” 的另外一个作用就是,将一些普通字符变得有特殊含义,其实我们早已经用过了,就是在匹配内容那里,“w”、“W”、“s”、“S”、“d”、“D”……这些原本都是普通的字符,只代表匹配它本身,但是在它前面加一个反斜杠之后,就赋予了它特殊的含义。
2. []
“[]” 表示一个字符集,匹配其中一个。例如:"[123]" 表示匹配 “1” 或 “2” 或 “3”。
System.out.println(Pattern.matches("[123]","3"));
当连字符( “-” )在字符集中出现在两个有顺序的字符之间时,表示匹配这个范围之内的任意一个,比如:"[1-3]" 的效果就和 “[123]” 一样,都表示匹配 “1” 或 “2” 或 “3”。"[a-c]"就表示匹配 “a” 或 “b” 或 “c”
System.out.println(Pattern.matches("[1-3]","3"));
System.out.println(Pattern.matches("[a-c]","a"));
System.out.println(Pattern.matches("[a-c]","b"));
System.out.println(Pattern.matches("[a-c]","c"));
注意:当连字符不是出现在两个 的字符之间时,就表示匹配连字符本身。
比如:"[-123]"、"[123-]" 和 “[1-3-]” 都表示匹配 “-” 或 “1” 或 “2” 或"3"
System.out.println(Pattern.matches("[-123]","-"));//true
System.out.println(Pattern.matches("[123-]","-"));//true
System.out.println(Pattern.matches("[1-3-]","-"));//true
在比如"[1-z]"就表示匹配 “1” 或 “-” 或 “z”,如果是 “[3-1]” 这种逆序的写法,在java中会报错,编译不通过,不知道其它情况下是怎样的。
还有就是有些元字符在 “[ ]” 中没有特殊含义!!
3. ()
“()” 表示一个整体,比如说,我要匹配 “110” 一次或多次,就可以用 “()” 将 “110” 表示为一个整体,然后后面再规定次数。
System.out.println(Pattern.matches("(110)+","110"));
System.out.println(Pattern.matches("(110)+","110110"));
System.out.println(Pattern.matches("(110)+","110011"));
关于表示子组后面再说。
4. ^
“^” 在字符集 “[]” 中,如果出现在第一个,是脱字符,表示除去什么什么。
比如 “[^123]” 表示匹配任意一个 “1”、“2”、“3” 之外的字符。 " [^a-d]" 表示匹配任意一个除"a"、“b”、“c”、“d” 之外的字符。
System.out.println(Pattern.matches("[^123]","3"));
System.out.println(Pattern.matches("[^123]","5"));
System.out.println(Pattern.matches("[^a-d]","d"));
System.out.println(Pattern.matches("[^a-d]","z"));
如果 “^” 在 “[]” 中,不是出现在第一个的,就表示一个普通的 “^” 字符。比如 “[12^3]” 就表示匹配 “1” 或 “2” 或 “^” 或 “3”。
System.out.println(Pattern.matches("[12^3]","^"));
如果"^“不是出现在字符集”[ ]"中,是表示从头开始匹配的意思,但是我不知道用java怎么演示。
5. |
“|” 表示对两个正则表达式进行或操作,这个符号优先级别很低,比如:“ab|cd” 不是表示匹配 “abd” 或 “acd” ,而是表示匹配 “ab” 或 "cd"
System.out.println(Pattern.matches("ab|cd","abd"));
System.out.println(Pattern.matches("ab|cd","acd"));
System.out.println(Pattern.matches("ab|cd","ab"));
System.out.println(Pattern.matches("ab|cd","cd"));
暂时就说这么多,最基本的内容。
热身练习
- 匹配非负整数
- 匹配正整数
- 匹配整数
习题是我从别处搬来的,想练习可以去这里:https://blog.csdn.net/qian_youyou/article/details/79121916
他给的答案中,每个表达式最前面都是"^",最后面都是"$",大家如果不知道的这个东西话,可以暂时将其忽略。