浪潮软件技术研究中心 申科建
1 介绍
正则表达式(Regular Expressions)描述了一种字符串匹配的模式,可以用来检查一个字符串是否含有某种子串、将匹配的子串做替换或者从某个字符串中取出符合某个条件的子串等。这样的表达式“^[+|-]?\d*\.\d+$”,如果以前从没接触过,乍看的确像古老的咒语,只有巫师能懂。但这正是表达式的强大之所在,正则表达式把复杂的问题简单化,简单到只有几个字符。如果不理解规则,就会把简单的几个字符复杂化,复杂到不可理解,但如果理解规则,那么就会发现,表达式就是表达式,它的确就是几个符号,表达一个明确的意思。
2 正则表达式的组成
正则表达式由特殊字符以及普通字符组成,特殊字符是正则表达式规则定义的有特殊含义的字符,普通字符是除特殊字符外的所有打印和非打印字符。
2.1 打印字符
所有大小写字母,所有数字,所有标点符号,以及一些特殊符号。
2.2 非打印字符
字符
含义
\cx
匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。X 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
\f
匹配一个换页符。等价于 \x0c 和 \cL。
\n
匹配一个换行符。等价于 \x0a 和 \cJ。
\r
匹配一个回车符。等价于 \x0d 和 \cM。
\s
匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S
匹配任何非空白字符。等价于 [^\f\n\r\t\v]。
\t
匹配一个制表符。等价于 \x09 和 \cI。
\v
匹配一个垂直制表符。等价于 \x0b 和 \cK。
2.3 特殊字符
特殊字符是有特殊含义的字符,如果需要使用它的原义,必须在字符前使用“\”进行转义(此处不再列举)。
3 操作符优先级
相同优先级的从左到右进行运算,不同优先级的运算先高后低。各种操作符的优先级从高到低如下:
操作符
描述
转义符
(), (?:), (?=), []
圆括号和方括号
*, +, ?, {n}, {n,}, {n,m}
限定符
^, $, \anymetacharacter
位置和顺序
|
“或”操作
4.正则表达式的简单示例
4.1 基本模式匹配
表达式“friend”匹配一个字符串中任何位置的“friend”。
4.2 定位符匹配
表达式“^friend$”只匹配“friend”字符串。其中^表示匹配字符串开始位置,$表示匹配字符串结束位置。
4.3 字符簇匹配
表达式“^[abcd]”匹配一个字符串中开始位置的a或b或c或d字符,只匹配一个字符。
表达式“^[^ab]”匹配一个字符串中开始位置的非a或b的字符,其中^在“[]”中表示“非”,而不是开始位置。
表达式“^[A-Z]”匹配一个字符串中开始位置的大写字符,其中“-”表示范围。
4.4重复次数
表达式“ab{2}”匹配一个字符串中的“abb”,其中“{2}”表示前面的子表达式重复2次。
表达式“ab{0,}”匹配一个字符串中的a加任意多个b(包括0个),其中“{0,}”表示前面的子表达式重复0或多次,“*”也可以表示0或多次,所以也可以写成“ab*”。
表达式“ab{1,}”匹配一个字符串中的a加1个或任意多个b,其中“{1,}”表示前面的子表达式重复1次或多次,“+”也可以表示重复1次或多次,所以也可以写成“ab+”。
表达式“ab{0,1}”匹配一个字符串中的“a”或“ab”,其中“{0,1}”表示前面的子表达式重复0次或1次,“?”也可以表示0次或1次,所以也可以写成“ab?”。
4.5 正则表达式的贪婪性
正则表达式默认总是尽可能多的匹配字符串,这种特性叫做正则表达式的贪婪性。例如表达式“a+”将匹配字符串“aaaaaaaa”中的所有a。
如果想改变这种特性,可以使用“?”,使表达式尽可能少的匹配字符串。例如表达式“^a+?”将匹配字符串“aaaaaa”中的第一个a。
4.6 一个实用例子
前面提到的表达式“^[+|-]?\d*\.\d+$”表示一个浮点数。^表示开始,[+|-]表示是+或-,?表示+-号有或没有,\d*表示0或多位数字,\.表示小数点,\d+表示1或多位数字,$表示结束。
5 正则表达式应用示例
下面的例子,只是简单介绍正则表达式在JavaScript、Java、C中的应用。虽然正则表达式有标准,但是在具体实现时,并非完全相同,还有一些差异,如果想更全面、深入的了解,请参考相关资料。
5.1 正则表达式在JavaScript中的应用
JavaScript中有一个RegExp类用来处理正则表达式,下面是一个例子,判断一个输入是否是浮点数:
<Script language=”JavaScript”>
<!--
function checkFloat(str) {
var re = /^[+|-]?\d*\.\d+$/;
return re.test(str);
}
//-->
</Script>
JavaScript中的String类也有一些函数用到正则表达式,下面是一个例子,将日期从yyyy-mm-dd格式转换成yyyy年mm月dd日格式:
<Script language=”JavaScript”>
<!--
function changeFormat(str) {
return str.replace(/(\d{4})-(\d{2})-(\d{2})/, "$1年$2月$3日");
}
//-->
</Script>
5.2 正则表达式在Java中的应用
Java中的java.util.regex包提供对正则表达式的支持,下面是一个例子,判断一个输入是否是浮点数:
public static boolean checkFloat(String str) {
return Pattern.matches("^[+|-]?\\d*\\.\\d+$", str);
}
注意:在java语言中,String中的\使用\表示。
Java中的String类也有一些函数使用正则表达式,下面是一个例子,将日期从yyyy-mm-dd格式转换成yyyy年mm月dd日格式:
public static String changeFormat(String str) {
return str.replaceAll("(\\d{4})-(\\d{2})-(\\d{2})", "$1年$2月$3日");
}
5.3 正则表达式在C语言中的应用
需要包含两个头文件:sys/types.h、regex.h,有四个函数:regcomp编译表达式函数,regexec表达式处理 函数,regerror错误处理函数,regfree释放资源。
下面一个例子,判断一个输入是否是浮点数:
int checkFloat(char *str) {
int n;
regex_t re;
regmatch_t pm[10];
char buf[256], pattern[256];
strcpy(pattern, "^[+|-]?[0-9]*\\.[0-9]+$");
n = regcomp(&re, pattern, REG_EXTENDED);
if (n != 0) {
regerror(n, &re, buf, sizeof(buf));
fprintf(stderr, “err:[%d]%s”, n, buf);
return n;
}
n = regexec(&re, str, sizeof(pm)/sizeof(regmatch_t), pm, 0);
if (n != 0) {
if (n != REG_NOMATCH) {
regerror(n, &re, buf, sizeof(buf));
fprintf(stderr, “err:[%d]%s”, n, buf);
}
}
regfree(&re);
return n;
}
下面是一个例子,将日期从yyyy-mm-dd格式转换成yyyy年mm月dd日格式:
int changeFormat(char *str) {
int n, i;
regex_t re;
regmatch_t pm[10];
char buf[256], pattern[256];
strcpy(pattern, "([0-9]{4})-([0-9]{2})-([0-9]{2})");
n = regcomp(&re, pattern, REG_EXTENDED);
if (n != 0) {
regerror(n, &re, buf, sizeof(buf));
fprintf(stderr, "err:[%d]%s", n, buf);
return n;
}
n = regexec(&re, str, sizeof(pm)/sizeof(regmatch_t), pm, 0);
if (n != 0) {
if (n != REG_NOMATCH) {
regerror(n, &re, buf, sizeof(buf));
fprintf(stderr, "err:[%d]%s", n, buf);
}
} else {
memset(buf, 0x00, sizeof(buf));
strncpy(buf, str + pm[1].rm_so, pm[1].rm_eo - pm[1].rm_so);
strcat(buf, "年");
strncat(buf, str + pm[2].rm_so, pm[2].rm_eo - pm[2].rm_so);
strcat(buf, "月");
strncat(buf, str + pm[3].rm_so, pm[3].rm_eo - pm[3].rm_so);
strcat(buf, "日");
strcpy(str, buf);
}
regfree(&re);
return n;
}
6 结束语
正则表达式应用非常广泛,在字符串处理时,往往有意想不到的效果,但写出高技巧的表达式,往往需要一定的功底,。
表达式其实不难,难的是记住它的规则,我认为最好的方法是多用,不需要死记规则,碰到不理解的规则查找资料,用多了就记住了。当然了,如果你超强能记住规则最好。
另外,虽然有标准,但不同的地方还是有细微差别的,在使用时也需要注意。
7 参考资料
正则表达式标准:http://www.opengroup.org/onlinepubs/007908799/xbd/re.html
网友笑容编写的《正则表达式(regular expression)》http://dev.csdn.net/develop/article/41/41299.shtm
Jeffrey E.F.Friedl编写的《Mastering Regular Expressions》,这本书是正则表达式的极好资料,不过是E文的,780页,太多了,我没参考。
另外还有从网上找的其他资料,不再一一列举。