iOS中使用正则表达式就不得不提NSRegularExpression,所以我们需要先搞清楚什么是NSRegularExpression,养成好习惯先查看下文档:NSRegularExpression文档,看到文档中的第一段:
# 原文:
The NSRegularExpression class is used to represent and apply regular expressions to Unicode strings. An instance of this class is an immutable representation of a compiled regular expression pattern and various option flags. The pattern syntax currently supported is that specified by ICU. The ICU regular expressions are described athttp://userguide.icu-project.org/strings/regexp.
大概翻译(翻译可能不太准,但是意思大概就是这样。建议大家看原文。):
NSRegularExpression是用Unicode字符串表达和应用正则表达式的类。这个类的实例是代表一个不可变的正则表达式和选项标识。当前支持的语法是ICU。ICU正则表达式的具体说明在:http://userguide.icu-project.org/strings/regexp
第一段话给了我们4个信息:
1) NSRegularExpression支持的是Unicode字符,代表可以输入所有字符。
2) NSRegularExpression实例的表达式内容和选项标识是不能改变的。直接查看NSRegularExpression的.h文件:
```
@interface NSRegularExpression:NSObject ()
@property(readonly,copy)NSString*pattern;
@property(readonly)NSRegularExpressionOptionsoptions;
+ (nullableNSRegularExpression*)regularExpressionWithPattern:(NSString*)pattern options:(NSRegularExpressionOptions)options error:(NSError**)error;
- (nullable instancetype)initWithPattern:(NSString*)pattern options:(NSRegularExpressionOptions)options error:(NSError**)errorNS_DESIGNATED_INITIALIZER;
@end
```
从.h文件中可以看出,属性pattern和options是用readonly修饰的代表着只读不可赋值,在初始化的时候可以赋值。
3) NSRegularExpression支持的ICU正则表达式语法。
4.)ICU正则表达式的详解地址:http://userguide.icu-project.org/strings/regexp(这个被墙了,需要翻墙。)
ICU正则表达式是基于Perl正则表达式的,而Perl正则表达式则是基于Version 8 Regular Expressions,有兴趣的可以自行查阅:ICU Regular Expressions,Perl Regular Expressions,Version 8 Regular Expressions,在这里就不祥说了。
Flag Options
先看到文档中的Flag Options,标识选项。如下表:
Flag Options描述
i不区分大小写
x允许空格和注释
s.匹配行终止符,默认情况下是不匹配的
m^和$匹配每一行的开头和结尾,默认情况下是只匹配文本开头和结尾。
Para如果设置了,字的边界按照Unicode UAX 29中的文本边界发现单词的定义。默认情况下,字边界由字符的简单分类为“word”或“non-word”,这近似于传统正则表达式行为的手段鉴定。用两个选择获得的结果可以是在空间和其他非单词字符的运行完全不同。
我们再来看看NSRegularExpressionOptions:
typedefNS_OPTIONS(NSUInteger, NSRegularExpressionOptions){ NSRegularExpressionCaseInsensitive =1<<0,/* Match letters in the pattern independent of case. */NSRegularExpressionAllowCommentsAndWhitespace =1<<1,/* Ignore whitespace and #-prefixed comments in the pattern. */NSRegularExpressionIgnoreMetacharacters =1<<2,/* Treat the entire pattern as a literal string. */NSRegularExpressionDotMatchesLineSeparators =1<<3,/* Allow . to match any character, including line separators. */NSRegularExpressionAnchorsMatchLines =1<<4,/* Allow ^ and $ to match the start and end of lines. */NSRegularExpressionUseUnixLineSeparators =1<<5,/* Treat only \n as a line separator (otherwise, all standard line separators are used). */NSRegularExpressionUseUnicodeWordBoundaries =1<<6/* Use Unicode TR#29 to specify word boundaries (otherwise, traditional regular expression word boundaries are used). */};
其实上面的注释已经说的很清楚了。
typedefNS_OPTIONS(NSUInteger, NSRegularExpressionOptions){ NSRegularExpressionCaseInsensitive =1<<0,/* 匹配是不区分大小写 */NSRegularExpressionAllowCommentsAndWhitespace =1<<1,/* 忽略空白和注释 */NSRegularExpressionIgnoreMetacharacters =1<<2,/* 将所有的patter当作普通字符串,例如:$\[]()+*^.|*/NSRegularExpressionDotMatchesLineSeparators =1<<3,/* .匹配换行和空格 */NSRegularExpressionAnchorsMatchLines =1<<4,/* $,^匹配每一行的开头和结尾 */NSRegularExpressionUseUnixLineSeparators =1<<5,/* 行分隔符只有\n(否则,所有标准的行分隔符时,例如\r也是换行) */NSRegularExpressionUseUnicodeWordBoundaries =1<<6/* 使用 Unicode TR#29 规定的边界。(否则,使用传统的正则表达式的词边界) */};
Regular Expression Metacharacters(正则表达式元字符)
元字符表如下:
字符表达式描述
\a匹配Bell(响铃),\u0007,ASCII表第七位
\A匹配文本开头,和^不同的是不能匹配换行的开头。^在NSRegularExpressionAnchorsMatchLines条件下可以匹配每一行的开头,同样$也是一样
\b匹配词的前面后面,或者后面,例如:匹配字符串为这是一个test代码.,正则为t\b或者\bt,前者匹配t结尾,后者匹配t开头,你可以理解为\b表示边界。分隔符可以是特殊字符或者中文。
\B文档原话是这样的:Match if the current position is not a word boundary.,意思应该是:如果当前位置不是边界则匹配。很容易让人理解为\B修饰的字符串不在边界,然而实际情况并不是这样。可以把\B理解为非边界,例如待匹配字符串有:test、tes、est,我们需要匹配的es在不在头部也不再尾部的字符串,相对应的正则应该是:\Bes\B,如果正则是\Bes则会匹配到:test和tes,正则是es\B则会匹配到:test和est
\cX匹配控制字符,例如\cM匹配control-M或者回车,点击查看更多的控制字符
\d匹配10进制数,0~9
\D匹配非10进制数
\E配合\Q使用,\Q开头\E结尾,中间的字符串都会被当作普通字符串。例如:\Q$\E等价于\$
\e匹配空格, \u001B
\f匹配\u000F
\n匹配\u000A
\G连续匹配,从当前位置开始匹配一直匹配到第一个不匹配的地方结束,例如待匹配字符串test1test,正则[a-z]可以匹配8个字符,如果正则是\Gtest则能匹配4个,如果待匹配字符串为:1test1test,正则:\Gtest将匹配不到任何字符串,正则:1\Gtest能匹配到4个字符
\N{UNICODE CHARACTER NAME}Match the named character.(具体作用和使用方式不明。。。。)
\p{UNICODE PROPERTY NAME}匹配指定的UNICODE属性名的字符,例如:属性名:Lu代表的是大写字母,待匹配字符串:Test,正则:\p{Lu},匹配结果是T。更多属性名
\P{UNICODE PROPERTY NAME}匹配非指定UNICODE属性名的字符,例如:属性名:Lu代表的是大写字母,待匹配字符串:Test,正则:\P{Lu},匹配结果是e,s,t。更多属性名
\Q配合\E使用,\Q开头\E结尾,中间的字符串都会被当作普通字符串。例如:\Q$\E等价于\$
\r换号符,\u000D.
\t制表符, \u0009
\s空白字符串,[\t\n\f\r\p{Z}]
\S非空白字符串, [^\t\n\f\r\p{Z}]
\uhhhh16进制直为hhhh的字符串
\uhhhhhhhh16进制直为hhhhhhhh的字符串
\w非字符
\W字符
\x{hhhh}16进制值为hhhh的字符
\xhh16进制值为hh的字符
\X官方文档解释:Match a Grapheme Cluster.
\Z结束输入,文档原文:Match if the current position is at the end of input, but before the final line terminator, if one exists.。
\z结束输入,文档原文:Match if the current position is at the end of input.
\n换行符,\u000A
\0ooo8进制值为ooo的字符串
[pattern]pattern表示要匹配的字符串,例如[A]表示匹配A
.任意字符
^开头
$结尾
\转义,例如匹配$,正则:\$
上表是笔者对文档中的元字符理解,有几个笔者也不清楚例如:\N{UNICODE CHARACTER NAME}怎么使用和具备什么作用。
正则表达式操作符
操作符表:
操作符描述
丨丨或,例如A丨B,匹配A或B
*0次或者多次
+1次或者多次
?0次或1次
{n}n为数字,表示前面匹配连续出现n次,例如:3{2}表示33
{n,}n为数字,表示前面匹配至少连续出现n次,例如:3{2,},表示:33,333,3333...}
{n,m}n,m为数字,n<=m,表示前面匹配连续出现n~m次,例如:3{2,5},表示:33,333,3333,33333
*?0次或者多次,但是次数尽可能少,例如正则:[\d]*?,待匹配字符:12345,匹配次数6次,都是空
+?1次或者多次,但是次数尽可能少,例如正则:[\d]+?,待匹配字符:12345,匹配次数5次,匹配结果是:1,2,3,4,5。
??0次或1次,例如正则:[\d]??,待匹配字符:12345,匹配次数6次,都是空
{n}?同{n}
{n,}?n为数字,但是次数尽可能少。例如正则:[\d]{1,}?,待匹配字符:12345,匹配次数5次,匹配结果是:1,2,3,4,5。
{n,m}?同{n,m},但是次数尽可能少。例如正则:[\d]{1,5}?,待匹配字符:12345,匹配次数5次,匹配结果是:1,2,3,4,5。
*+0次或者多次,但是次数尽可能多(贪婪模式)。例如正则:[\d]*+,待匹配字符:12345,匹配次数2次,匹配结果是:12345和空。
++1次或者多次,但是次数尽可能多(贪婪模式)。例如正则:[\d]*+,待匹配字符:12345,匹配次数1次,匹配结果是:12345。
?+0次或1次,但是次数尽可能多(贪婪模式)。例如正则:[\d]*+,待匹配字符:12345,匹配次数5次,匹配结果是:1,2,3,4,5。
{n}+同{n}
{n,}+同{n,},但是次数尽可能多(贪婪模式)。例如正则:[\d]{1,}+,待匹配字符:12345,匹配次数1次,匹配结果是:12345。
{n,m}+同{n,m},但是次数尽可能多(贪婪模式)。例如正则:[\d]{1,5}+,待匹配字符:12345,匹配次数1次,匹配结果是:12345。
(...)子表达式,并捕获匹配字符(捕获和不捕获的区别在后面会介绍),例如:待匹配字符串:``
(?:...)匹配子表达式...,但是不捕获字符(捕获和不捕获的区别在后面会介绍)
(?>...)贪婪子表达式,不捕获,例如正则:(?>[\d]{1,}),待匹配字符:12345,匹配次数1次,匹配结果是:12345。
(?# ... )注释
(?= ... )零宽度正预测先行断言。匹配...字符前面位置,注意是位置不是字符,所以宽度为0。例如正则:(?=\d),待匹配字符串:abc123,匹配的位置是下面字符的前面:1,2,3
(?! ... )零宽度负预测先行断言。匹配后面不是跟着...字符的位置,注意是位置不是字符,所以宽度为0。例如正则:(?=\d),待匹配字符串:abc123,匹配的位置是下面字符的后面:a,b,c,iOS匹配时会自动匹配多一个终止符。
(?<= ... )零宽度正回顾后发断言。匹配...字符后面位置,注意是位置不是字符,所以宽度为0。例如正则:(?=\d),待匹配字符串:abc123,匹配的位置是下面字符的后面:1,2,3
(?
(?ismwx-ismwx: ... )设置子正则...匹配的flag option。例如:待匹配字符串:test,正则:(?i:TEST),匹配结果:test。正则:(?-i:TEST),匹配不到。用代码测试时option不要选NSRegularExpressionCaseInsensitive 。
(?ismwx-ismwx)设置之后位置正则匹配的flag option。例如:待匹配字符串:test,正则:(?i)TEST,匹配结果:test。正则:(?-i)TEST,匹配不到,正则:T(?i)EST,匹配不到,因为前面的T是区分大小写的。用代码测试时option不要选NSRegularExpressionCaseInsensitive 。
工具
测试工具多中多样,可以直接在网上搜索‘正则表达式在线检测’就会出现一堆(如图:正则表达式在线检测.png)
正则表达式在线检测.png
也可以自己写代码进行验证但是比较麻烦每次都要run,为了方便于是笔者就自己写了个简单的测试用具如果需要的可以点击iOS正则表达式检测工具进行下载。如图:
iOS正则表达式检测工具.png
目前没有做字符串替换功能。
捕获和不捕获的区别
捕获
捕获会将当前的匹配到的字符储存起来,在正则表达式中可以用\n进行使用,n是10进制数,第n个捕获就写\n,在替换模式中可以用$n,表示第n个捕获的字符串。
不捕获
不捕获则不会储存匹配到的字符串
例子:
//待匹配字符串:hello//正则:(\w)\1//匹配结果:ll//代码如下:NSString*string = [NSStringstringWithFormat:@"hello"];NSError*error =NULL;NSString*regexString =@"(\\w)\1";NSRegularExpression*regex = [NSRegularExpressionregularExpressionWithPattern:regexString options:NSRegularExpressionUseUnicodeWordBoundarieserror:&error];NSUIntegerinteger = [regex numberOfMatchesInString:string options:NSMatchingReportProgressrange:NSMakeRange(0, string.length)];NSArray*matches = [regex matchesInString:string options:0range:NSMakeRange(0, [string length])];for(NSTextCheckingResult* resultinmatches) {NSRangematchRange = [result range];NSString* subString = [string substringWithRange:matchRange];NSLog(@"%@",subString);}
//待匹配字符串:hello//正则:(he)((\w)\3)(o)//替换模式:$1-$2-$3-$4//替换结果:he-ll-l-o//捕获顺序说明:根据"("的顺序//代码如下:NSString*string = [NSStringstringWithFormat:@"hello"];NSError*error =NULL;NSString*regexString =@"(he)((\\w)\\3)(o)";NSRegularExpression*regex = [NSRegularExpressionregularExpressionWithPattern:regexString options:NSRegularExpressionUseUnicodeWordBoundarieserror:&error];NSLog(@"%li",regex.numberOfCaptureGroups);NSLog(@"%@",[regex stringByReplacingMatchesInString:string options:NSMatchingReportProgressrange:NSMakeRange(0, string.length) withTemplate:@"$1-$2-$3-$4"]);
Example
1.国内手机号码
国内手机号码都是11位,开头大概有:
中国移动:134、135、136、137、138、139、150、151、152、157(TD)、158、159、182、183、184、187、178、188、147、1705(虚拟运营商移动号段)
中国联通:130、131、132、145(数据卡号段)155、156、176、185、186、1709(虚拟运营商联通号段)
中国电信:133、153、177、180、181、189、134、1700(虚拟运营商电信号段)
参考百度知道
正则如下:
(134|135|136|137|138|139|150|151|152|157|158|159|182|183|184|187|178|188|147|1705|130|131|132|145|155|156|176|185|186|1709|133|153|177|180|181|189|134|1700)\d{8}
分析:
“(134|135|136|137|138|139|150|151|152|157|158|159|182|183|184|187|178|188|147|1705|130|131|132|145|155|156|176|185|186|1709|133|153|177|180|181|189|134|1700)“匹配的号码开头。“\d{8}“匹配后面8个必须是数字
经测试匹配成功。
2.中文
正则表达式:[\u4E00-\u9FA5]
分析:中文编码和部分日文编码在\u4E00-\u9FA5范围
3.邮箱
A@B.C
规则如下:
A可以为数字和字母,且长度至少为1
B可以为数字和字母,且长度至少为1
C为字母或者中文,长度至少为2。(域名后缀长度为1的没见过)
正则表达式:\b(?i)\w+@\w+\.[A-Z\u4E00-\u9FA5]{2,}\b
分析:
“\b“是边界,表示邮箱前后都是空格或者其他无内容字符“(?i)“表示后面的字母都是不区分大小写的“\w+“非字符,长度至少为1“.[A-Z\u4E00-\u9FA5]{2,}“字母或者中文,长度至少为2
4.国内电话
国内电话的格式是:区号-号码
区号最短3位,最长4。号码最短5位,最长8位
正则:[\d]{3,4}-[d]{5,8}
分析:
“[\d]{3,4}“ 数字,3到4位“-“分割符-“[d]{5,8}“ 数字,5到8位
5.用户名,英文开头,允许输入数字和英文
正则:^[A-Za-z][A-Za-z0-9]*
分析:
“^[A-Za-z]“字母开头“[A-Za-z0-9]*“可以输入数字和英文
6.QQ号
QQ还最短是5位。
正则:[1-9]\d{4,}
分析:
“[1-9]“开头是不为0的数字“\d{4,}“至少有4个数字
7.身份证
第二代居民身份证居民身份证编码规则如下:
(1)前1、2位数字表示:所在省(直辖市、自治区)的代码;
(2)第3、4位数字表示:所在地级市(自治州)的代码;
(3)第5、6位数字表示:所在区(县、自治县、县级市)的代码;
(4)第7—14位数字表示:出生年、月、日;
(5)第15、16位数字表示:所在地的派出所的代码;
(6)第17位数字表示性别:奇数表示男性(1、3、5、7、9),偶数表示女性(0、2、4、6、8);
(7)第18位数字是校检码:也有的说是个人信息码,不是随计算机的随机产生,它是 用来检验身份证的正确性。校检码可以是0—9的数字,有时也用x表示。作为尾号的校验码,是由号码编制单位按统一的公式计算出来的,如果某人的尾号是0-9,都不会出现X,但如果尾号是10,那么就得用X来代替,因为如果用10做尾号,那么此人的身份证就变成了19位。X是罗马数字的10,用X来代替10,可以保证公民的身份证符合国家标准。
注意:这里最大能输入2019年。
正则:[1-6][0-7][\d]{4}((19[\d]{2})|(20[0-1][\d]))((0[1-9])|(1[0-2]))((0[1-9])|([1-2]\d)|(3[0-1]))[\d]{3}[\dx]
分析:
“[1-6][0-7]“根据省(直辖市、自治区)的代码第一位是1-6,第二位是0-7“[\d]{4}“地级市、所在区编码都是数字“((19[\d]{2})|(20[0-1][\d]))((0[1-9])|(1[0-2]))((0[1-9])|([1-2]\d)|(3[0-1]))“出生日期,最早的是1900/01/01,最晚是2019/12/31“[\d]{3}[\dx]“ 四个数字或者3个数字+x
8.邮政编码
国内邮政编码为6位数字
正则\d{6}
分析:都是数字
9.网址
正则为:(?i)\b(http://|https://)?([www.]?)[\w\.\-]+\.[A-Z\u4E00-\u9FA5]{2,}(\:[0-9]+)*(/($|[a-zA-Z0-9\.\,\;\?\'\\+&%\$#\=~_\-]+))*
能匹配:
http://jianshu.com:8080/http://jianshu.com/?a=asdsa&b=asdashttp://简书.com/
不能匹配:
ftp://jianshu.comftps://jianshu.com
分析:
"(?i)"不区分大小写"\b"边界"(http://|https://)?"http://或者https://开头也可以没有"([www.]?)"www. 可有可无"[\w\.\-]+"可以出现数字、英文、英文句号、-、中文,至少一个"\.":英文句号."[A-Z\u4E00-\u9FA5]{2,}"英文,中文至少两个"(\:[0-9]+)*"\、:、数字可有可无"(/($|[a-zA-Z0-9\.\,\;\?\'\\\+&;%\$#\=~_\-]+))*"斜杠/后面可以出现的字符
10.出生日期
格式:yyyy/MM/dd
注意:这里最大能输入2019年。
正则:((19[\d]{2})|(20[0-1][\d]))/((0[1-9])|(1[0-2]))/((0[1-9])|([1-2]\d)|(3[0-1]))
分析:
"(19[\d]{2})|(20[0-1][\d])"出生年份1900~2019"/"分隔符/"((0[1-9])|(1[0-2]))"出生月份01-12"/"分隔符/"((0[1-9])|([1-2]\d)|(3[0-1]))"出生日期1-31
11.时间
24小时制,时间格式:hh:mm:ss
正则:(([0-1]\d)|(2[0-3])):[0-5]\d:[0-5]\d
分析:
"(([0-1]\d)|(2[0-3]))"00到23小时[0-5]\d""00到59
12.图片文件名
正则:\w+\.(?i:png|jpg|jpeg|gif)
分析:
"[\w]+"图片文件名不可以为字符"(?i:png|jpg|jpeg|gif)"后缀名为png、jpg、jpeg、gif中的一种,不区分大小写。
转载 http://www.jianshu.com/p/4eb7e7971146
网友评论