引言
正则表达式是一种强大而灵活的文本处理工具。字符串处理是许多程序中非常重要的一部分,它们可以用于文本显示,数据表示,查找键和很多目的。如果曾经用过内建正则表达式支持的语言或工具,那么应该会知道用正则表达式处理文本和匹配模式是多么简单。许多语言,包括Perl、PHP、Python、JavaScript和Jscript等,都支持用正则表达式处理文本,而一些文本编辑器也用正则表达式实现高级“搜索-替换”功能。
“正则表达式”(Regular Expression)就是一个字符构成的串,它定义了一个用来搜索匹配字符串的模式,是用一个“字符串”来描述一个特征,然后去验证另一个“字符串”是否符合这个特征。比如表达式“ab+”描述的特征是“一个 'a' 和任意个 'b' ”,那么 'ab','abb','abbbbbbbbbb' 都符合这个特征。
正则表达式可以用来:
(1)验证字符串是否符合指定特征,比如验证是否是合法的邮件地址。
(2)用来查找字符串,从一个长文本中查找符合指定特征的字符串,比查找固定字符串更加灵活方便。
(3)用来替换,比普通的替换更强大。
一、正则表达式的构造
构造 |
匹配 |
1.字符
字符 |
|
x |
字符 x |
\\ |
反斜线字符 |
\0n |
带有八进制值 0 的字符 n (0<=n<=7) |
\0nn |
带有八进制值 0 的字符 nn (0<=n<=7) |
\0mnn |
带有八进制值 0 的字符 mnn(0<=m<=3、0<=n<=7) |
\xhh |
带有十六进制值0x 的字符 hh, |
\uhhhh |
带有十六进制值0x 的字符 hhhh,如匹配中文字符: [\u4e00-\u9fa5] |
\t |
制表符 ('\u0009') |
\n |
新行(换行)符 ('\u000A') |
\r |
回车符 ('\u000D') |
\f |
换页符 ('\u000C') |
\a |
报警 (bell) 符 ('\u0007') |
\e |
转义符 ('\u001B') |
\cx |
对应于 x 的控制符 |
2.字符类
字符类 |
|
[abc] |
a、b 或 c(简单类) |
[^abc] |
任何字符,除了 a、b 或 c(否定) |
[a-zA-Z] |
a 到 z 或 A 到 Z,两头的字母包括在内(范围) |
[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](减去) |
3.预定义字符类
预定义字符类 |
|
. |
任何字符(与行结束符可能匹配也可能不匹配) |
\d |
数字:[0-9] |
\D |
非数字: [^0-9] |
\s |
空白字符:[ \t\n\x0B\f\r] |
\S |
非空白字符:[^\s] |
\w |
单词字符:[a-zA-Z_0-9] |
\W |
非单词字符:[^\w], 匹配所有的字母、数字、下划线以外的字符 |
4.POSIX字符类
POSIX 字符类(仅 US-ASCII) |
|
\p{Lower} |
小写字母字符:[a-z] |
\p{Upper} |
大写字母字符:[A-Z] |
\p{ASCII} |
所有 ASCII:[\x00-\x7F] |
\p{Alpha} |
字母字符:[\p{Lower}\p{Upper}] |
\p{Digit} |
十进制数字:[0-9] |
\p{Alnum} |
字母数字字符:[\p{Alpha}\p{Digit}] |
\p{Punct} |
标点符号:!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ |
\p{Graph} |
可见字符:[\p{Alnum}\p{Punct}] |
\p{Print} |
可打印字符:[\p{Graph}\x20] |
\p{Blank} |
空格或制表符:[ \t] |
\p{Cntrl} |
控制字符:[\x00-\x1F\x7F] |
\p{XDigit} |
十六进制数字:[0-9a-fA-F] |
\p{Space} |
空白字符:[ \t\n\x0B\f\r] |
5.Java.lang.Character类
java.lang.Character 类(简单的 java字符类型) |
|
\p{javaLowerCase} |
等效于 java.lang.Character.isLowerCase() |
\p{javaUpperCase} |
等效于 java.lang.Character.isUpperCase() |
\p{javaWhitespace} |
等效于 java.lang.Character.isWhitespace() |
\p{javaMirrored} |
等效于 java.lang.Character.isMirrored() |
6.Unicode块和类别的类
Unicode 块和类别的类 |
|
\p{InGreek} |
Greek块(简单块)中的字符 |
\p{Lu} |
大写字母(简单类别) |
\p{Sc} |
货币符号 |
\P{InGreek} |
所有字符,Greek 块中的除外(否定) |
[\p{L}&&[^\p{Lu}]] |
所有字母,大写字母除外(减去) |
7.边界匹配器
边界匹配器 |
|
^ |
行的开头 |
$ |
行的结尾 |
\b |
单词边界 |
\B |
非单词边界 |
\A |
输入的开头 |
\G |
上一个匹配的结尾 |
\Z |
输入的结尾,仅用于最后的结束符(如果有的话) |
\z |
输入的结尾 |
8.Greedy数量词
Greedy 数量词 |
|
X? |
X,一次或一次也没有 |
X* |
X,零次或多次 |
X+ |
X,一次或多次 |
X{n} |
X,恰好 n 次 |
X{n,} |
X,至少 n 次 |
X{n,m} |
X,至少 n 次,但是不超过 m 次 |
9.Reluctant数量词
Reluctant 数量词 |
|
X?? |
X,一次或一次也没有 |
X*? |
X,零次或多次 |
X+? |
X,一次或多次 |
X{n}? |
X,恰好 n 次 |
X{n,}? |
X,至少 n 次 |
X{n,m}? |
X,至少 n 次,但是不超过 m 次 |
10.Possessive数量词
Possessive 数量词 |
|
X?+ |
X,一次或一次也没有 |
X*+ |
X,零次或多次 |
X++ |
X,一次或多次 |
X{n}+ |
X,恰好 n 次 |
X{n,}+ |
X,至少 n 次 |
X{n,m}+ |
X,至少 n 次,但是不超过 m 次 |
11.Greedy,Reluctant,Possessive数量词的比较
Greedy,Reluctant,Possessive的区别在于:(注意,仅限于进行“.”等模糊处理时)
greedy量词被看作“贪婪的”,因为它第一次就读入整个被模糊匹配的字符串。如果第一个匹配尝试(整个输入字符串)失败,匹配器就会在被匹配字符串中的最后一位后退一个字符并且再次尝试,重复这个过程,直到找到匹配或者没有更多剩下的字符可以后退为止。根据表达式中使用的量词,它最后试图匹配的内容是1 个或者0个字符,即尽可能的匹配。
但是,reluctant量词采取相反的方式:它们从被匹配字符串的开头开始,然后逐步地一次读取一个字符搜索匹配。它们最后试图匹配的内容是整个输入字符串,即勉强去匹配。
最后,possessive量词总是读完整个输入字符串,尝试一次(而且只有一次)匹配。和greedy量词不同,possessive从不后退,即整个读入,能匹配则匹配。
1)贪婪模式:
在使用修饰匹配次数的特殊符号时,有几种表示方法可以使同一个表达式能够匹配不同的次数,比如:"{m,n}","{m,}","?","*","+",具体匹配的次数随被匹配的字符串而定。这种重复匹配不定次数的表达式在匹配过程中,总是尽可能多的匹配。比如,针对文本 "dxxxdxxxd",举例如下:
表达式 |
匹配结果 |
"\w+" 将匹配第一个 "d" 之后的所有字符 "xxxdxxxd" |
|
"\w+" 将匹配第一个 "d" 和最后一个 "d" 之间的所有字符 "xxxdxxx"。虽然 "\w+" 也能够匹配上最后一个 "d",但是为了使整个表达式匹配成功,"\w+" 可以 "让出" 它本来能够匹配的最后一个 "d" |
由此可见,"\w+" 在匹配的时候,总是尽可能多的匹配符合它规则的字符。虽然第二个举例中,它没有匹配最后一个 "d",但那也是为了让整个表达式能够匹配成功。同理,带 "*" 和 "{m,n}" 的表达式都是尽可能地多匹配,带 "?" 的表达式在可匹配可不匹配的时候,也是尽可能的 "要匹配"。这种匹配原则就叫作 "贪婪" 模式。
2)非贪婪模式:
在修饰匹配次数的特殊符号后再加上一个 "?" 号,则可以使匹配次数不定的表达式尽可能少的匹配,使可匹配可不匹配的表达式,尽可能的 "不匹配"。这种匹配原则叫作 "非贪婪" 模式,也叫作 "勉强" 模式。如果少匹配就会导致整个表达式匹配失败的时候,与贪婪模式类似,非贪婪模式会最小限度的再匹配一些,以使整个表达式匹配成功。举例如下,针对文本 "dxxxdxxxd" 举例:
表达式 |
匹配结果 |
(d)(\w+?) |
"\w+?" 将尽可能少的匹配第一个 "d" 之后的字符,结果是:"\w+?" 只匹配了一个 "x" |
(d)(\w+?)(d) |
为了让整个表达式匹配成功,"\w+?" 不得不匹配 "xxx" 才可以让后边的 "d" 匹配,从而使整个表达式匹配成功。因此,结果是:"\w+?" 匹配 "xxx" |
更多的情况,举例如下:
举例1:表达式"<td>(.*)</td>" 与字符串"<td><p>aa</p></td><td><p>bb</p></td>" 匹配时,匹配的结果是:成功;匹配到的内容是"<td><p>aa</p></td> <td><p>bb</p></td>"整个字符串, 表达式中的"</td>" 将与字符串中最后一个 "</td>" 匹配。
举例2:相比之下,表达式"<td>(.*?)</td>" 匹配举例1中同样的字符串时,将只得到"<td><p>aa</p></td>", 再次匹配下一个时,可以得到第二个"<td><p>bb</p></td>"。
12.Logical运算符
Logical 运算符 |
|
XY |
X 后跟 Y |
X|Y |
X 或 Y |
(X) |
X,作为捕获组 |
13.Back引用
Back 引用 |
|
\n |
任何匹配的 nth 捕获组 |
捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C)))中,存在四个这样的组:
1((A)(B(C)))
2A
3(B(C))
4(C)
在表达式中可以通过\n来对相应的组进行引用,例如(ab)34\1就表示ab34ab,(ab)34(cd)\1\2就表示ab34cdabcd。
14.引用
引用 |
|
\ |
Nothing,但是引用以下字符 |
\Q |
Nothing,但是引用所有字符,直到 \E |
\E |
Nothing,但是结束从 \Q 开始的引用 |
QE之间的字符串会原封不动的使用(转义字符的除外)。例如,ab\Q{|}\\\E可以匹配ab{|}\\
反斜线、转义和引用
反斜线字符 ('\') 用于引用转义构造,如上表所定义的。同时,还用于引用其他将被解释为非转义构造的字符。因此,表达式 \\ 与单个反斜线匹配,而 \{ 与大括号匹配。
在不表示转义构造的任何字母字符前使用反斜线都是错误的;它们是为将来扩展正则表达式语言保留的。但可以在非字母字符前使用反斜线,不管该字符是否非转义构造的一部分。
根据 JavaLanguage Specification 的要求,Java 源代码的字符串中的反斜线被解释为 Unicode 转义或其他字符转义。因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被 Java 字节码编译器解释。例如,当解释为正则表达式时,字符串字面值 "\b" 与单个退格字符匹配,而 "\\b" 与单词边界匹配。字符串字面值 "\(hello\)" 是非法的,将导致编译时错误;要与字符串(hello) 匹配,必须使用字符串字面值 "\\(hello\\)"。
15.特殊构造(非捕获)
特殊构造(非捕获) |
|
(?:X) |
X,作为非捕获组 |
(?idmsux-idmsux) |
|
(?idmsux-idmsux:X) |
|
(?=X) |
X,通过零宽度的正 lookahead |
(?!X) |
X,通过零宽度的负 lookahead |
(?<=X) |
X,通过零宽度的正 lookbehind,反向 |
(?<!X) |
X,通过零宽度的负 lookbehind,反向 |
(?>X) |
X,作为独立的非捕获组 |
(?:X)与(?>X)的区别在于(?>X)是不回溯的。例如被匹配的字符串为abcm
当表达式为a(?:b|bc)m是可以匹配的,而当表达式是a(?>b|bc)时是不能匹配的,因为当后者匹配到b时,由于已经匹配,就跳出了非捕获组,而不再次对组内的字符进行匹配。
16.特殊字符列表
正则表达式的特殊字符有以下(都是半角的单字节节符):
.,/ - + = ? * &^ $ ! : |
< > ( ) [ ] {}
对于特殊字符,其实就是正则表达式里用到的标点符号,在不确认是否需要对某个符号进行转义时,我们可以遵循正则表达式的转义规则:可以在非字母字符前使用反斜线,不管该字符是否非转义构造的一部分。
注意:这里我把正则表达式使用到的符号都列表出来了,但事实上某些符号在单独使用时并不需要进行转义,因为它们只有在正则表达式中组合其它字符使用时才有特殊意义。但为避免不必要的错误和减少理解的复杂度,建议在正则表达式中需要表达单字节符号的本身含义时,均使用转义字符将其转义。
二、JAVA内建正则表达式
从Java1.4起,Java核心API就引入了java.util.regex程序包,它是一种有价值的基础工具,可以用于很多类型的文本处理,如匹配,搜索,提取和分析结构化内容.java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包。它主要包括两个类:Pattern和Matcher.
1.Pattern类
Pattern是一个正则表达式经编译后的表现模式。在java中,通过适当命名的Pattern类可以容易确定String是否匹配某种模式.模式可以象匹配某个特定的String那样简单,也可以很复杂,如采用分组和字符类,空白,数字,字母或控制符.因为Java字符串基于统一字符编码(Unicode),正则表达式也适用于国际化的应用程序。
Pattern类的主要方法简述
说明 |
|
Static Pattern compile(String regex) |
将给定的正则表达式编译到模式中。 |
static Pettern compile(String regex,int flag) |
将给定的正则表达式编译到具有给定标志的模式中。 |
Matcher match(CharSequence input) |
创建匹配给定输入与此模式的匹配器。 |
static boolean matches(String regex,CharSequence input) |
编译给定正则表达式并尝试将给定输入与其匹配。 |
String[] split(CharSequence input) |
围绕此模式的匹配拆分给定输入序列。 |
String[] split(CharSequence input,int limit) |
分围绕此模式的匹配拆分给定输入序列,limit参数可以限制分隔的次数 |
2.Matcher类
一个Matcher对象是一个状态机器,是一个匹配器,它依据Pattern对象做为匹配模式对字符串展开匹配检查。首先一个Pattern实例订制了一个正则表达式经编译后的模式,然后一个Matcher实例在这个给定的Pattern实例的模式控制下进行字符串的匹配工作。
Matcher类的主要方法简述
说明 |
|
boolean matches() |
尝试将整个区域与模式匹配。 |
boolean lookingAt() |
尝试将从区域开头开始的输入序列与该模式匹配。 |
boolean find(int start) |
尝试查找与该模式匹配的输入序列的下一个子序列。 |
int groupCount() |
返回此匹配器模式中的捕获组数。 |
String replaceAll(String replacement) |
替换字符串相匹配的输入序列的每个子序列。 |
String repalceFirst(String replacement) |
替换字符串匹配的输入序列的第一个子序列。 |
Matcher appendReplacement(StringBuffer sb,String replacement) |
|
StringBuffer appendTail(StringBuffer sb) |
将输入序列中匹配之后的末尾字串添加到sb当前位置之后. |
注:find可以在任意位置匹配表达式,lookingAt和matches只有在表达式与输入的最开始处就开始匹配才会成功。matches只有在整个输入都匹配正则表达式时才会成功,而lookingAt只要输入的第一部份匹配就会成功
3.匹配的模式(Pattern flags)
匹配模式用于需要一个控制正则表达式的匹配行为时,作为其方法的参数:
如:Pattern Pattern.compile(String regex,int flag)
flag的取值范围如下:
编译标志 |
|
Pattern.CANON_EQ |
当且仅当两个字符的"正规分解(canonical decomposition)"都完全相同的情况下,才认定匹配。比如用了这个标志之后,表达式"a/u030A"会匹配"?"。默认情况下,不考虑"规范相等性(canonical equivalence)"。 |
Pattern.CASE_INSENSITIVE |
默认情况下,大小写不明感的匹配只适用于US-ASCII字符集。这个标志能让表达式忽略大小写进行匹配。要想对Unicode字符进行大小不明感的匹配,只要将UNICODE_CASE与这个标志合起来就行了。 |
Pattern.COMMENTS |
在这种模式下,匹配时会忽略(正则表达式里的)空格字符(注:不是指表达式里的"//s",而是指表达式里的空格,tab,回车之类)。注释从#开始,一直到这行结束。可以通过嵌入式的标志来启用Unix行模式。 |
Pattern.DOTALL |
在这种模式下,表达式'.'可以匹配任意字符,包括表示一行的结束符。默认情况下,表达式'.'不匹配行的结束符。 |
Pattern.MULTILINE |
在这种模式下,'^'和'$'分别匹配一行的开始和结束。此外,'^'仍然匹配字符串的开始,'$'也匹配字符串的结束。默认情况下,这两个表达式仅仅匹配字符串的开始和结束。 |
Pattern.UNICODE_CASE |
在这个模式下,如果你还启用了CASE_INSENSITIVE标志,那么它会对Unicode字符进行大小写不明感的匹配。默认情况下,大小写不明感的匹配只适用于US-ASCII字符集。 |
Pattern.UNIX_LINES |
在这个模式下,只有'/n'才被认作一行的中止,并且与'.','^',以及'$'进行匹配。 |
注:你可以用在表达式里插记号的方式来启用绝大多数的模式。这些记号就在上面那张表的各个标志的下面。你希望模式从哪里开始启动,就在哪里插记号。可以用"OR" ('|')运算符把这些标志合使用
4.String类
在上面的说明我们可以知道,正则表达式就是为了处理字符的,而JAVA中的String类(java.lang.String)也内建了一些直接使用正则表达式的方法,以方便直接、简单的使用正则表达式对字符串进行处理。它的一些使用正则表达式的方法有:
说明 |
|
Boolean matches(String regex) |
告知此字符串是否匹配给定的正则表达式。 |
String replaceAll(String regex,String replacement) |
使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 |
String replaceFirst(String regex,String replacement) |
使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。 |
String[] split(String regex) |
根据给定正则表达式的匹配拆分此字符串。 |
String[] split(String regex,int limit) |
根据匹配给定的正则表达式以及限制拆分大小来拆分此字符串。 |
注意:当正则表达式不仅仅只使用一次时,非String模式的正则表达式使用会更具性能。(因为String模式每次会先编译然后再使用)。
三、注意事项
1.如果要要求表达式所匹配的内容是整个字符串,而不是从字符串中找一部分,那么可以在表达式的首尾使用 "^" 和 "$",比如:"^\d+$" 要求整个字符串只有数字。
2.如果要求匹配的内容是一个完整的单词,而不会是单词的一部分,那么在表达式首尾使用 "\b",比如:使用"\b(if|while|else|void|int……)\b"来匹配程序中的关键字。
3.表达式不要匹配空字符串。否则会一直得到匹配成功,而结果什么都没有匹配到。比如:准备写一个匹配 "123"、"123."、"123.5"、".5" 这几种形式的表达式时,整数、小数点、小数数字都可以省略,但是不要将表达式写成:"\d*\.?\d*",因为如果什么都没有,这个表达式也可以匹配成功。更好的写法是:"\d+\.?\d*|\.\d+"。
4.能匹配空字符串的子匹配不要循环无限次。如果括号内的子表达式中的每一部分都可以匹配 0 次,而这个括号整体又可以匹配无限次,那么情况可能比上一条所说的更严重,匹配过程中可能死循环。虽然现在有些正则表达式引擎已经通过办法避免了这种情况出现死循环了,比如 .NET 的正则表达式,但是我们仍然应该尽量避免出现这种情况。如果我们在写表达式时遇到了死循环,也可以从这一点入手,查找一下是否是本条所说的原因。
5.合理选择贪婪模式与非贪婪模式。
6.或 "|" 的左右两边,对某个字符最好只有一边可以匹配,这样,不会因为 "|" 两边的表达式因为交换位置而有所不同。
四、举例说明
1.实例一:匹配
正则式是最简单的能准确匹配一个给定String的模式,模式与要匹配的文本是等价的.静态的Pattern.matches方法用于比较一个String是否匹配一个给定模式.如:
Stringdata="java";
booleanresult=Pattern.matches("java",data);
2.实例二:匹配
String[] dataArr = {"moon","mon","moon","mono" };
for (String str :dataArr) {
String patternStr="m(o+)n";
boolean result = Pattern.matches(patternStr,str);
if (result) {
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
注:模式”m(o+)n”表示mn中间的o可以重复一次或多次,因此moon,mon,mooon能匹配成功,而mono在n后多了一个o,和模式匹配不上.注:
+表示一次或多次;?表示0次或一次;*表示0次或多次.
3.实例三:匹配之方括号
String[] dataArr = {"ban","ben","bin","bon","bun","byn","baen"};
for (String str :dataArr) {
String patternStr="b[aeIoU]n";
boolean result = Pattern.matches(patternStr,str);
if (result) {
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
注:方括号中只允许匹配单个字符,模式“b[aeIoU]n”指定,只有以b开头,n结尾,中间是a,e,i,o,u中任意一个的才能匹配上,所以数组的前五个可以匹配,后两个元素无法匹配.
方括号[]表示只有其中指定的字符才能匹配.(但可以在其后加上“+”进行方括号里的多字符匹配)
4.实例四:匹配之括号
String[] dataArr = {"been","bean","boon","buin","bynn"};
for (String str :dataArr) {
String patternStr="b(ee|ea|oo)n";
boolean result = Pattern.matches(patternStr,str);
if (result) {
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
注:如果需要匹配多个字符,那么[]就不能用上了,这里我们可以用()加上|来代替,模式b(ee|ea|oo)n就能匹配been,bean,boon等.因此前三个能匹配上,而后两个不能.
()表示一组,|表示或的关系
5.实例五:匹配之预定义字符1
String[] dataArr = {"1","10","101","1010","100+"};
for (String str :dataArr) {
String patternStr="\\d+";
boolean result = Pattern.matches(patternStr,str);
if (result) {
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
注:从前面可以知道,\\d表示的是数字,而+表示一次或多次,所以模式\\d +就表示一位或多位数字.
因此前四个能匹配上,最后一个因为+号是非数字字符而匹配不上.
6.实例六: 匹配之预定义字符2
String[] dataArr = {"a100","b20","c30","df10000","gh0t"};
for (String str :dataArr) {
String patternStr="\\w+\\d+";
boolean result = Pattern.matches(patternStr,str);
if (result) {
System.out.println("字符串"+str+"匹配模式"+patternStr+"成功");
}
else{
System.out.println("字符串"+str+"匹配模式"+patternStr+"失败");
}
}
注:模式\\w+\\d+表示的是以多个单字字符开头,多个数字结尾的字符串,因此前四个能匹配上,最后一个因为数字后还含有单字字符而不能匹配.
7.实例七: 拆分字符串1
String str="薪水,职位姓名;年龄性别";
String[] dataArr=str.split("[,\\s;]");
for (String strTmp :dataArr) {
System.out.println(strTmp);
}
注:String类的split函数支持正则表达式,上例中模式能匹配”,”,单个空格,”;”中的一个,split函数能把它们中任意一个当作分隔符,将一个字符串劈分成字符串数组.
8.实例八: 拆分字符串2.
注意这里要把复杂的模式写在前面,否则简单模式会先匹配上.
String input="职务=GM薪水=50000,姓名=职业经理人 ;性别=男年龄=45 ";
StringpatternStr="(\\s*,\\s*)|(\\s*;\\s*)|(\\s +)";
Patternpattern=Pattern.compile(patternStr);
String[]dataArr=pattern.split(input);
for (String str :dataArr) {
System.out.println(str);
}
9.实例九:拆分字符串3
String str="2007年12月11日";
Pattern p =Pattern.compile("[年月日]");
String[] dataArr =p.split(str);
for (String strTmp :dataArr) {
System.out.println(strTmp);
}
注:Pattern是一个正则表达式经编译后的表现模式,它的split方法能有效劈分字符串.
注意其和String.split()使用上的不同.
10.实例十:分组应用
String str="10元 1000人民币 10000元 100000RMB";
str=str.replaceAll("(\\d+)(元|人民币|RMB)","¥$1");
System.out.println(str);
注:上例中,模式“(\\d +)(元|人民币|RMB)”按括号分成了两组,第一组\\d+匹配单个或多个数字,第二组匹配元,人民币,RMB中的任意一个,替换部分$1表示第一个组匹配的部分.
替换后的str为¥10 ¥1000 ¥10000 ¥100000
11.实例十一:非终端添加和替换
Pattern p =Pattern.compile("m(o+)n",Pattern.CASE_INSENSITIVE);
// 用Pattern类的matcher()方法生成一个Matcher对象
Matcher m =p.matcher("moon mooon Mon mooooon Mooon ccc");
StringBuffer sb = newStringBuffer();
// 使用find()方法查找第一个匹配的对象
boolean result =m.find();
while (result) {
m.appendReplacement(sb,"moon");
result = m.find();
}
// 最后调用appendTail()方法将最后一次匹配后的剩余字符串加到sb里;
m.appendTail(sb);
System.out.println("替换后内容是" + sb.toString());
注:输出为:替换后内容是moon moon moon moon moon ccc
12.实例十二:数量词
除了用+表示一次或多次,*表示0次或多次,?表示0次或一次外,还可以用{}来指定精确指定出现的次数,X{2,5}表示X最少出现2次,最多出现5次;X{2,}表示X最少出现2次,多则不限;X{5}表示X只精确的出现5次.
例程:
String[] dataArr = {"google","gooogle","gooooogle","goooooogle","ggle"};
for (String str :dataArr) {
String patternStr = "g(o{2,5})gle";
boolean result = Pattern.matches(patternStr,str);
if (result) {
System.out.println("字符串" + str + "匹配模式" + patternStr + "成功");
}else {
System.out.println("字符串" + str + "匹配模式" + patternStr + "失败");
}
}
13.实例十三:使用界限符”-“
“-”表示从..到..,如[a-e]等同于[abcde]
String[] dataArr = {"Tan","Tbn","Tcn","Ton","Twn"};
for (String str : dataArr){
String regex = "T[a-c]n";
boolean result = Pattern.matches(regex,str);
if (result) {
System.out.println("字符串" + str + "匹配模式" + regex + "成功");
} else {
System.out.println("字符串" + str + "匹配模式" + regex + "失败");
}
}
14.实例十四:不区分大小写匹配.
正则表达式默认都是区分大小写的,使用了Pattern.CASE_INSENSITIVE则不对大小写进行区分.
StringpatternStr="ab";
Patternpattern=Pattern.compile(patternStr,Pattern.CASE_INSENSITIVE);
String[] dataArr = {"ab","Ab","AB"};
for (String str :dataArr) {
Matcher matcher=pattern.matcher(str);
if(matcher.find()){
System.out.println("字符串" + str + "匹配模式" + patternStr + "成功");
}
}
15.实例十五:利用分组解析html
解析正则表达式中的文字,\\1对应第一个小括号括起来的group1.
Stringregex="<(\\w+)>(\\w+)</\\1>";
Patternpattern=Pattern.compile(regex);
Stringinput="<name>Bill</name><salary>50000</salary><title>GM</title>";
Matchermatcher=pattern.matcher(input);
while(matcher.find()){
System.out.println(matcher.group(2));
}
16.实例十六:将单词数字混合的字符串的单词部分大写.
Stringregex="([a-zA-Z]+[0-9]+)";
Patternpattern=Pattern.compile(regex);
Stringinput="age45 salary500000 50000 title";
Matchermatcher=pattern.matcher(input);
StringBuffer sb=newStringBuffer();
while(matcher.find()){
Stringreplacement=matcher.group(1).toUpperCase();
matcher.appendReplacement(sb,replacement);
}
matcher.appendTail(sb);
System.out.println("替换完的字串为"+sb.toString());
注:结果输出为: 替换完的字串为AGE45 SALARY500000 50000 title
17.实例十七:找出不包含某词的名称
如:从下面输入找出不包含饭堂的机构:
深圳市幼儿园饭堂
深圳市饭堂幼儿园
深圳市幼儿园
深圳市饭饭幼儿园
深圳市堂幼儿园
深圳市中央幼儿园
深圳市附属幼儿园大饭堂
其正则表达式为:(?m)^[\u4e00-\u9fa5&&[^饭堂]]*(饭[^堂])?堂?[\u4e00-\u9fa5&&[^饭堂]]*$
18.实例十八:使用分组在notpad++中对数据进行替换
这个实例是前几天我应用到的,认为还不错,所以在这里介绍给大家进行使用。
前几天我在处理数据的时候,有一份数据有20多万行机构数据,而其中包含了一些带有数字的记录,我想把这些带数字的机构选出来,然后作进一步的处理。
这种情况我们一般使用分组的方式进行替换即可实现,先定义一个分组把带数字的记录包含进去,然后用或者关系定义其它机构在另一个分组,然后在替换中引用第一个分组$1,这样即可实现保留分组1(即我们想保留的含数字的记录),而分组二我们在替换中没有引用,即会替换成空。然后再使用textFX插件去掉空行即可。
需要注意的是,很多文本处理工具目前都支持正则表达式的操作,但是其支持的情况各不相同,有时你会发现某些很正常的正则表达式并不生效,这时并不一定是表达式写错了,而是有可能其工具对正则表达式的支持并不全面导致的,特别是一些特定字符的表示、一些字符编码的表示。
五、常用正则表达式
下面的一些正则表达式提供给大家理解用。当理解正则表达式的基本用法后,大部份表达式都能很快的写出来,只有小部份针对字符区间的特殊应用是需要硬记的。
1.整数或者小数:^[0-9]+\.{0,1}[0-9]{0,2}$
2.匹配数字:"^[0-9]*$"。
3.匹配n位的数字:"^\d{n}$"。
4.匹配至少n位的数字:"^\d{n,}$"。
5.匹配m~n位的数字:"^\d{m,n}$"。
6.匹配零和非零开头的数字:"^(0|[1-9][0-9]*)$"。
7.匹配有两位小数的正实数:"^[0-9]+(.[0-9]{2})?$"。
8.匹配有1~3位小数的正实数:"^[0-9]+(.[0-9]{1,3})?$"。
9.匹配非零的正整数:"^\+?[1-9][0-9]*$"。
10.匹配非零的负整数:"^\-[1-9][]0-9"*$。
11.匹配长度为3的字符:"^.{3}$"。
12.匹配由26个英文字母组成的字符串:"^[A-Za-z]+$"。
13.匹配由26个大写英文字母组成的字符串:"^[A-Z]+$"。
14.匹配由26个小写英文字母组成的字符串:"^[a-z]+$"。
15.匹配由数字和26个英文字母组成的字符串:"^[A-Za-z0-9]+$"。
16.匹配由数字、26个英文字母或者下划线组成的字符串:"^\w+$"。
17.验证用户密码:"^[a-zA-Z]\w{5,17}$"正确格式为:以字母开头,长度在6~18之间,只能包含字符、数字和下划线。
18.验证是否含有^%&',;=?$\"等字符:"[^%&',;=?$\x22]+"。
19.验证Email地址:"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"。
20.验证InternetURL:"^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$"。
21.验证电话号码:"^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$"正确格式为:"XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX"。
22.验证身份证号(15位或18位数字):"^\d{15}|\d{18}$"。
23.验证一年的12个月:"^(0?[1-9]|1[0-2])$"正确格式为:"01"~"09"和"1"~"12"。
24.验证一个月的31天:"^((0?[1-9])|((1|2)[0-9])|30|31)$"正确格式为;"01"~"09"和"1"~"31"。
25.匹配中文字符的正则表达式(区间值为20901个): [\u4e00-\u9fa5]
26.匹配双字节字符(包括汉字在内):[^\x00-\xff]
27.匹配空行的正则表达式:\n[\s| ]*\r
28.匹配html标签的正则表达式:<(.*)>(.*)<\/(.*)>|<(.*)\/>
29.匹配首尾空格的正则表达式:(^\s*)|(\s*$)
30.匹配空行的正则表达式:\n[\s| ]*\r
31.匹配双字节符号:[^\x00-\xff&&[^\u4e00-\u9fa5]]+