java正则表达式的减号_详解JavaAPI之正则表达式
正则表达式描述的是⼀种规则,符合这种限定规则的字符串我们认为它某种满⾜条件的,是我们所需的。在正则表达式中,主要有两种字符,⼀种描述的是普通的字符,另⼀种描述的是元字符。其中元字符是整个正则表达式的核⼼,并由它完成规则的制定⼯作。本篇⽂章主要从Java这门程序设计语⾔的⾓度理解正则表达式的应⽤,主要涉及以下内容:
基本正则表达式的理论基础
Java中⽤于正则表达式匹配的类
⼏种常⽤的正则表达式使⽤实例
⼀、正则表达式的理论基础
1、普通字符的表⽰
我们说正则表达式主要由普通字符和元字符组成,那么我们⾸先先看看普通字符该如何表⽰。⼤部分普通字符由字符本⾝即可表⽰,例
如:'s','i','n','g','l','e'等。除此之外,也有⼀些特殊的表⽰⽅式。
以/0开头,后⾯紧跟1-3位数字,表⽰的是⼀个⼋进制数。这个数的⼗进制值对应于ASCII编码中的相应字符。
以/x或者/X开头,后⾯紧跟两位字符,表⽰的是⼀个⼗六进制的数。该数的⼗进制的值对应于ASCII编码中相应的字符。
以/u开头,后⾯紧跟四位字符,表⽰⼀个Unicode编号。该编号对应于Unicode字符集中的⼀个具体字符。
另外还有⼀些元字符,虽然它们具有特殊的含义,但是往往在某种特殊情况下,需要将这些元字符当做普通字符使⽤,我们使⽤ '/'+元字符,表⽰转移该元字符,此后该元字符将表⽰⼀个普通字符。例如:'//','/^',它们分别表⽰的是 '/'和 '^',不再具有特殊含义了。下⾯我们开始逐渐介绍正则表达式语法中的元字符的特殊含义。
2、字符组匹配单个字符
我们⽤⼀对中括号([.....])表⽰字符组,整个字符组中会有多个字符位列其中,该字符组表⽰的含义是:匹配任意⼀个字符,该字符是位列字符组中的。例如:[single]匹配的是字符's','i','n','g','l','e'中的任意⼀个字符。以上我们简单介绍了字符组的基本概念以及它所能匹配的内容,其实有时候为了表述连续的
字符,我们会结合元字符 '-' ⼀起来操作字符组。例如:[0123456789],匹配的是0到9之间的任意⼀个数字,对于这种情况我们可以选择这样来简化操作:[0-9]。其实两者表述的含义是⼀样的,为了简化起见,如果遇到连续的字符表述,可以选择使⽤元字符来简化。同样的还有[a-z],它匹配任意⼀个⼩写字母。对于元字符 '-' 还需要说明⼀点的是:该字符只有出现在两个字符之间才具有特殊含义,单独出现在字符组的所有字符之前或者之后只能表述普通字符 '-' 。下⾯介绍有关字符组的⼀些其他相关的元字符。
元字符 '^' 表⽰排除的意思,和元字符 '-' 类似,只有放在所有字符的最前⾯才具有特殊含义,否则只能表⽰普通字符。例如:[1234],该字符组匹配⼀个字符,但是不是1或2或3或4。当然,[cyy],匹配的是四个普通字符,'c','^','y','y'。此外,需要注意⼀点的是,除了以上介绍的⼏种元字符必须置放于指定位置上才能起作⽤以外,其余所有元字符在字符组中统统被视作普通字符,不再具有特殊含义。
除此之外,字符组还⽀持嵌套使⽤。例如:[0-9[a-z]],该字符组匹配⼀个数字或者⼀个字母。我们也可以使⽤&&加强限定规则。例如:[0-9&&[^0123]],该字符组匹配的是0到9之间任意⼀个数字,但是该数字不能是0到3中任意⼀个,也就是只能匹配4到9之间任意⼀个数字。最后和字符组有关的内容还是涉及⼀个预定义字符组,所谓预定义字符组就是对字符组的适当封装,对于⼀些简单的组合使⽤简介的调⽤⽅式。例如:
\d:等同于字符组 [0-9],表⽰任意⼀个数字字符
\w:较为常见,等同于字符组[0-9a-zA-Z],表⽰任意⼀个world(单词字符)
java中split的用法\s:等同于[ \t\n\x0B\f\r],匹配的是⼀个空格字符(space)
当然,它们也有相对应的⼤写形式,但是表⽰的意思却是截然相反的。
\D:等同于[^0-9],表⽰⼀个任意⾮数字字符
\W:等同于[^0-9a-zA-Z],表⽰任意⼀个⾮单词字符,往往会是⼀些特殊符号
\S:等同于[^\t\n\x0B\f\r],匹配⼀个任意⾮空格的字符
3、⽤于指定字符多次出现的量词
所谓的量词主要是三个元字符,它们主要⽤于指定量词前⾯的字符在匹配时可以多次出现,具体区别接下来会介绍。⾸先我们需要知道,这三个元字符是:+ ,*, ?。下⾯描述它们各⾃作⽤及相互之间的区别:
+:该元字符指定位于元字符前⾯的普通字符可以出现⼀次或者多次。例如:se+cyy这个正则表达式,字符secyy,seeeecyy都是可以匹配的,但是scyy是不能匹配的,前⾯的字符是必须出现的。
*:该元字符指定位于元字符前⾯的普通字符可以出现零次或多次。例如:
se*cyy
对于该正则表达式⽽⾔,secyy,seecyy都是可匹配的,并且scyy也是可以匹配的。这就是和元字符 + 的简单区别。
:该元字符指定位于元字符前⾯的普通字符可以出现也可以不出现,但是不能多次出现。例如:secyy,对于该正则表达式,secyy,scyy 等都是可匹配的,但是seeeecyy则是不能匹配的。它指定你前⾯的⼀个字符要么出现,要么不出现,不允许多次出现。
在这⾥我们要申明⼀个误区,这⾥的三个元字符量词作⽤的是紧邻该元字符前⾯的⼀个字符,并不是作⽤与元字符前⾯所有的字符,这⾥是需要注意的,包括笔者当初也都是误以为此的。
以上我们介绍了简单量词的概念,但是它们只能⽤于表⽰模糊的次数。可以出现多次,但是多次是多少却没有定论。对于要求字符出现精确次数的情况,我们可以使⽤通⽤量词来解决。{m,n}是通⽤量词的最基本形式,它指定前⾯的字符出现的次数在m到n之间。看⼏个例⼦:
se{0,10}cyy:其中e可以出现0-10次
se{9}cyy:其中e必须出现9次
se{0,}cyy:其中e可以出现0-⽆穷⼤次,等同于se*cyy。
4、分组划分组别
在介绍分组之前,⽆论是使⽤量词还是字符组都是针对的⼀个字符。⽽分组针对的就是⼀串字符,我们也可以对分组使⽤量词,控制该分组出现的次数。我们使⽤()括号表⽰分组,例如:
sing(le)de(cyy)
其中le和cyy分别是⼀个分组,对于⼀个完整的正则表达式,从头开始,每个分组都是有编号的,按照出现的次序,以1为基数递增。⾄于为什么要有编号,下⽂说。对于分组我们依然是可以使⽤量词控制其出现次数的,例如:
sing(le)+cccc:在该正则表达式中,分组le可以出现⼀次或者多次
sing(le)*cccc:在该正则表达式中,分组le可以出现零次或者多次
结合元字符 '| ',可以实现和字符组⼀样的功效,例如:
(happy|cyy|single)
该正则表达式可以匹配三个字符⼦串,happy,cyy,single。但是这⾥需要注意的是,元字符 | 如果⽤于字符组中就不再具有特殊含义,将会被作为普通字符来匹配。(这⼀点其实在介绍字符组的时候已经强调过)
下⾯解决⼀个上⽂遗留问题,分组的编号到底有什么作⽤。为分组编号其实是为了重新捕获和使⽤分组,每个分组按照出现的次序从1开始递增,我们使⽤ +分组编号进⾏引⽤。例如:
(.*)\1>:该正则表达式等效于:(.*)\w+>
(\w+)表⽰任意个字符(字母或数字),(.*)表⽰任意的符号,\1则引⽤了分组(\w+)。所以在这⾥,html中所有⾮单标签元素都是能匹配的。当然,如果我们不想使⽤默认的编号来引⽤分组,我们其实也是可以在定义分组的时候为分组命名。为分组命名的语法格式为:(?X),引⽤分组的语法格式为:\k。例如:
a)>(.*)\k>:等效于:(.*)
上述正则表达式定义了⼀个名为num1的分组,并后续进⾏了引⽤。下⾯介绍正则表达式的最后⼀块理论基础,边界匹配。
5、边界匹配
以上我们所介绍的所有内容主要还是针对单个字符或者多个字符组成的分组,我们可以限制他们的出现次数以及出现位置等。但是其实在正则表达式中,我们也是可以限制边界必须满⾜某种条件的。主要涉及的元字符有:^, $, \A, \Z, \z和\b。
⾸先看元字符 ^ ,在字符组中,该元字符表⽰否定的意思,此处匹配正则表达式⾸部位置边界。例如:^abc匹配⼀个以abc开头的字符串。
元字符 $匹配的字符串的尾部边界,它规定被匹配的字符串必须以什么结束。例如:
abc$:dabc,abc,abc/n都是可匹配的
实际上,如果被匹配字符串是以指定字符结尾或者指定字符之后跟换⾏符,都是可匹配的。此处需要注意尾部边界匹配时的表述格式。(不同于⾸部匹配)
\b匹配的是单词边界,所谓的单词边界指的就是:当⼀边是字符,⼀边是⾮字符的时候,此处即为单词边界。也就是单词结束的那个位置。还有⼀些边界,例如:\A,\b,\Z等,各⾃匹配的边界如下图所⽰;
当然,对于边界匹配最通⽤的⼀种⽅式就是环视。它不局限于整个表达式的开头和结尾,它可以出现在表达式中的任何位置,既可以向前匹配,也可以向后匹配。主要分为以下四种情况:
肯定顺序环视:它要求表达式的右边字符串必须满⾜某种约定,语法(?=....)。例如:single(?=cyy),字符e的右边即为边界并且要求必须为cyy,所以该表达式只能匹配singlecyy。
否定顺序环视:它要求表达式的右边字符串必须不能满⾜某种约定,和上⼀中情况是相反的,语法格式为:(?!...)。
肯定逆序环视:它要求表达式的左边必须满⾜某种约束,语法格式为:(?<=...)。
否定逆序环视:它要求表达式的左边必须不能满⾜某种约束,语法格式为:(?
虽然看起来有四种不同的环视类型,但是实际上分为两种,⼀种是向左看,⼀种是向右看。以上有关正则表达式的基本内容⼤致介绍完结,下⾯主要看看如何在Java中验证我们上述的这些理论。
⼆、Java API对正则表达式的⽀持
在Java中,对正则表达式的⽀持,主要还是这个包,我们常⽤的是其中的Pattern和Matcher这两个类。其中Pattern绑定了⼀个正则表达式,也就是代表了⼀个规则,Matcher绑定了⼀个Pattern和⼀个被处理的字符串,我们可以利⽤Matcher中的⼀些⽅法来完成匹配⼯作。此外,Java中所有的正则表达式都是以字符串的形式出现的,所以⾃然离不开String这个类,该类中的很多⽅法的参数都是基于正则表达式的,下⽂将详细介绍。我们⾸先看Pattern这个类。
Pattern主要⽤于编译⼀个正则表达式,也就是创建⼀个Pattern对象,该对象与实际的⼀个正则表达式想绑定,它仅仅代表⼀个规则,与实际要匹配的字符串⽆关。例如:
String str = "//w";
Pattern p = Patternpile(str);
Pattern的compile⽅法将str这个正则表达式编译成⼀种内部结构,然后以Pattern实例的形式返回,⾄于这种内部结构是什么样⼦的,此处暂时不涉及。我们只需要知道,此时返回的pattern实例是绑定了⼀个正则表达式的。当然,Pattern还有⼀个compile重载,可显式指定匹配模式。
public static Pattern compile(String regex, int flags) {
return new Pattern(regex, flags);
}
此处主要有四种匹配模式可选,单⾏模式,多⾏模式,⽆视⼤⼩写模式,⽆视元字符模式(该模式下,所有元字符将会失效)。各⾃对应的常量:Pattern.DOTALL,Pattern.MULTILINE,Pattern.CASE_INSENSITIVE,Pattern.LITERAL。这些常量的值如下:
public static final int DOTALL = 0x20;(32)
public static final int MULTILINE = 0x08;(8)
public static final int CASE_INSENSITIVE = 0x02;(2)
public static final int LITERAL = 0x10;(16)
当然,我们没必要记住他们各⾃所对应的常量的值,在使⽤的时候直接调⽤它们的常量名即可。下⾯通过介绍String的⼏个基本⽅法,了解正则表达式在Java中的基本使⽤情况。
⾸先我们看split⽅法,该⽅法⽤于分割字符串,返回⼀个String数组。
public String[] split(String regex, int limit)
第⼀个参数接受的是⼀个正则表达式,第⼆参数⽤于限定分割次数。其实从其源代码中我们⼤致可以知晓该⽅法作⽤原理:⾸先利⽤indexOf⽅法到分割符⾸次出现的位置,将该位置以前所有字符保存,拿到剩余⼦串的所有内容,⼀样的操作。最后得到的数组就是按照分隔符分割的结果。limit只不过强制限定了分割次数,达到次数上限,即使后⾯仍有分隔符可匹配,也选择放弃。(打包后⾯所有内容为⼀个分组),看个例⼦:
public static void main(String [] args){
String str = "cyy,single.abc/https";
String[] results = str.split("[,./]");
for(String s : results){
System.out.print(s+" ");
}
}
输出结果:cyy single abc https
此处有⼈可能会有疑问,说好的Pattern和Matcher才是正则表达式的主要操作类,怎么没见到他们。其实在split内部调⽤的就是Pattern 的相关⽅法。
return Patternpile(regex).split(this, limit);
这是String类中split⽅法的最后⼀⾏代码,String中的split⽅法除了最后⼀⾏代码,其余代码处理的都是regex为普通单个字符的情况,⽽对于多个字符乃⾄包含元字符的时候都是由Pattern中split⽅法处理的,该⽅法中会创建Matcher类并调⽤其中find等⽅法进⾏匹配查,代码量⽐较多,此处不再赘述。
下⾯看String的⼀个匹配校验的⽅法。
public boolean matches(String regex) {
return Pattern.matches(regex, this);
}
显然,该⽅法内部调⽤的是Pattern的matches⽅法,
public static boolean matches(String regex, CharSequence input) {
Pattern p = Patternpile(regex);
Matcher m = p.matcher(input);
return m.matches();
}
这是⼀个⾮常标准的对正则表达式的处理流程,⾸先编译(绑定)正则表达式字符串获取Pattern实例,然后调⽤Pattern的matcher⽅法获取Matcher实例,接着就可以利⽤Matcher实例完成⼤量⼯作。此处调⽤matches⽅法完成对已绑定的正则表达式和预处理字符串的匹配⼯作,返回值为boolean。
最后看String的ReplaceAll⽅法:
public String replaceAll(String regex, String replacement) {
return Patternpile(regex).matcher(this).replaceAll(replacement);
}
该⽅法实际上还是依赖的Matcher中的replaceAll⽅法,由于⼀个Matcher实例是同时绑定⼀个正则表达式和⼀个被匹配字符串的。所以在Matcher内部的replaceAll⽅法在进⾏搜索匹配的时候就⽆需传⼊额外参数。具体代码,⼤家可以⾃⾏查看,此处节约篇幅不再赘述。
三、常见正则表达式的案例
接下来我们主要从⽇常较为普遍使⽤的⼀些案例中来深刻理解上述所有内容。
1、Email地址
通常我们的Email地址的格式主要是:
3-18字符,可使⽤英⽂、数字、减号、点或下划线
必须以英⽂字母开头,必须以英⽂字母或数字结尾
点、减号、下划线不能连续出现两次或两次以上
以上是腾讯QQ邮箱的要求,相对⽽⾔已算是较为复杂,接下来我么看如何实现它。⾸先,第⼀条要求:
[-._a-z0-9A-Z]{3,18}
满⾜第⼆条要求:
[a-zA-Z][-._a-z0-9A-Z]{1,16}/w
满⾜第三个条件:
(?![-0-9a-zA-Z._]*(--|\.\.|__))[a-zA-Z][-._a-z0-9A-Z]{1,16}/w
⾄于最后⼀个条件的匹配,我们使⽤否定顺序环视来实现,它要求右边界所有内容不能是如下的形式:0个或者多个(英⽂、数字、减号、点或下划线)加上两个连续减号或者点或者下划线。也就是说,右边如果由多个字符或者⼀个减号,点或者下划线,那是没事的,可⼀旦出现连续的减号,点或者下划线,那么就将⽴马被否定顺序环视匹配,进⽽不满⾜条件结束。
其实上述对邮箱⽤户名的匹配算是⽐较严格的,⼀般⽤于匹配邮箱⽤户名的正则表达式则没这么严格,具体要求如下:
由英⽂字母、数字、下划线、减号、点号组成
⾄少1位,不超过64位
开头不能是减号、点号和下划线
由于⽐较简单,此处直接写出结果:
[^-._][-._a-zA-Z0-9]{0,63}
2、⼿机号码
在看⼀个⼿机号码的正则表达式匹配情况,具体要求如下:
中国的⼿机号码都是11位数字
⽬前⼿机号第1位都是1,第2位取值为3、4、5、7、8之⼀
最终的表述结果为:
1[34578][0-9]{9}
还有很多案例,此处为了使篇幅不⾄过长,到此为⽌。更多案例还有待⼤家⾃⾏测试。
本⽂如有错误,望⼤家指出,相互学习。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。