正则表达式

正则表达式是一种用来进行文本匹配的工具,其语法优美简洁。在开发中,查找、对比以及匹配字符串是家常便饭的业务,通过正则表达式我们将这些业务描述成某些需求规则,来让我们的代码更美观、实用。例如我们要验证用户输入的密码长度是否满足6~18位的长度,新手最常见的验证方式是判断输入的密码长度

return (textField.text.length >= 6 && textField.text.leng <= 18);

尽管这种判断方式没有任何问题,而上面的验证换做正则表达式的匹配字符就显得短小精悍

^.{6,18}$

还有,大部分的固定电话都是0区号-八位数字的格式,那么正则表达式的匹配如下

^0\\d{2}\-?\\d{8}$

再比如,对于密码强度的认证。如今的密码应该包括一个或以上的大写字母以及小写字母,对此正则表达式的匹配是

(^.*[A-Z]+.*[a-z]+.*$|^.*[a-z]+.*[A-Z]+.*$)

表达式字符解析

如果是新手开发者,那么很有可能不理解上面的正则表达式。那么我们先看第一个匹配条件,匹配6-18位的密码长度。根据题目的要求,就能够轻易的判断出{6,18}表示6-18位。其中,{n}匹配字符重复n次;{n,}匹配重复n次或更多次;{n,m}匹配重复n到m之间次数。

那么读者对^.$三个符号可能不明所以。^$属于特殊符号,前者表示匹配字符串的开头,后者表示匹配字符串的结尾。由于我们有时需要在一段很长的字符串中匹配某些小片段字符串时(比如在图文混编中匹配长字符串中留空的表情符号),适当使用这两个符号可以减少匹配次数,提高执行效率。

那么结合这几个字符代表含义,可以得出.符号表示任一字符(除换行符之外)。那么还可以得出{6,18}这个特殊符号表示的是前一个符号代表的位数。结合起来说^.{6,18}$匹配一段6至18位长度的字符串,所以可以用来验证密码长度

当然了,.字符的匹配率太高,更多时候我们需要匹配的是具体到数字或者字母,甚至准确的数字和字母。那么除了.字符以外,还有\d用来表示任意的单个数字,\w表示任意一个字母或数字或者直接使用0-9的任意数字表示具体数字。而特殊符号?表示前一个字符为0或者1个。

所以上面的^0\\d{2}\-?\\d{8}$匹配了以数字0开头,接着两个数字,后面跟着-号跟八个数字,然后结尾的字符串,不管是010-88888888,还是02098989898都能进行正确匹配。

最后的一个表达式可能是最复杂的表达式,根据上面对部分符号的解析。我们可以把

^.*[A-Z]+.*[a-z]+.*$|^.*[a-z]+.*[A-Z]+.*$

拆分成 ^.*[A-Z]+.*[a-z]+.*$ 跟 ` ^.[a-z]+.[A-Z]+.*$`

这两个单独的表达式,而中间的|不难猜到就是逻辑的或。我们将对前一个进行拆分,来一步步识别这个表达式的匹配规则。这里教大家我拆分表达式的方法——我把表达式的字符分为两类:值表达和修饰表达。所谓值表达就是说这个符号表示了某个值,就像\d表示数字,.表示任意非换行符字符。修饰表达用来修饰值达成某种条件,比如{2}表示前面一个值重复两次,表示前一个值重复0次或者更多次。根据这种方法,那么^.*[A-Z]+.*[a-z]+.*$可以拆分成部分:^.*[A-Z]+ 以及 [a-z]+.*

^$就不再多说。

.*这里要介绍的就是*, 表示前一个值符号重复任意次数。

[A-Z]+中-表示从左边的值到右边的值之间所有值形成的闭集合;[]方括号表示的值必须是括号中间集合的子集,要注意的是括号里面可以有多个集合,比如[A-Z0-9a-z]表示匹配任意一个大小写字母或数字;+*有点像,但是+表示的是至少一个的重复值。

