相关阅读
- Java基础:String类
- Java字符串格式化
- Java基础:正则表达式
正则表达式概述
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
正则表达式,又称正规表示法、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本
许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。正则表达式通常缩写成“regex”,单数有regexp、regex,复数有regexps、regexes、regexen
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑
目的
给定一个正则表达式和另一个字符串,我们可以达到如下的
- 给定的字符串是否符合正则表达式的过滤逻辑(称作“匹配”)
- 可以通过正则表达式,从字符串中获取我们想要的特定部分
正则表达式的特点
- 灵活性、逻辑性和功能性非常的强;
- 可以迅速地用极简单的方式达到字符串的复杂控制。
- 对于刚接触的人来说,比较晦涩难懂。
由于正则表达式主要应用对象是文本,因此它在各种文本编辑器场合都有应用,小到著名编辑器EditPlus,大到Microsoft Word、Visual Studio等大型编辑器,都可以使用正则表达式来处理文本内容
正则表达式基础知识
元字符
正则表达式主要依赖于元字符。
元字符不代表他们本身的字面意思, 他们都有特殊的含义。 一些元字符写在方括号中的时候有一些特殊的意思。 以下是一些元字符的介绍:
| 元字符 | 描述 |
| —— | ———————————————————— | — |
| . | 句号匹配任意单个字符除了换行符 |
| [ ] | 字符种类. 匹配方括号内的任意字符 |
| [^ ] | 否定的字符种类. 匹配除了方括号里的任意字符 |
| * | 匹配>=0个重复的在*号之前的字符 |
| + | 匹配>=1个重复的+号前的字符 |
| ? | 标记?之前的字符为可选 |
| {n,m} | 匹配num个中括号之前的字符 (n <= num <= m) |
| (xyz) | 字符集, 匹配与 xyz 完全相等的字符串 |
| | | 或运算符,匹配符号前或后的字符 |
| \ | 转义字符,用于匹配一些保留的字符 [ ] ( ) { } . \* + ? ^ $ \ |
|
| ^ | 从开始行开始匹配 |
| $ | 从末端开始匹配 |
常见符号
字符
符号 |
说明 |
X |
字符X |
\\ |
反斜线 |
\t |
制表符 (‘\u0009’) |
\n |
回车 |
\r |
换行 |
\f |
换页符 (‘\u000C’) |
\a |
报警 (bell) 符 (‘\u0007’) |
字符类
符号 |
说明 |
[abc] |
a、b或c |
[^abc] |
任何字符,除了a、b或c |
[a-zA-Z] |
a到z,或A到Z |
[0-9] |
0到9的字符 |
[a-d[m-p]] |
a到 d或 m 到 p:[a-dm-p](并集) |
[a-z&&[def]] |
d、e或 f(交集) |
[a-z&&[^bc]] |
a到 z,除了 b和 c:[ad-z](减去) |
[a-z&&[^m-p]] |
a到 z,而非 m到 p:[a-lq-z](减去) |
\u4E00-\u9FA5 |
中文字符 |
预定义字符
符号 |
说明 |
. |
任何字符 |
\d |
数字:[0-9] |
\D |
非数字: [^0-9] |
\s |
空白字符:[ \t\n\x0B\f\r] |
\S |
非空白字符:[^\s] |
\w |
单词字符:[a-zA-Z_0-9] |
\W |
非单词字符:[^\w] |
中括号表达式
范围匹配,一种特殊的匹配单个字符的方法。
- 即[ ]表达式,用来匹配单个字符,只不过这一个字符的所在范围有中括号内的表达式来确定
- 枚举:[abc],表示匹配a、b、c的任意一个
- 范围:[a-f],表示匹配a-f之间的任意一个字符,包括边界,其中右边界一定要≥左边界,否则引擎编译错误
- 并:[a-cm-p],就表示a-c的范围和m-p的范围求并,其实前面的abc之类的枚举也是一种并运算
- 交:[a-z&&b-d],就表示a-z的范围和b-d的范围求交,结果等于[b-d]
- 补:[^abc]表示非a、b、c的任意一个字符,[^a-f]表示非a-f的任一字符;^也是一个脱字符,必须是中括号表达式的第一个字符,否则不起任何作用。补的是^后面紧跟的整个表达式。
- 嵌套:[a-m&&[def]],a-m和[def]都表示范围,因此可以做运算,结果等于[d-f]
- 有了上面这些基本运算,就可以构造一些很复杂的运算了:[a-z&&[^bc]] == [ad-z],但是注意不要用^构造复杂表达式,以下一些表达式将不起作用!
- ^后面紧跟一个[ ]嵌套:^[a-d],非法!不起任何作用
- ^后面是一个运算(交并补):^a-cf-h、^a-z&&c-h、^^a-c,都是非法的!!不起任何作用!!
- ^后面只能跟单纯的枚举和范围运算!!例如[^azh]、[^a-h]等;
边界匹配器
符号 |
说明 |
^ |
行开头 |
$ |
行结尾 |
\b |
单词边界 |
\B |
非单词边界 |
\A |
输入的开头 |
\G |
上一个匹配的结尾 |
\Z |
输入的结尾,仅用于最后的结束符(如果有的话) |
\z |
输入的结尾 |
贪婪与懒惰数量词
符号 |
说明 |
X? |
0次或1次 |
X* |
0次以上 |
X+ |
1次以上 |
X{n} |
恰好n次 |
X{n,} |
至少n次 |
X{n,m} |
n-m次 |
当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b
,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。在贪婪数量词后面加上?就是懒惰匹配。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:
a.*?b
匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。
为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——The match that begins earliest wins。
代码/语法 |
说明 |
*? |
重复任意次,但尽可能少重复 |
+? |
重复1次或更多次,但尽可能少重复 |
?? |
重复0次或1次,但尽可能少重复 |
{n,m}? |
重复n到m次,但尽可能少重复 |
{n,}? |
重复n次以上,但尽可能少重复 |
分组和捕获
分组可以分为两种形式,捕获组和非捕获组。
1、捕获组
捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C)))中,存在四个这样的组:
- 分组0:((A)(B(C)))
- 分组1:(A)
- 分组2:(B(C))
- 分组3:(C)
正则表达式中每个”()”内的部分算作一个捕获组,每个捕获组都有一个编号,从1,2…,编号0代表整个匹配到的内容,组零始终代表整个表达式。
之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。捕获的子序列稍后可以通过 Back 引用(反向引用) 在表达式中使用,也可以在匹配操作完成后从匹配器检索。
Back引用(\n
)是说在后面的表达式中我们可以使用组的编号来引用前面的表达式所捕获到的文本序列。注意:反向引用,引用的是前面捕获组中的文本而不是正则,也就是说反向引用处匹配的文本应和前面捕获组中的文本相同,这一点很重要。
例如 ([" ']).*\1
其中使用了分组,\1
就是对引号这个分组的引用,它匹配包含在两个引号或者两个单引号中的所有字符串,如,”abc” 或 “‘ “ 或’ “ ‘ ,但是请注意,它并不会对”a’或者 ‘a”匹配。原因上面已经说明,Back引用只是引用文本而不是表达式。
捕获组的作用就是为了可以在正则表达式内部或者外部(Java方法)引用它。在另一个字符串引用捕获组的内容的方法(“$”),在替换中常用$匹配组的内容。$n用来匹配第n个()里的内容。
利用Matcher中的group(int group)获取捕获组内容。
2、捕获组命名
如果捕获组的数量非常多,那都用数字进行编号并引用将会非常混乱,并且难以记忆每个捕获组的内容及意义,因此对捕获组命名显得尤为重要;Java 7开始提供了对捕获组命名的语法,并且可以通过捕获组的名称对捕获组反向引用(内外都行)。
命名捕获组的语法格式:(?<自定义名>expr)
,例如:(?<year>\d{4})-(?<date>\d{2}-(?<day>\d{2}))
,有三个命名捕获组year、date和day,从左到右编号分别为1、2、3(编号同样是有效的)
命名捕获组的反向引用:
- 正则表达式内引用:
\k<捕获组名称>
,例如:(?<year>\d{4})-\k<year>可以匹配1999-1999
;
- 外部引用:Matcher对象的start、end、group的String name参数指定要查询的捕获组的名称。
普通捕获组和命名捕获组的混合编号:
- 普通捕获组是相对命名捕获组的,即没有显式命名的捕获组;
- 当所有捕获组都是命名捕获组时那么编号规则和原来相同,即按照左括号(的出现顺序来编号;
- 当普通捕获组和命名捕获组同时出现时,编号规则为:先不忽略命名捕获组,只对普通捕获组按照左括号顺序编号,然后再对命名捕获组从左往右累计编号,例如:先忽略命名命名捕获组
<date>
,先对普通捕获组编号\d{4}是1,\d\d是2,然后再接着累加地对命名捕获组编号,因此<date>
是3;
3、非捕获组
非捕获组,只需要将捕获组中”()”变为”(?:)”即可。
以 (?) 开头的组是纯的非捕获组,它不捕获文本 ,也不针对组合计进行计数。就是说,如果小括号中以?号开头,那么这个分组就不会捕获文本,当然也不会有组的编号,因此也不存在Back引用。
我们通过捕获组就能够得到我们想要匹配的内容了,那为什么还要有非捕获组呢?原因是捕获组捕获的内容是被存储在内存中,可供以后使用,比如反向引用就是引用的内存中存储的捕获组中捕获的内容。而非捕获组则不会捕获文本,也不会将它匹配到的内容单独分组来放到内存中。所以,使用非捕获组较使用捕获组更节省内存。在实际情况中我们要酌情选用。
前后关联约束(前后预查)
前置约束和后置约束都属于非捕获簇(用于匹配不在匹配列表中的格式)。 前置约束用于判断所匹配的格式是否在另一个确定的格式之后。
例如, 我们想要获得所有跟在 $
符号后的数字, 我们可以使用正向向后约束 (?<=\$)[0-9\.]*
。这个表达式匹配 $
开头, 之后跟着 0,1,2,3,4,5,6,7,8,9,.
这些字符可以出现大于等于 0 次。
前后关联约束如下:
符号 |
描述 |
?= |
前置约束-存在 |
?! |
前置约束-排除 |
?<= |
后置约束-存在 |
?<! |
后置约束-排除 |
?=exp
前置约束(存在)
?=exp 前置约束(存在), 表示第一部分表达式必须跟在 ?=exp 定义的表达式之后.
返回结果只瞒住第一部分表达式. 定义一个前置约束(存在)要使用 ()
. 在括号内部使用一个问号和等号: (?=exp).
前置约束的内容写在括号中的等号后面. 例如, 表达式 [T|t]he(?=\sfat)
匹配 The
和 the
, 在括号中我们又定义了前置约束(存在) (?=\sfat)
,即 The
和 the
后面紧跟着 (空格)fat
.
1
| "[T|t]he(?=\sfat)" => The fat cat sat on the mat.
|
?!exp
前置约束-排除
前置约束-排除 ?!
用于筛选所有匹配结果, 筛选条件为 其后不跟随着定义的格式 前置约束-排除
定义和 前置约束(存在)
一样, 区别就是 =
替换成 !
也就是 (?!exp).
表达式 [T|t]he(?!\sfat)
匹配 The
和 the
, 且其后不跟着 (空格)fat
.
1
| "[T|t]he(?!\sfat)" => The fat cat sat on the mat.
|
?<=exp
后置约束-存在
后置约束-存在 记作(?<=exp)用于筛选所有匹配结果, 筛选条件为 其前跟随着定义的格式. 例如, 表达式 (?<=[T|t]he\s)(fat|mat)
匹配 fat
和 mat
, 且其前跟着 The
或 the
.
1
| "(?<=[T|t]he\s)(fat|mat)" => The fat cat sat on the mat.
|
?<!exp
后置约束-排除
后置约束-排除 记作 (?<!exp)用于筛选所有匹配结果, 筛选条件为 其前不跟着定义的格式. 例如, 表达式 (?<!(T|t)he\s)(cat)
匹配 cat
, 且其前不跟着 The
或 the
.
1
| "(?<![T|t]he\s)(cat)" => The cat sat on cat.
|
标志
标志也叫修饰语, 因为它可以用来修改表达式的搜索结果. 这些标志可以任意的组合使用, 它也是整个正则表达式的一部分.
标志 |
描述 |
i |
忽略大小写. |
g |
全局搜索. |
m |
多行的: 锚点元字符 ^ $ 工作范围在每行的起始. |
忽略大小写 (Case Insensitive)
修饰语 i
用于忽略大小写. 例如, 表达式 /The/gi
表示在全局搜索 The
, 在后面的 i
将其条件修改为忽略大小写, 则变成搜索 the
和 The
, g
表示全局搜索.
1 2
| "The" => The fat cat sat on the mat. "/The/gi" => The fat cat sat on the mat.
|
全局搜索 (Global search)
修饰符 g
常用语执行一个全局搜索匹配, 即(不仅仅返回第一个匹配的, 而是返回全部). 例如, 表达式 /.(at)/g
表示搜索 任意字符(除了换行) + at
, 并返回全部结果.
1 2
| "/.(at)/" => The fat cat sat on the mat. "/.(at)/g" => The fat cat sat on the mat.
|
多行修饰符 (Multiline)
多行修饰符 m
常用语执行一个多行匹配.
像之前介绍的 (^,$)
用于检查格式是否是在待检测字符串的开头或结尾. 但我们如果想要它在每行的开头和结尾生效, 我们需要用到多行修饰符 m
.
例如, 表达式 /at(.)?$/gm
表示在待检测字符串每行的末尾搜索 at
后跟一个或多个 .
的字符串, 并返回全部结果.
1 2 3 4 5 6 7
| "/.at(.)?$/" => The fat cat sat on the mat.
"/.at(.)?$/gm" => The fat cat sat on the mat.
|
更多正则表达式教程
learn-regex:一个学习正则的开源项目,由浅入深,很容易入手,而且教程有中文版。
正则表达式30分钟入门教程
Java正则表达式的应用
正则表达式的应用
String 类提供的几个特殊方法
方法声明 |
功能描述 |
matches() |
判断该字符串是否匹配指定的正则表达式 |
replaceAll() |
将该字符串所有匹配正则表达式的子串替换成指定的字符串 |
replaceFirst() |
将该字符串中第一个匹配正则表达式的子串替换成指定的字符串 |
split(String regex) |
以regex为分隔符,将该字符串分割成多个子串 |
匹配功能
1
| public boolean matches(String regex)
|
校验邮箱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| package cn.itcast_02; import java.util.Scanner;
public class RegexTest { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入邮箱:"); String email = sc.nextLine();
String regex = "\\w+@\\w{2,6}(\\.\\w{2,3})+";
boolean flag = email.matches(regex);
System.out.println("flag:"+flag); } }
|
分割功能
1
| public String[] split(String regex)
|
代码示例:我有如下一个字符串:”91 27 46 38 50”,请写代码实现最终输出结果是:”27 38 46 50 91”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package cn.itcast_03; import java.util.Arrays;
public class RegexTest { public static void main(String[] args) { String s = "91 27 46 38 50";
String[] strArray = s.split(" ");
int[] arr = new int[strArray.length];
for (int x = 0; x < arr.length; x++) { arr[x] = Integer.parseInt(strArray[x]); }
Arrays.sort(arr);
StringBuilder sb = new StringBuilder(); for (int x = 0; x < arr.length; x++) { sb.append(arr[x]).append(" "); } String result = sb.toString().trim();
System.out.println("result:"+result); } }
|
替换功能
1
| public String replaceAll(String regex,String replacement)
|
使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package cn.itcast_04;
public class RegexDemo { public static void main(String[] args) { String s = "helloqq12345worldkh622112345678java";
String regex = "\\d+"; String ss = "";
String result = s.replaceAll(regex, ss); System.out.println(result); } }
|
获取功能
Pattern和Matcher类的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package cn.itcast_05; import java.util.regex.Matcher; import java.util.regex.Pattern;
public class RegexDemo { public static void main(String[] args) { Pattern p = Pattern.compile("a*b"); Matcher m = p.matcher("aaaaab"); boolean b = m.matches(); System.out.println(b);
String s = "aaaaab"; String regex = "a*b"; boolean bb = s.matches(regex); System.out.println(bb); } }
|
Pattern 匹配模式
Pattern类为正则表达式的编译表示形式。指定为字符串的表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建Matcher对象,依照正则表达式,该对象可与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一个模式。
方法声明 |
功能描述 |
compile() |
把正则表达式编译成匹配模式 |
matcher() |
根据匹配模式去匹配指定的字符串,得到匹配器 |
Matcher 匹配器
方法声明 |
功能描述 |
matches() |
匹配字符串 |
find() |
查找有没有满足条件的子串 |
group() |
获取满足条件的子串 |
reset() |
将Matcher的状态重新设置为最初的状态 |
reset(CharSequence input) |
重新设置Matcher的状态,并且将候选字符序列设置为input后进行Matcher, 这个方法和重新创建一个Matcher一样,只是这样可以重用以前的对象。 |
start() |
返回Matcher所匹配的字符串在整个字符串的的开始下标 |
start(int group) |
指定你感兴趣的sub group,然后返回sup group(子分组)匹配的开始位置。 |
end() |
返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。 |
Pattern类为正则表达式的编译表示形式。指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建Matcher对象,依照正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式
分组:
简单的说,分组其实就是为了能够指定同一个规则可以使用多少次。正则表达式中的分组就是整个大的正则表达式和用()圈起来的内容。
在这个正则表达式”\w(\d\d)(\w+)”中
- 分组0:是”\w(\d\d)(\w+)”
- 分组1:是(\d\d)
- 分组2:是(\w+)
如果我们稍稍变换一下,将原先的正则表达式改为”(\w)(\d\d)(\w+)”,我们的分组就变成了
- 分组0:是”\w(\d\d)(\w+)”
- 分组1:是”(\w)”
- 分组2:是”(\d\d)”
- 分组3:是”(\w+)”
我们看看和正则表达式”\w(\d\d)(\w+)”匹配的一个字符串A22happy
- group(0)是匹配整个表达式的字符串的那部分A22happy
- group(1)是第1组(\d\d)匹配的部分:22
- group(2)是第2组(\w+)匹配的那部分happy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static void main(String[] args) { String Regex="\\w(\\d\\d)(\\w+)"; String TestStr="A22happy"; Pattern p=Pattern.compile(Regex); Matcher matcher=p.matcher(TestStr); if (matcher.find()) { int gc=matcher.groupCount(); for (int i = 0; i <= gc; i++) { System.out.println("group "+i+" :"+matcher.group(i)); } } }
|
start()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static void testStart(){ String candidateString = "My name is Bond. James Bond."; String matchHelper[] ={" ^"," ^"}; Pattern p = Pattern.compile("Bond"); Matcher matcher = p.matcher(candidateString); matcher.find(); int startIndex = matcher.start(); System.out.println(candidateString); System.out.println(matchHelper[0] + startIndex); matcher.find(); int nextIndex = matcher.start(); System.out.println(candidateString); System.out.println(matchHelper[1] + nextIndex); }
|
运行结果:
group方法
注意:一定要先find(),然后才能group()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
public static void testGroup() { Pattern p = Pattern.compile("Bond"); String candidateString = "My name is Bond. James Bond."; Matcher matcher = p.matcher(candidateString); matcher.find(); System.out.println(String.format("group匹配的字符串 : %s",matcher.group())); System.out.println(String.format("匹配的开始位置 : %d", matcher.start())); System.out.println(String.format("匹配的结束位置 : %d", matcher.end()));
System.out .println("---再次使用matcher.find()方法,看看matcher中group、start、end方法的效果"); matcher.find(); System.out.println(String.format("group匹配的字符串 : %s",matcher.group()));; System.out.println(String.format("匹配的开始位置 : %d", matcher.start())); System.out.println(String.format("匹配的结束位置 : %d", matcher.end())); System.out.println(String.format("candidateString字符串的长度 : %d", candidateString.length())); }
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| package cn.itcast_05; import java.util.regex.Matcher; import java.util.regex.Pattern;
public class RegexDemo2 { public static void main(String[] args) { String s = "da jia ting wo shuo,jin tian yao xia yu,bu shang wan zi xi,gao xing bu?"; String regex = "\\b\\w{3}\\b";
Pattern p = Pattern.compile(regex); Matcher m = p.matcher(s);
while (m.find()) { System.out.println(m.group()); }
} }
|
- 判断身份证:要么是15位,要么是18位,最后一位可以为字母,并写程序提出其中的年月日。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public static void main(String[] args) { testID_Card(); }
public static void testID_Card() { String[] strs = { "130681198712092019", "13068119871209201x", "13068119871209201", "123456789012345", "12345678901234x", "1234567890123" }; String regex = "(\\d{14}\\w)|\\d{17}\\w"; Pattern regular = Pattern.compile(regex);
StringBuilder sb = new StringBuilder(); for (int i = 0; i < strs.length; i++) {
Matcher matcher = regular.matcher(strs[i]); sb.append("身份证: "); sb.append(strs[i]); sb.append(" 匹配:"); sb.append(matcher.matches()); System.out.println(sb.toString()); sb.delete(0, sb.length()); }
GetBirthDay(strs);
}
private static void GetBirthDay(String[] strs) { System.out.println("准备开始获取出生日期"); Pattern BirthDayRegular = Pattern.compile("(\\d{6})(\\d{8})(.*)"); Pattern YearMonthDayRegular = Pattern.compile("(\\d{4})(\\d{2})(\\d{2})"); for (int i = 0; i < strs.length; i++) { Matcher matcher = BirthDayRegular.matcher(strs[i]);
if (matcher.matches()) { Matcher matcher2 = YearMonthDayRegular.matcher(matcher.group(2)); if (matcher2.matches()) { System.out.println(strs[i]+" 中的出生年月分解为: "+"年" + matcher2.group(1) + " 月:" + matcher2.group(2) + " 日:" + matcher2.group(3));
} } } }
|
运行结果:
正则表达式工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
| import java.util.regex.Matcher; import java.util.regex.Pattern;
public final class RegexUtils {
public static boolean checkEmail(String email) { String regex = "^(\\w)+(\\.\\w+)*@(\\w)+((\\.\\w{2,3}){1,3})$"; return Pattern.matches(regex, email); }
public static boolean checkIdCard(String idCard) { String regex = "[1-9]\\d{13,16}[a-zA-Z0-9]{1}"; return Pattern.matches(regex, idCard); }
public static boolean checkMobile(String mobile) { String regex = "(\\+\\d+)?1[34578]\\d{9}$"; return Pattern.matches(regex, mobile); }
public static boolean checkPhone(String phone) { String regex = "^1\\d{10}$"; return Pattern.matches(regex, phone); }
public static boolean checkDigit(String digit) { String regex = "\\-?[1-9]\\d+"; return Pattern.matches(regex, digit); }
public static boolean checkDecimals(String decimals) { String regex = "\\-?[1-9]\\d+(\\.\\d+)?"; return Pattern.matches(regex, decimals); }
public static boolean checkBlankSpace(String blankSpace) { String regex = "\\s+"; return Pattern.matches(regex, blankSpace); }
public static boolean checkChinese(String chinese) { String regex = "^[\u4E00-\u9FA5]+$"; return Pattern.matches(regex, chinese); }
public static boolean checkBirthday(String birthday) { String regex = "[1-9]{4}([-./])\\d{1,2}\\1\\d{1,2}"; return Pattern.matches(regex, birthday); }
public static boolean checkURL(String url) { String regex = "(https?://(w{3}\\.)?)?\\w+\\.\\w+(\\.[a-zA-Z]+)*(:\\d{1,5})?(/\\w*)*(\\??(.+=.*)?(&.+=.*)?)?"; return Pattern.matches(regex, url); }
public static boolean checkPostcode(String postcode) { String regex = "[1-9]\\d{5}"; return Pattern.matches(regex, postcode); }
public static boolean checkIpAddress(String ipAddress) { String regex = "[1-9](\\d{1,2})?\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))"; return Pattern.matches(regex, ipAddress); }
public static boolean checkNickname(String nickname) { String regex = "^[a-zA-Z0-9\u4E00-\u9FA5_]+$"; return Pattern.matches(regex, nickname); }
public static boolean hasCrossSciptRiskInAddress(String str) { String regx = "[`~!@#$%^&*+=|{}':;',\\[\\].<>~!@#¥%……&*——+|{}【】‘;:”“’。,、?-]"; if (str != null) { str = str.trim(); Pattern p = Pattern.compile(regx, Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(str); return m.find(); } return false; } }
|
说明:正则表达式通常用于两种任务:1.验证,2.搜索/替换。用于验证时,通常需要在前后分别加上^和$,以匹配整个待验证字符串;搜索/替换时是否加上此限定则根据搜索的要求而定,此外,也有可能要在前后加上\b而不是^和$。此表所列的常用正则表达式,除个别外均未在前后加上任何限定,请根据需要,自行处理。
校验数字的表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 1. 数字:^[0-9]*$ 2. n位的数字:^\d{n}$ 3. 至少n位的数字:^\d{n,}$ 4. m-n位的数字:^\d{m,n}$ 5. 零和非零开头的数字:^(0|[1-9][0-9]*)$ 6. 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$ 7. 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$ 8. 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$ 9. 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$ 10. 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$ 11. 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$ 12. 非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$ 13. 非负整数:^\d+$ 或 ^[1-9]\d*|0$ 14. 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$ 15. 非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$ 16. 非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$ 17. 正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$ 18. 负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$ 19. 浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$
|
校验字符的表达式
1 2 3 4 5 6 7 8 9 10 11 12
| 1. 汉字:^[\u4e00-\u9fa5]{0,}$ 2. 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$ 3. 长度为3-20的所有字符:^.{3,20}$ 4. 由26个英文字母组成的字符串:^[A-Za-z]+$ 5. 由26个大写英文字母组成的字符串:^[A-Z]+$ 6. 由26个小写英文字母组成的字符串:^[a-z]+$ 7. 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$ 8. 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$ 9. 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$ 10. 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$ 11. 可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+ 12. 禁止输入含有~的字符:[^~\x22]+
|
特殊需求表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| 1. Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
2. 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
3. InternetURL:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
4. 手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
5. 电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
6. 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
7. 身份证号(15位、18位数字):^\d{15}|\d{18}$
8. 短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$
9. 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
10. 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
11. 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
12. 日期格式:^\d{4}-\d{1,2}-\d{1,2}
13. 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
14. 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
15. 钱的输入格式:
i. 有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$
ii. 这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
iii. 一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
iv. 这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
v. 必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$
vi. 这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
vii. 这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
viii. 1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
16. xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
17. 中文字符的正则表达式:[\u4e00-\u9fa5]
18. 双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
19. 空白行的正则表达式:\n\s*\r (可以用来删除空白行)
20. HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
21. 首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
22. 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
23. 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
24. IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)
25. IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))
|