前言
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard),也就是和?。如果你想查找某个目录下的所有的Word文档的话,你会搜索.doc。在这里,*会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求——当然,代价就是更复杂——比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像010-12345678或0376-7654321)。
测试正则表达式
如果你不觉得正则表达式很难读写的话,要么你是一个天才,要么,你不是地球人。正则表达式的语法很令人头疼,即使对经常使用它的人来说也是如此。由于难于读写,容易出错,所以找一种工具对正则表达式进行测试是很有必要的。
不同的环境下正则表达式的一些细节是不相同的,本教程介绍的是微软 .Net Framework 4.5 下正则表达式的行为,所以,我向你推荐我编写的.Net下的工具 Regester。请参考该页面的说明来安装和运行该软件。
注意事项
1、一个字母就是一个表达式
runooa+b,可以匹配 runoob、runooab、runoooooab 等,+ 号代表前面的字符必须至少出现一次(1次或多次)。
runoob,可以匹配 runob、runoob、runoooooob 等, 号代表字符可以不出现,也可以出现一次或者多次(0次、或1次、或多次)。
colou?r 可以匹配 color 或者 colour,? 问号代表前面的字符最多只可以出现一次(0次、或1次)。
i | 执行对大小写不敏感的匹配。 |
---|---|
g | 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。 |
m | 执行多行匹配。 |
特殊字符
所谓特殊字符,就是一些有特殊含义的字符,如上面说的 runoo*b 中的 *,
简单的说就是表示任何字符串的意思。如果要查找字符串中的 * 符号,则需要对 * 进行转义,
即在其前加一个 \: runo\*ob 匹配 runo*ob。
许多元字符要求在试图匹配它们时特别对待。若要匹配这些特殊字符,必须首先使字符"转义",
即,将反斜杠字符\ 放在它们前面。下表列出了正则表达式中的特殊字符:
特别字符 | 描述 |
---|---|
$ | 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n’ 或 ‘\r’。。 |
( ) | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。 |
* | 匹配前面的子表达式零次或多次。 |
+ | 匹配前面的子表达式一次或多次。 |
. | 匹配除换行符 \n 之外的任何单字符。 |
[ | 标记一个中括号表达式的开始。 |
? | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。 |
\ | 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。 |
^ | 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。 |
{ | 标记限定符表达式的开始。 |
| | 指明两项之间的一个选择。 |
正则表达式 - 元字符
下表包含了元字符的完整列表以及它们在正则表达式上下文中的行为:
字符 | 描述 |
---|---|
\ | 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,‘n’ 匹配字符 “n”。‘\n’ 匹配一个换行符。序列 ‘\’ 匹配 “” 而 “(” 则匹配 “(”。 |
^ | 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 ‘\n’ 或 ‘\r’ 之后的位置。 |
$ | 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 ‘\n’ 或 ‘\r’ 之前的位置。 |
* | 匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次。例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。 |
? | 匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 或 “does” 。? 等价于 {0,1}。 |
{n} | n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。 |
{n,} | n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o*’。 |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。 |
? | 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 “oooo”,‘o+?’ 将匹配单个 “o”,而 ‘o+’ 将匹配所有 ‘o’。 |
x|y | 匹配 x 或 y。例如,'z |
[xyz] | 字符集合。匹配所包含的任意一个字符。例如, ‘[abc]’ 可以匹配 “plain” 中的 ‘a’。 |
[^xyz] | 负值字符集合。匹配未包含的任意字符。例如, ‘[^abc]’ 可以匹配 “plain” 中的’p’、‘l’、‘i’、‘n’。 |
[a-z] | 字符范围。匹配指定范围内的任意字符。例如,‘[a-z]’ 可以匹配 ‘a’ 到 ‘z’ 范围内的任意小写字母字符。 |
[^a-z] | 负值字符范围。匹配任何不在指定范围内的任意字符。例如,‘[^a-z]’ 可以匹配任何不在 ‘a’ 到 ‘z’ 范围内的任意字符。 |
\d | 匹配一个数字字符。等价于 [0-9]。 |
---|---|
\D | 匹配一个非数字字符。等价于 [^0-9]。 |
\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。 |
\w | 匹配字母、数字、下划线。等价于’[A-Za-z0-9_]'。 |
\W | 匹配非字母、数字、下划线。等价于 ‘[^A-Za-z0-9_]’。 |
限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。
有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。
正则表达式的限定符有:
字符 | 描述 |
---|---|
* | 匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次。例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。 |
? | 匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 、 “does” 中的 “does” 、 “doxy” 中的 “do” 。? 等价于 {0,1}。 |
{n} | n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。 |
{n,} | n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o*’。 |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。 |
正则表达式中(括号) [方括号] {大括号}的区别
括号() : 括号是多个匹配,它把括号内的当做一组来处理,限制一些多选的范围,比如上面的需求只能是com cn net结尾的用括号就是最好的选择。
括号能提取字符串,如(com|cn|net)就可以限制,只能是com或cn或net。
括号将括号里面的内容作为一组,这就是与[]不同的地方。
方括号[]: 方括号是单个匹配,如[abc]他限制的不是abc连续出现,而是只能是其中一个,这样写那么规则就是找到这个位置时只能是a或是b或是c;
方括号是正则表达式中最常用的,常用的用法有:[a-zA-Z0-9]匹配所有英文字母和数字,[^a-zA-Z0-9]匹配所有非英文字母和数字。
大括号{}: 大括号的用法很简单,就是匹配次数,它需要和其他有意义的正则表达式一起使用。
比如[a-c]{2}意思就是匹配a-c之间的一个字母出现且只出现两次;
比如(com){1}意思就是com必须出现一次
比如\W{1,3}意思就是非字母数字最少出现一次最多出现3次。
案列:
1)数据的长度
2)用户名由英文字母和数字组成的4-16位字符,以字母开头
^[a-zA-Z]{1}[a-zA-Z0-9]{3,15}$
3)身份证
^[1-9]\d{5}[12]\d{3}((0?[1-9])|(1[012])){1}((0?[1-9])|([1-2]\d)|(3[0-1]))\d{3}[0-9X]$
4)中文
[\u4e00-\u9fa5]
5)密码必须由英文字母和数字组成的4-10位字符
^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{4,10}
6)……邮箱
^[\w\d]+@[\w\d]+(\.\w{2,3}){1,2}
7)邮政编码
430000 以43开头 01 09之间 00 到 50之间
8)出生日期
^((0?[1-9])|(1[012])){1}-((0?[1-9])|([1-2]\d)|(3[0-1])){1}$
9)手机号
^(1)\d{10}$
以1 开头 重复10个数字
代码案例
package com.ft.utils;
import org.junit.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 测试类方法
*
* @author lihua
*/
public class Tools {
private static Pattern p = Pattern.compile("\\[(.*?)\\]", Pattern.CASE_INSENSITIVE);
private static Pattern p1 = Pattern.compile(":(\\w+)");
private final static Pattern pattern = Pattern.compile("[0-9]*");
@Test
public void testRegex() {
String str = "15972979517";
String regex = "^[0-9]{11}$";
boolean matches = str.matches(regex);
System.out.println("matches==" + matches);
}
/**
* 利用正则表达式判断字符串是否是数字
*
* @return
* @paramisSpecialChar
*/
@Test
public void isNumeric() {
Matcher isNum = pattern.matcher("");
if (!isNum.matches()) {
System.out.println("false");
}
System.out.println("true");
}
/**
* 包括空格判断
*
* @param
* @return
*/
@Test
public void containSpace() {
System.out.println(Pattern.compile("lihua").matcher("123lihua455").find());
System.out.println(Pattern.compile("lihua").matcher("123lihua455").matches());
}
/**
* 特殊字符判断
*
* @param
* @return
*/
@Test
public void isSpecialChar() {
String input = "123";
String regEx = "[\n\r\'\"\\|;$+`~%!@#^=''?~!@#¥ ……&——‘”“'?*()(),,。.、《》<>:;|/\\{}{}【】\\[\\]]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(input);
System.out.println(m.find());
}
@Test
public void test() {
String str = "fafag[[AaojfoafA]]gdthtyh";
String newstr = "";
String regEx = "(\\[+)(\\S*)(]+)";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(str);
//是否与匹配器匹配
if (m.find()) {
String s = str.replaceFirst("\\[", "");
int index = s.lastIndexOf("]");
String substring = s.substring(0, index);
System.out.println(substring + s.substring(index + 1));
}
}
@Test
public void test10() {
String str = "1";
String reg = "[a-zA-Z]";
Pattern compile = Pattern.compile(reg);
Matcher matcher = compile.matcher(str);
if (matcher.matches()) {
System.out.println("是全字母");
}
if (matcher.find()) {
System.out.println("有字母");
}
}
@Test
public void test11() {
String content = "I am noob " +
"from runoob.com.";
String pattern = ".*runoob.*";
boolean isMatch = Pattern.matches(pattern, content);
System.out.println("字符串中是否包含了 'runoob' 子字符串? " + isMatch);
}
}