正值表达式(regular expression)是一个描述字符模式的对象,它是一种可以用于模式匹配和替换的强有力的工具,主要应用于处理文本操作,应用非常广泛,很多开发语言都对正值表达式进行支持,比如:java、c#、javascript、perl等。
本文档是关于正值表达式使用指南。包括:基本概念、开发规范、java处理方式、javascript处理方式、代码实例等。
通过该文档可以了解正值表达式的基本知识、开发规范、常用的开发方式等,并能够通过java、javascript语言进行基于正值表达式的开发工作。
本文档适合所有Java开发人员。
正则表达式最早是由数学家Stephen Kleene于1956年提出,主要使用在字符字符串的格式比对,后来在信息领域广为应用,现在已经成为ISO(国际标准组织)的标准之一。
一个正则表达式,就是用某种模式去匹配一类字符串的一个公式。可以把数小时辛苦而且易错的文本处理工作压缩在几分钟(甚至几秒钟)内完成。正则表达式被各种文本编辑软件、类库(例如Rogue Wave的tools.h++)、脚本工具(像awk/grep/sed)广泛的支持,而且像Microsoft的Visual C++这种交互式IDE也开始支持它了。
构造 |
匹配 |
|
|
字符 |
|
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 |
/t |
制表符 ('/u0009') |
/n |
新行(换行)符 ('/u 000A ') |
/r |
回车符 ('/u000D') |
/f |
换页符 ('/u 000C ') |
/a |
报警 (bell) 符 ('/u0007') |
/e |
转义符 ('/u001B') |
/cx |
对应于 x 的控制符 |
在正值表达式中,许多标点符号具有特殊的含义。它们是:^ $ . * + ? = ! : | / / () [] {}
斜线字符 ('/') 用于引用转义构造,如上表所定义的,同时还用于引用其他将被解释为非转义构造的字符。因此,表达式 // 与单个反斜线匹配,而 /{ 与左括号匹配。
将单独的字符放入方括号内可以组合为字符类(character class)。
符类可以出现在其他字符类中,并且可以包含并集运算符(隐式)和交集运算符 (&&)。并集运算符表示至少包含其某个操作数类中所有字符的类。交集运算符表示包含同时位于其两个操作数类中所有字符的类。
字符类运算符的优先级如下所示,按从最高到最低的顺序排列:
1 |
字面值转义 |
/x |
2 |
分组 |
[...] |
3 |
范围 |
a-z |
4 |
并集 |
[a-e][i-u] |
5 |
交集 |
[a-z&&[aeiou]] |
注意,元字符的不同集合实际上位于字符类的内部,而非字符类的外部。例如,正则表达式 . 在字符类内部就失去了其特殊意义,而表达式 - 变成了形成元字符的范围。
字符类 |
|
[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](减去) |
|
|
预定义字符类 |
|
. |
任何字符(与行结束符可能匹配也可能不匹配) |
/d |
数字:[0-9] |
/D |
非数字: [^0-9] |
/s |
空白字符:[ /t/n/x0B/f/r], /t(tab)、/n(new line)、/r(return) |
/S |
非空白字符:[^/s] |
/w |
单词字符:[a-zA-Z_0-9] |
/W |
非单词字符:[^/w] |
指定重复的字符总是出现在它们所作用的模式之后。
Greedy 数量词 |
|
X? |
X,一次或一次也没有 |
X* |
X,零次或多次 |
X+ |
X,一次或多次 |
X{n} |
X,恰好 n 次 |
X{n,} |
X,至少 n 次 |
X{n,m} |
X,至少 n 次,但是不超过 m 次 |
例如:
//s+java/s+ //match “java” with one or more spaces before and after
//d{2,4}/ // Match between two and four digits
Logical 运算符 |
|
XY |
X 后跟 Y |
X|Y |
X 或 Y |
(X) |
X,作为捕获组 |
|
|
Back 引用 |
|
/n |
任何匹配的 nth 捕获组 |
实例:/(ab|cd)+|ef)/ 匹配的既可以是字符串“ef”,也可以是字符串“ab”或者“cd”的一次或多次。
捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C))) 中,存在四个这样的组:
1 |
((A)(B(C))) |
2 |
/A |
3 |
(B(C)) |
4 |
(C) |
组零始终代表整个表达式。
之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。捕获的子序列稍后可以通过 Back 引用在表达式中使用,也可以在匹配操作完成后从匹配器检索。
与组关联的捕获输入始终是与组最近匹配的子序列。如果由于量化的缘故再次计算了组,则在第二次计算失败时将保留其以前捕获的值(如果有的话)例如,将字符串 "aba" 与表达式 (a(b)?)+ 相匹配,会将第二组设置为 "b"。在每个匹配的开头,所有捕获的输入都会被丢弃。
以 (?) 开头的组是纯的非捕获 组,它不捕获文本,也不针对组合计进行计数。
行结束符 是一个或两个字符的序列,标记输入字符序列的行结尾。以下代码被识别为行结束符:
边界匹配器 |
|
^ |
行的开头 |
$ |
行的结尾 |
/b |
单词边界 |
/B |
非单词边界 |
/A |
输入的开头 |
/G |
上一个匹配的结尾 |
/Z |
输入的结尾,仅用于最后的结束符(如果有的话) |
/z |
输入的结尾 |
例如:
标志 |
|
i |
执行一个不区分大小写的匹配 |
g |
执行一个全局匹配。简而言之,即要找到所有的匹配,而不是找到第一个后就停止。 |
m |
多行模式,^和$ 除了匹配字符串的开头和结尾外还匹配每行的开头和结尾 |
例如:查找所有java,//bjava/b/gi
正则表达式的编译表示形式。
指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建 Matcher 对象,依照正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。
因此,典型的调用顺序是
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();
在仅使用一次正则表达式时,可以方便地通过此类定义 matches 方法。此方法编译表达式并在单个调用中将输入序列与其匹配。语句
boolean b = Pattern.matches("a*b", "aaaaab");
等效于上面的三个语句,尽管对于重复的匹配而言它效率不高,因为它不允许重用已编译的模式
// 验证字符串是否为正整数或正小数
String regEx = "^[0-9]+//.?[0-9]*$";
Pattern pattern = Pattern.compile(regEx);
Matcher matcher = pattern.matcher(param);
if(!matcher.find()){
logger.debug(message + "必须为:正整数或正小数");
}
String text = "abcdebcadxbc"; String[] tokens = text.split(".bc");
for(String token : tokens) {
System.out.print(token + " ");
}
输出:d ad
使用.bc来作比对,由于符合的子字符串有abc、ebc、xbc 3个,所以split()方法会使用这3个字符串为依据来作字符串分离,返回的自然就是不符合表达式.bc的d与ad
JavaScript的RegExp类表示正值表达式,而String和RegExp都定义了使用正值表达式进行强大模式匹配和文本检索与替换函数。
ECMAScript v3对Javascript正值表达式进行了标准化。JavaScript 1.2实现了ECMAScript v3要求的正值表达式特性的子集,Javascript 1.5实现了完整的标准。
Javascript正值表达式完全以Perl程序语言的正值表达式为基础。
l 表单验证。验证某些域符合某种规则,例如邮件输入框必须输入的是邮件、联系电话输入框输入的必须是数字等等
l 处理DOM模型。例如通过表达式定位DOM中的一个对象或一系列对象,一个例子就是定位id属性中含有某个特殊字符的div对象。
l 纯编程逻辑。直接用于编程的逻辑之中。
类String支持四种利用正值表达式的方法,
最简单的search(),例如:”Javascirpt”.search(/script/i);
replace()进行检索和替换。例如:text.replace(/javacript/gi), “JavaScript”);
match是最常用的。” 1 plus 2 equals 3” .match(//d+/g) // 返回[“ 1” , “ 2” , “ 3” ]
split可以分解为字符串数组。例如:”1,2, 3, 4, 5” .split(//s*,/s*/); // returns [“ 1” ,” 2” ,” 3” ,” 4” , “ 5” ]。
无论是字符串直接量还是正值表达式都使用了字符/表示转义序列,所以必须用//替换所有的/字符。例如:var zipCode = new RegExp(“//d{5}”, “g”);
exec()方法在一个字符串中检索匹配。如果没有则返回null。例如:
var pattern = /Java/g;
var test = “JavaScript is more fun than Java!”;
var result;
while((reslut = pattern.exec(text)) != null) {
alter(“mathed ‘” + result[0] + “’” + “at position “ + result.index + “;next search begins at “ + pattern.lastIndex);
}
test()比较简洁。
var pattern = /java/I;
pattern.test(“Javascript”); // return true
注意:只有带g标志的正值表达式才会发生这种特殊的lastIndex行为。如果没有g,exec()和test()将忽略它的lastIndex。
匹配中文字符的正则表达式: [/u4e00-/u9fa5] 评注:匹配中文还真是个头疼的事,有了这个表达式就好办了 匹配双字节字符(包括汉字在内):[^/x00-/xff] 评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1) 匹配空白行的正则表达式:/n/s*/r 评注:可以用来删除空白行 匹配HTML标记的正则表达式:<(/S*?)[^>]*>.*?</>|<.*? /> 评注:网上流传的版本太糟糕,上面这个也仅仅能匹配部分,对于复杂的嵌套标记依旧无能为力 匹配首尾空白字符的正则表达式:^/s*|/s*$ 评注:可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式 匹配Email地址的正则表达式:/w+([-+.]/w+)*@/w+([-.]/w+)*/./w+([-.]/w+)* 评注:表单验证时很实用 匹配网址URL的正则表达式:[a-zA-z]+://[^/s]* 评注:网上流传的版本功能很有限,上面这个基本可以满足需求 匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$ 评注:表单验证时很实用 匹配国内电话号码:/d-/d|/d-/d 评注:匹配形式如 0511-4405222 或 021-87888822 匹配腾讯QQ号:[1-9][0-9]{4,} 评注:腾讯QQ号从10000开始 匹配中国邮政编码:[1-9]/d(?!/d) 评注:中国邮政编码为6位数字 匹配身份证:/d|/d 评注:中国的身份证为15位或18位 匹配ip地址:/d+/./d+/./d+/./d+ 评注:提取ip地址时有用
l "^//d+$" //非负整数(正整数 + 0)
l "^[0-9]*[1-9][0-9]*$" //正整数 包括:以0开头
l /^/+?[1-9][0-9]*$/ // 正整数 不能以0开头
l "^((-//d+)|(0+))$" //非正整数(负整数 + 0)
l "^-[0-9]*[1-9][0-9]*$" //负整数
l "^-?//d+$" //整数
l "^//d+(//.//d+)?$" //非负浮点数(正浮点数 + 0)
l "^(([0-9]+//.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*//.[0-9]+)|([0-9]*[1-9][0-9]*))$" //正浮点数
l "^((-//d+(//.//d+)?)|(0+(//.0+)?))$" //非正浮点数(负浮点数 + 0)
l "^(-(([0-9]+//.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*//.[0-9]+)|([0-9]*[1-9][0-9]*)))$" //负浮点数
l "^(-?//d+)(//.//d+)?$" //浮点数
1. 《Javascript 权威指南》第5版
2.