结合上面的解析,那么^.*[A-Z]+.*[a-z]+.*$表示以任意数量的字符开头,然后跟着一个大写字母,大写字母后面有任意数量的字符以及一个小写字母,然后又是任意数量的数字、字母或者字符。而^.*[a-z]+.*[A-Z]+.*$表示小写字母在大写字母前面任意数量的字符的位置,两个结合匹配可以确保字符串中包括至少一个小写字母和一个大写字母。

ps:有一点要注意,上面展示的表达式解析时\d这些特殊符号在我们的代码中多了一个\,这是因为\本身是转义符号,为了保证表达式能正常匹配,我们要给\进行一次转义,所以就变成了\\。基本上所有的符号字符都需要转义。


语法/字符说明表

----> 值表达

.      匹配除换行符外的任意字符

\w     匹配字母或者数字的字符

\W     匹配任意不是字母或数字的字符

\s     匹配任意的空白符(空格、制表符、换行符)

\S     匹配任意不是空白符的字符

\d     匹配任意数字

\D     匹配任意非数字的字符

\b     匹配单词的结尾或者开头的字符

\B     匹配任意不是单词结尾或开头的字符

[^x]   匹配任意非x的字符。如[^[a-z]]匹配非小写字母的任意字符

^      匹配字符串的开头

$      匹配字符串的结尾

----> 修饰表达

*	   匹配重复任意次数

+ 	   匹配重复一次以上的次数

?      匹配一次或零次

{n}    匹配重复n次

{n,}   匹配重复n次或n次以上

{n,m}  匹配重复最少n次最多m次

除了上面列出的字符外,还有包括表示位置指定等较难运用的其他正则表达式,但上面的字符已经足够我们正常使用了。想了解更多知识可以度娘Google。


代码实操

上面我们只是简单的讲解了正则表达式中各个字符代表的意义,那么在iOS开发中应该怎么使用。对于有意使用正则规则来匹配的开发者,我的建议是封装成为类别方法,一次封装,多次调用。下面用我自己封装的代码进行说明。这些方法通过扩展UITextField方法来实现:

@interface UITextField (LXDValidate)

/*! 判断文本框是否为空(非正则表达式)*/

- (BOOL)isEmpty;

/*! 判断邮箱是否正确*/

- (BOOL)validateEmail;

/*! 判断验证码是否正确*/

- (BOOL)validateAuthen;

/*! 判断密码格式是否正确*/

- (BOOL)validatePassword;

/*! 判断手机号码是否正确*/

- (BOOL)validatePhoneNumber;

/*! 自己写正则传入进行判断*/

- (BOOL)validateWithRegExp: (NSString *)regExp;

@end

@implementation UITextField (LXDValidate)

- (BOOL)isEmpty
{
   return self.text.length == 0;
}

- (BOOL)validateEmail
{
	return [self validateWithRegExp: @"^[a-zA-Z0-9]{4,}@[a-z0-9A-Z]{2,}\\.[a-zA-Z]{2,}$"];
}
- (BOOL)validateAuthen
{
	return [self validateWithRegExp: @"^\\d{5,6}$"];
}

- (BOOL)validatePassword
{
	NSString * length = @"^\\w{6,18}$";        //长度
	NSString * number = @"^\\w*\\d+\\w*$";      //数字
	NSString * lower = @"^\\w*[a-z]+\\w*$";      //小写字母
	NSString * upper = @"^\\w*[A-Z]+\\w*$";    //大写字母
	return [self validateWithRegExp: length] && [self validateWithRegExp: number] && [self validateWithRegExp: lower] && [self validateWithRegExp: upper];
}

- (BOOL)validatePhoneNumber
{
	NSString * reg = @"^1\\d{10}$"; 
	return [self validateWithRegExp: reg];
}

- (BOOL)validateWithRegExp: (NSString *)regExp
{
    NSPredicate * predicate = [NSPredicate predicateWithFormat: @"SELF MATCHES %@", regExp];
	return [predicate evaluateWithObject: self.text];
}

@end