在正则表达式诞生之前,通常我们对某一字符串文本进行验证的时候,需要编配大量的匹配规则,尤其出现在需要用户输入的地方,比如对用户输入的用户名,密码和邮箱进行输入验证,如果存在非法字符,则可能认为是恶意输入。下面通过Java语言介绍一个没有使用正则表达式的邮箱输入验证案例。
给定一个邮箱:(其中的空格实际上是下划线,显示问题)
对于该邮箱(任意邮箱),我们将其划分为3个部分,首先,邮箱一定至少包含一个"."号且只包含一个"@"符号,以这两个符号将邮箱分成3部分,第一部分为@之前的内容,可以是任意字母,数字,中划线,下划线,点号;第二部分为@与最后一个点号之间的内容,可以是任意字母,数字,中划线,下划线,但不可以是点号;第三部分为最后一个点号一直到结尾处的内容,实际上应该是域名,为了简化,我们认为此处内容也可以是任意字母,数字,下划线,中划线,但不可以是点号。
明白以上规则之后,见代码
public static boolean isDigit(char c){ if(c >= '0' && c <= '9'){ return true; }else{ return false; } } public static boolean isLetter(char c){ if((c >= 'a' && c <= 'z') || (a >= 'A' && a<= 'Z')){ return true; }else{ return false; } } public static boolean commVerify(String emailStr){ boolean flag = true; if(emailStr.indexOf(".") == -1 || emailStr.indexOf("@") == -1){ flag = false; }else{ int highIndex = emailStr.lastIndexOf("."); String highStr = emailStr.substring(highIndex+1); System.out.println(highStr); char highChars[] = highStr.toCharArray(); for(int i=0;i
以上代码虽然可以实现邮箱规则的匹配,但是各种条件语句和循环语句的嵌套导致结构不清晰,代码量多。如何改用正则表达式呢。这便是本文要介绍的问题。
正则表达式是一种文本规则描述的匹配工具,其规则和语法非常多,也不需要全部记住,但对最基本的一定要牢记,最基本最简单的正则表达式就是文本字符串本身, 用于匹配与该字符串相等的字符串。
[]表选择
表达式 | 描述 |
[abc] | 表示取值可以是a,可以是b,也可以是c |
[^abc] | 表示取值不是abc中的任意字符,即除abc以外的内容 |
[a-zA-Z] | 表示所有字母,大写和小写,[a-z]表示小写字母,[A-Z]表示大写字母 |
常用元字符
表达式 | 描述 |
. | 除换行符以外的任意字符 |
\d | 所有数字 |
\w | 所有字母,数字,下划线 |
\s | 所有空白字符 |
\b | 单词边界位置 |
^ | 字符串开始 |
$ | 字符串结尾 |
常用的反义字符
表达式 | 描述 |
\D | 非数字 |
\W | 非字母、数字、下划线 |
\S | 非空白字符 |
\B | 非单词开始或结束边界位置 |
表示出现的次数
表达式 | 描述 |
X? | 表示出现0次或1次 |
X* | 表示出现0次或多次 |
X+ | 表示出现1次或多次 |
X{n} | 表示出现n次 |
X{n,} | 表示出现的长度大于n次 |
X{n,m} | 表示出现n到m次 |
关系运算
表达式 | 描述 |
X|Y | 表示要么是X的正则,要么是Y的正则 |
(X) | 表示一组规范,一个分组 |
介绍了以上的正则表达式及其表示含义之后,大家都或多或少了解了一些相关知识,现在顺便介绍一下正则表达式的贪婪匹配和惰性匹配策略。一般匹配都是遵循贪婪匹配策略,即尽可能多地匹配,而以下规则支持惰性匹配,即尽可能少地匹配
表达式 | 描述 |
*? | 重复任意次,但尽可能少地重复 |
+? | 重复1次或多次,但尽可能少地重复 |
?? | 重复0次或1次,但尽可能少地重复 |
{n,m}? | 重复n到m次,尽可能少地重复 |
{n,}? | 重复至少n次,但尽可能少地重复 |
下面就上面介绍地邮箱验证的例子,我们给出使用正则表达式进行规则匹配的案例,对比之下,可以发现,使用正则表达式,不但能使程序结构清晰,也大大简化了代码量和我们的逻辑判断工作。
JAVA对正则表达式有着良好地支持,自JDK1.4,它提供了java.util.regex包,主要负责正则匹配的两个类为Pattern类和Matcher类,Pattern根据指定正则表达式字符串构建匹配模式,并利用matcher方法返回Matcher类对象实例,用于完成匹配,实际的匹配是交给Matcher完成的,Pattern类对象的构造是通过静态方法compile构造。
public static boolean regexVerify(String emailStr){ Pattern p = Pattern.compile("[\\w-\\.]+@[\\w-]+\\.[\\w-]+"); Matcher m = p.matcher(emailStr); if(m.matches()) //if(emailStr.matches("[\\w-\\.]+@[\\w-]+\\.[\\w-]+"))//String object has a good support to regexp return true; else return false; }
从以上代码可以看出,使用Java的正则表达式可以快速完成与大量逻辑判断相同的工作,且结构清晰,只要定义的正则表达式正确,其实在Java中一般不直接使用Pattern类和Matcher类完成正则模式匹配,因为正则匹配的对象都是文本字符串,目的就是为了验证文本规则,而String在Java中又是一个及其特殊的类,因此自从JDK1.4,引进正则表达式,String类就作了相应的修改,提供了split,matches,replaceAll,replaceFirst等方法。如上述代码注释的内容即为直接使用String对象的matches方法,其效果是完全一样的。
另外我想延伸的一点是,上述程序代码即正则表达式规则,虽然能够判别邮箱,但是邮箱通过的域名都是有限的(如163.com,qq.com),因此,本文提供精确的邮箱认证方式,即登录到邮箱服务器,验证邮箱是否存在,并返回验证结果。该功能需要Appache 的common-net.jar和dnsjava.jar包的支持。
public static boolean verifyDNS(String emailStr) { String host = ""; String hostName = emailStr.split("@")[1]; Record[] result = null; SMTPClient client = new SMTPClient() try { // 查找MX记录 Lookup lookup = new Lookup(hostName, Type.MX); lookup.run(); if (lookup.getResult() != Lookup.SUCCESSFUL) { return false; } else { result = lookup.getAnswers(); } // 连接到邮箱服务器 for (int i = 0; i < result.length; i++) { host = result[i].getAdditionalName().toString(); client.connect(host); if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) { client.disconnect(); continue; } else { break; } } client.login("xxxxxxxxxxxxxxxz.com"); client.setSender(); client.addRecipient(emailStr); if (250 == client.getReplyCode()) { return true; } } catch (Exception e) { e.printStackTrace(); } finally { try { client.disconnect(); } catch (Exception e) { } } return false; }
这部分代码拷贝至。