正则与Xcode和iOS,以及子模式
首先Xcode里可以使用正则进行搜索,不管是单文件的Command+F
还是全局的搜索。用正则可以搜索一些特殊的,比如“123....abc”这种格式,就是你知道开头和结尾,但是不确定中间的,就可以用正则123.*abc
搜索出来,而淡出的文字搜索只能搜连续的文字。
然后Xcode里不仅可以用正则搜索还可以替换,这才是福音啊!
比如有一个字符串@"确定",很多地方都用到了,现在需要把它改为本地化的模式,即NSLocalizedString(@"确定", nil);
。而且只有在方法initWithName:@"确定" font:xxx
里用到的才修改。
当然可以一个地方一个地方的修改,但是用Xcode的全局替换+正则子模式可以一建搞定:
在全局搜索里选择:Replace - Regular Expression,搜索正则initWithName:(@"确定") font
,替换结果为initWithName:NSLocalizedString($1,nil) font
。
在正则里使用了一对小括号,就是子模式,就是在匹配正则的时候不仅匹配整个整体,还会匹配小括号内部的内容,然后你通过$1
就可以得到第一个子模式匹配的内容。在这个例子里,小括号内部正好是@"确定"
,那么$1
就等于@"确定"
,在替换后的结果里,$1
会使用真正的值来替换。
如果你有多个小括号,那么从前往后可以分别用$+序号来得到它们。
比如对于initWithName:@"iOS开发" departmentName:@"一号标题"
,你要把这方法里的两个字符串都替换成本地化宏的形式,而且你不知道这两个字符串具体的内容。
那么搜索正则为:initWithName:(@".*") departmentName:(@".*")
,替换结果为:initWithName:NSLocalizedString($1,nil) departmentName:NSLocalizedString($2,nil)
最后结果为:initWithName:NSLocalizedString(@"iOS开发",nil) departmentName:NSLocalizedString(@"一号标题",nil)
使用子模式最厉害的地方在于它可以把原字符串的某一部分复制过来,而却不需要知道它们具体是什么值。比如上面的情况,两个字符串可以是任何值,修改完后它还是原来的值。用纯文本替换是完全不可能达到这个效果的,因为它要求你确定明确的写下替换后的值。
在iOS开发里也是可以用子模式的,比如处理一段字符串,你要把里面的手机号提取出来。然后据你观察,手机号都是这种形式:"xx手机号:15812345678",那么正则可以写成:手机号:[1-9]{11}
。
匹配出来很简单,但问题是匹配得到的不是手机号的值,而是“手机号:xxx”这一整串。可以选择:
- 得到整串后再用subString等方法截取
- 使用子模式匹配
第一种如果你要得到的内容位置不是固定的就比较难办,比如"手机号:"和手机号之间也有不确定的空格。
iOS里怎么使用子模式?
正则使用NSRegularExpression
这个类,使用matchesInString: options: range:
等方法后,一般可以得到一个NSTextCheckingResult
的数组。
每一个NSTextCheckingResult
代表一个正则匹配,然后进入这个类,可以看到有两个方法:
/* A result must have at least one range, but may optionally have more (for example, to represent regular expression capture groups). The range at index 0 always matches the range property. Additional ranges, if any, will have indexes from 1 to numberOfRanges-1. */
@property (readonly) NSUInteger numberOfRanges NS_AVAILABLE(10_7, 4_0);
- (NSRange)rangeAtIndex:(NSUInteger)idx NS_AVAILABLE(10_7, 4_0);
这里就是获取子模式匹配结果的方法,numberOfRanges
标识有几个range,第一个range是整个正则匹配的范围,之后的1,到numberOfRanges-1表示子模式匹配到的range。使用rangeAtIndex
就可以得到子模式的range。
回到上面手机号的问题,正则就该写为手机号[ ]*:[ ]*([1-9]{11})
,这样手机号: 15811329743
和@"手机号 : 14729749274"
都可以匹配到。
然后取子模式的range:
for (NSTextCheckingResult * match in matches) {
if([match numberOfRanges] > 1){
NSString *phone = [str substringWithRange:[match rangeAtIndex:1]];
}
}
这样就可以应对各种奇怪的变化而直接拿到需要的值!