iOS中的正则表达式

2,058 阅读6分钟
原文链接: www.jianshu.com

正则表达式也用过一段时间了,不同的语言中用起来并不相同,这里姑且做为iOS正则的一个总结贴。

[TOC]

iOS中的正则表达式

正则语法

1. 基本

我把同类正则符号做了分组列举,以/作为分隔

元字符 描述
\ 转义符 如\\n为\n,单\n为换行符
^ / $ 行首与行尾
? / * / + / {n} / {m,n} 零次或一次/>=0次/>=1次/n次/m-n次,mn可省略一端限制
? 跟在(*,+,?,{n},{n,},{n,m})后面时,为非贪婪模式。如,对于“oooo”,o+得到oooo,o+?得到o
x|y 匹配x或y
&& 字符组与运算,有些语言支持,比如&&+[] Java中就支持,但JavaScript不支持。验证iOS中支持。
[xyz1-9] 匹配xyz或者1-9数字其中一个
[^xyz] 非xyz
\b / \B 单词/非单词的边界,可放前后。如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”
\d / \D 数字/非数字
\s / \S 不可见/可见字符。\s不可见,等价于[ \f\n\r\t\v]
\w / \W \w包括下划线的字符,包括字母/数字/中文/下划线,不包括emoj。
. 匹配任何单个字符(注意不包括换行符)

1.1 &&+[]运算

对于&&+[]运算符,原话是这样描述的:

有些语言支持,比如&&+[] Java中就支持,但JavaScript不支持。
示例7:匹配英文字母中除去元音字符的字符
[[a-z]&&[^aeiou]] // a-z 且排除掉5个元音字符aeiou

我的验证,iOS中是支持的。

NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", @"[[a-z ]&&[^acdkn]]+"];
NSLog(@"测试&&:%d", [predicate1 evaluateWithObject:@"better boy"]);
NSLog(@"测试&&:%d", [predicate1 evaluateWithObject:@"better boy run"]);

NSPredicate *predicate11 = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", @"[1234567890&&[^6]]+"];
NSLog(@"测试&&:%d", [predicate11 evaluateWithObject:@"1234567890"]);
NSLog(@"测试&&:%d", [predicate11 evaluateWithObject:@"123457890"]);
/*打印结果:
2018-10-11 14:15:03.206346+0800 WEEX_TEST[96757:22569846] 测试&&:1
2018-10-11 14:15:03.206425+0800 WEEX_TEST[96757:22569846] 测试&&:0
2018-10-11 14:15:03.206534+0800 WEEX_TEST[96757:22569846] 测试&&:0
2018-10-11 14:15:03.206600+0800 WEEX_TEST[96757:22569846] 测试&&:1
*/

2. 进阶

元字符 描述
\f/\n/\r/\t/\v 换页符/换行符/回车符/制表符/垂直制表符
\xn 十六进制转义值,可表示ASCII编码。如:“\x41”匹配“A”。
\un 四个十六进制数字表示的Unicode字符。如,\u00A9
\n 标识一个八进制转义值或引用。注:引用优先于转义,如前面不足n个表达式,才转义生效。
\num 对所获取的匹配的引用。例如,"78aaa",用@"(.)\\1",得到"aa"。🤣😌456678aa87aaaas,用@"(\d)(\d)(\w)\3\2\1",得到"78aa87"。注:引用优先于转义,如前面不足n个表达式,才转义生效。

3. 非获取匹配

非获取匹配,匹配pattern但不获取匹配结果.

元字符 描述
?:pattern 例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。
?=pattern 正向肯定预查.
?<=pattern 反向肯定预查.
?!pattern 正向否定预查.
?<!pattern 反向否定预查.

其中,包含了4个概念:

  • 正向 --> 在查找目标右侧
  • 反向 --> 在查找目标左侧
  • 肯定 --> 是pattern
  • 否定 --> 不是pattern

正常读起来就是:

  • 正向肯定预查:在目标右侧是pattern
  • 反向肯定预查:在目标左侧是pattern
  • 正向否定预查:在目标右侧不是pattern
  • 反向否定预查:在目标左侧不是pattern

<span id = "jump_find_emoji_code"></span>
来个例子,查找中括号内的emoji表情,但不要中括号

//用到了:正向肯定预查?=与反向肯定预查?<=
NSString *sourceStr3 = @"我是一个[笑脸],[强],你是一个[自信]";
NSString *pattern = @"(?<=\\[).*?(?=\\])";
NSRegularExpression *regex3 = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil];
NSArray<NSTextCheckingResult *> *results3 = [regex3 matchesInString:sourceStr3 options:NSMatchingReportProgress range:NSMakeRange(0, sourceStr3.length)];
for (NSTextCheckingResult *result in results3) {
    NSLog(@"中括号内:%@", [sourceStr3 substringWithRange:result.range]);
}
/*打印结果:
2018-10-10 16:08:35.145447+0800 WEEX_TEST[15000:20387096] 中括号内:笑脸
2018-10-10 16:08:35.145550+0800 WEEX_TEST[15000:20387096] 中括号内:强
2018-10-10 16:08:35.145652+0800 WEEX_TEST[15000:20387096] 中括号内:自信
*/

分解:

(?<=\\[)    //目标左侧是[
.*          //查找任意数量的任意字符,也是我们想查询的目标
?           //非贪婪。如果去掉?,会得到:笑脸],[强],你是一个[自信
(?=\\])     //目标右侧是]

升级一下:如果需求emoji表情为两个字,如何丢弃一个字的emoji呢?如果有些干扰的或者一半的中括号,如何规避掉呢?

NSString *sourceStr3 = @"我是一个[笑脸],[[强]],][][[∑®†]你是一个[自信]";
NSString *pattern = @"(?<=\\[)([^\\[\\]]{2,}?)(?=\\])";
NSRegularExpression *regex3 = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil];
NSArray<NSTextCheckingResult *> *results3 = [regex3 matchesInString:sourceStr3 options:NSMatchingReportProgress range:NSMakeRange(0, sourceStr3.length)];
for (NSTextCheckingResult *result in results3) {
    NSLog(@"中括号内:%@", [sourceStr3 substringWithRange:result.range]);
}
/*打印结果:
2018-10-10 16:52:30.027836+0800 WEEX_TEST[27737:20549839] 中括号内:笑脸
2018-10-10 16:52:30.027931+0800 WEEX_TEST[27737:20549839] 中括号内:∑®†
2018-10-10 16:52:30.027984+0800 WEEX_TEST[27737:20549839] 中括号内:自信
*/

用到了[^]符号,去否定[];用到了{2,},去匹配两位以上。其实{2,}后面的那个非贪婪?也可以去掉的,因为碰到[]就匹配结束了,不会出现贪婪∑®†]你是一个[自信进来的情况。

iOS中正则的三种表现形式

  1. [string rangeOfString: options:]查找字符串范围,返回第一个匹配成功的range
  2. NSPredicate谓词匹配数组与字符串。
  3. NSRegularExpression匹配。

1. [string rangeOfString: options:]

例查找第一个出现的数字:

    NSString *sourceStr2 = @"123元45678秒09分";
    NSString *regex2 = @"(?<=“).*(?=”)";
    NSRange range2 = [sourceStr2 rangeOfString:regex2 options:NSRegularExpressionSearch];
    if (range2.length > 0) {
        NSLog(@"%@", [sourceStr2 substringWithRange:range2]);
    }
    //打印:123

1.1 出现的option参数

typedef NS_OPTIONS(NSUInteger, NSStringCompareOptions) {
    NSCaseInsensitiveSearch = 1,  //不区分大小写比较
    NSLiteralSearch = 2,       //逐字节比较 区分大小写
    NSBackwardsSearch = 4,     //从字符串末尾开始搜索
    NSAnchoredSearch = 8,      //搜索限制范围的字符串
    NSNumericSearch = 64,     //按照字符串里的数字为依据,算出顺序。例如 Foo2.txt < Foo7.txt < Foo25.txt
    NSDiacriticInsensitiveSearch    NS_ENUM_AVAILABLE(10_5, 2_0) = 128,//忽略 "-" 符号的比较
    NSWidthInsensitiveSearch    NS_ENUM_AVAILABLE(10_5, 2_0) = 256,//忽略字符串的长度,比较出结果
    NSForcedOrderingSearch  NS_ENUM_AVAILABLE(10_5, 2_0) = 512,//忽略不区分大小写比较的选项,并强制返回 NSOrderedAscending 或者 NSOrderedDescending
    NSRegularExpressionSearch   NS_ENUM_AVAILABLE(10_7, 3_2) = 1024    /*只能应用于 rangeOfString:..., stringByReplacingOccurrencesOfString:...和 replaceOccurrencesOfString:... 方法。
    使用通用兼容的比较方法,如果设置此项,可以去掉 NSCaseInsensitiveSearch 和 NSAnchoredSearch*/
};

2. NSPredicate

==NSPredicate下,例子引自: sunny_zl==

其中,只有MATCHES时为正则匹配,这儿也把NSPredicate用法罗列下。

2.1 比较运算符

包括:

1. >=
2. <=
3. >
4. <
5. !=
6. ==
7. BETWEEN {下限,上限} //大于或等于下限

例:

NSNumber *testNumber = @123;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF = 123"];
if ([predicate evaluateWithObject:testNumber]) {
    NSLog(@"testString:%@", testNumber);
}
//结果:testString:123

2.2 逻辑运算符

包括:

1. AND &&
2. OR ||
3. NOT !

例:

NSArray *testArray = @[@1, @2, @3, @4, @5, @6];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF > 2 && SELF < 5"];
NSArray *filterArray = [testArray filteredArrayUsingPredicate:predicate];
NSLog(@"filterArray:%@", filterArray);
//结果为:(3, 4)

2.3 字符串比较运算符

包括:

1. BEGINSWITH   //以指定的字符串开头
2. ENDSWITH     //以指定的字符串结尾
3. CONTAINS     //包含指定的字符串
4. LIKE         //是否匹配指定的字符串模板。?代表一个字符和*代表任意多个字符。如:name LIKE '*ac*'中间包含ac;name LIKE '?ac*'匹配第2个位置开始为ac
5. MATCHES      //匹配指定的正则表达式

注:字符串比较都是区分大小写和重音符号的。
如:cafécafe是不一样的,Cafecafe也是不一样的。

cd符号:[c]是不区分大小写,[d]是不区分重音符号。如:name LIKE[cd] 'cafe'

如:

//简单判断是否手机号
- (BOOL)checkPhoneNumber:(NSString *)phoneNumber {
    NSString *regex = @"^[1][3-8]\\d{9}$";
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
    return [pred evaluateWithObject:phoneNumber];
}

2.4 集合运算符

包括:

1. ANY、SOME:集合中任意一个元素满足条件,就返回YES。
2. ALL:集合中所有元素都满足条件,才返回YES。
3. NONE:集合中没有任何元素满足条件就返回YES。如:NONE person.age < 18
4. IN只有当左边表达式或值出现在右边的集合中才会返回YES。

如:

//查找不在filterArray中元素
NSArray *filterArray = @[@"ab", @"abc"];
NSArray *array = @[@"a", @"ab", @"abc", @"abcd"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)", filterArray];
NSLog(@"%@", [array filteredArrayUsingPredicate:predicate]);
//输出:( a, abcd )

2.5 谓词中使用占位符参数

包括:

%K:用于动态传入属性名
%@:用于动态设置属性值
$VALUE: 使用动态改变的属性值

如:

//定义一个property来存放属性名,定义一个value来存放值
NSString *property = @"name";
NSString *value = @"Jack";
//该谓词的作用是如果元素中property属性含有值value时就取出放入新的数组内,这里是name包含Jack
NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K CONTAINS %@", property, value];
NSArray *newArray = [array filteredArrayUsingPredicate:pred];
NSLog(@"newArray:%@", newArray);

//------------- 传递VALUE值 -------------
// 创建谓词,属性名改为age,要求这个age包含$VALUE字符串,子谓词传递value值
NSPredicate *predTemp = [NSPredicate predicateWithFormat:@"%K > $VALUE", @"age"];
// 指定$VALUE的值为 25
NSPredicate *pred1 = [predTemp predicateWithSubstitutionVariables:@{@"VALUE" : @25}];
NSArray *newArray1 = [array filteredArrayUsingPredicate:pred1];
NSLog(@"newArray1:%@", newArray1);

2.6 基本用法

//字符串内查找 查找数字
NSString *regex = @"\d";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
BOOL match = [predicate evaluateWithObject:sourceString];

//数字比较 判断是否100-200间
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF BETWEEN {100, 200}"];

//过滤数组内字符串 在filterArray中
NSArray *filterArray = @[@"ab", @"abc"];
NSArray *array = @[@"a", @"ab", @"abc", @"abcd"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF IN %@", filterArray];
NSArray *newArray = [array filteredArrayUsingPredicate:predicate];

//过滤数组内字典 查找name张姓的
NSArray *array = @[@{@"name" : @"zhangsan", @"age" : @"10"},
                   @{@"name" : @"lisi",     @"age" : @"11"},
                   @{@"name" : @"wangwu",   @"age" : @"12"}];
 
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name CONTAINS[c] 'zhang'"];
NSArray *results = [array filteredArrayUsingPredicate:predicate];

//过滤数组内对象,同数组内字典,不再举例

//对象的key比较 对象的age大于25
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age > 25"];

3. NSRegularExpression

3.1 查找匹配

1. 查找第一个匹配范围。 
- (NSRange)rangeOfFirstMatchInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range;

2. 查找第一个匹配结果。
- (nullable NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range;

3. 查找满足条件总次数。
- (NSUInteger)numberOfMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range;

4. 查找所有匹配结果。
- (NSArray<NSTextCheckingResult *> *)matchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range;

5. 查找并通过block遍历结果。
- (void)enumerateMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range usingBlock:(void (NS_NOESCAPE ^)(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL *stop))block;

还发现了一个方法:+ (NSString *)escapedPatternForString:(NSString *)string;,这家伙是用来做什么的呢?它是帮助我们添加转义符的!

如:

NSString *ttt = @"\\d{4,}\n";
NSLog(@"%@", ttt);
NSLog(@"%@", [NSRegularExpression escapedPatternForString:ttt]);
/*打印结果:
2018-10-10 17:25:16.870223+0800 WEEX_TEST[37135:20678832] \d{4,}
2018-10-10 17:25:16.870438+0800 WEEX_TEST[37135:20678832] \\d\{4,\}
*/

3.2 查找替换

1. 字符串替换,返回一个新串
- (NSString *)stringByReplacingMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range withTemplate:(NSString *)templ;

2. 可变字符串替换,返回操作次数
- (NSUInteger)replaceMatchesInString:(NSMutableString *)string options:(NSMatchingOptions)options range:(NSRange)range withTemplate:(NSString *)templ;

3. 自定义替换功能(没搞懂,用了后没发现什么作用)
- (NSString *)replacementStringForResult:(NSTextCheckingResult *)result inString:(NSString *)string offset:(NSInteger)offset template:(NSString *)templ;

4. 转换为正则表达式字符串
+ (NSString *)escapedTemplateForString:(NSString *)string;

例子1,查找并替换数字:

NSString *sourceStr = @"123元45678秒09分";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\d+" options:NSRegularExpressionCaseInsensitive error:nil];
NSArray<NSTextCheckingResult *> *results = [regex matchesInString:sourceStr options:NSMatchingReportProgress range:NSMakeRange(0, sourceStr.length)];
for (NSTextCheckingResult *result in results) {
    NSLog(@"%@", [sourceStr substringWithRange:result.range]);
}
    
NSString *replaced = [regex stringByReplacingMatchesInString:sourceStr options:NSMatchingReportProgress range:NSMakeRange(0, sourceStr.length) withTemplate:@"<<-->>"];
NSLog(@"replaced:%@", replaced);
/*打印结果:
2018-10-10 17:40:30.122470+0800 WEEX_TEST[41545:20749671] 123
2018-10-10 17:40:30.122601+0800 WEEX_TEST[41545:20749671] 45678
2018-10-10 17:40:30.122712+0800 WEEX_TEST[41545:20749671] 09
2018-10-10 17:40:30.122858+0800 WEEX_TEST[41545:20749671] replaced:<<-->>元<<-->>秒<<-->>分
*/

例子2,可变字符串查找并替换

NSMutableString *sourceStr2 = [@"123元45678秒09分" mutableCopy];
NSInteger replaceNum = [regex replaceMatchesInString:sourceStr2 options:NSMatchingReportProgress range:NSMakeRange(0, sourceStr.length) withTemplate:@"<<-->>"];
NSLog(@"replaceNum:%ld replaced:%@", replaceNum, sourceStr2);
/*打印结果:
2018-10-10 17:45:56.019120+0800 WEEX_TEST[43137:20772997] replaceNum:3 replaced:<<-->>元<<-->>秒<<-->>分
*/

3.3 出现的option参数

以下说明引自:roc_lei

NSRegularExpressionOptions:

typedef NS_OPTIONS(NSUInteger, NSRegularExpressionOptions) {
   NSRegularExpressionCaseInsensitive             = 1 << 0,     /*不区分字母大小写的模式*/
   NSRegularExpressionAllowCommentsAndWhitespace  = 1 << 1,     /*忽略掉正则表达式中的空格和#号之后的字符*/
   NSRegularExpressionIgnoreMetacharacters        = 1 << 2,     /*将正则表达式整体作为字符串处理*/
   NSRegularExpressionDotMatchesLineSeparators    = 1 << 3,     /*允许.匹配任何字符,包括换行符 */
   NSRegularExpressionAnchorsMatchLines           = 1 << 4,     /*允许^和$符号匹配行的开头和结尾*/
   NSRegularExpressionUseUnixLineSeparators       = 1 << 5,     /*设置\n为唯一的行分隔符*/
   NSRegularExpressionUseUnicodeWordBoundaries    = 1 << 6      /*使用Unicode TR#29标准作为词的边界,否则所有传统正则表达式的词边界都有效*/
};

NSMatchingOptions:

typedef NS_OPTIONS(NSUInteger, NSMatchingOptions) {
   NSMatchingReportProgress         = 1 << 0, //找到最长的匹配字符串后调用block回调
   NSMatchingReportCompletion       = 1 << 1, //找到任何一个匹配串后都回调一次block
   NSMatchingAnchored               = 1 << 2, //从匹配范围的开始处进行匹配
   NSMatchingWithTransparentBounds  = 1 << 3, //允许匹配的范围超出设置的范围
   NSMatchingWithoutAnchoringBounds = 1 << 4  //禁止^和$自动匹配行还是和结束
};

NSMatchingFlags:

typedef NS_OPTIONS(NSUInteger, NSMatchingFlags) {
   NSMatchingProgress               = 1 << 0, //匹配到最长串后被设置     
   NSMatchingCompleted              = 1 << 1, //全部分配完成后被设置    
   NSMatchingHitEnd                 = 1 << 2, //匹配到设置范围的末尾时被设置   
   NSMatchingRequiredEnd            = 1 << 3, //当前匹配到的字符串在匹配范围的末尾时被设置     
   NSMatchingInternalError          = 1 << 4  //由于错误导致的匹配失败时被设置   
};

iOS中正则使用实例

一般使用场景分为两种,全匹配与输入框内边输入边匹配。
实例这里,只演示简单正则使用规则,不做完全严格的匹配。

1. 全匹配

1.1 是否有效身份证号

//15或18位身份证:第一位为1-9,中间13或16位数字,最后一位为0-9xX
BOOL validateIdentityCard(NSString *identityCard){
    NSString *regex2 = @"^[1-9](\\d{13}|\\d{16})[0-9xX]$";
    NSPredicate *identityCardPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex2];
    return [identityCardPredicate evaluateWithObject:identityCard];
}

1.2 是否有效手机号

BOOL validatePhoneNumber(NSString *phone) {
    NSString *regex = @"^1[34578]\\d{9}$";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex];
    return [predicate evaluateWithObject:phone];
}

1.3 是否有效6-16位密码

//6-16位密码,只允许输入大小写与数字,并且大小写与数字至少每种一个
BOOL validatePWD(NSString *pwd) {
    NSString *regex = @"^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])[a-zA-Z0-9]{6,16}$";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex];
    return [predicate evaluateWithObject:pwd];
}

1.4 金额数字添加逗号分隔符

NSString *sourceText = @"1234567890";
NSString *pattern3 = @"\\B(?=(?:\\d{3})+(?!\\d))";
NSRegularExpression *regex3 = [NSRegularExpression regularExpressionWithPattern:pattern3 options:NSRegularExpressionCaseInsensitive error:nil];
NSArray<NSTextCheckingResult *> *results3 = [regex3 matchesInString:sourceText options:NSMatchingReportProgress range:NSMakeRange(0, sourceText.length)];
NSString *replaced = [regex3 stringByReplacingMatchesInString:sourceText options:NSMatchingReportProgress range:NSMakeRange(0, sourceText.length) withTemplate:@","];
NSLog(@"replaced:%@", replaced);
/*打印结果:
2018-10-11 10:52:32.896514+0800 WEEX_TEST[38772:22081217] replaced:1,234,567,890
*/

1.5 查找中括号内emoji表情

见<a href="#jump_find_emoji_code" target="_self">非获取匹配中的查找emoji</a>

1.6 限最多8位金额,最多两位小数,第一位不能为.

//控制8位金额,最多两位小数,只能输入数字与.,第一位不能为.或+-
NSString *toString = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSString *stringRegex = @"(([0]|(0[.]\\d{0,2}))|([1-9]\\d{0,7}(([.]\\d{0,2})?)))?";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", stringRegex];
if ([predicate evaluateWithObject:toString]) {
    return YES;
}
return NO;

2. 边输入边匹配

边输入边匹配的场景还是挺多的,比如限制输入框输入手机号码,输入金额,输入密码,或输入身份证号。

它与全匹配不同,需要在输入过程中,边输入边验证。当检测到不匹配时,拒绝用户输入,所以用:-textField:shouldChangeCharactersInRange:replacementString:。还要支持回车退格等。注意:正则检测的是替换后的string

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    //过滤字符
    if ([string isEqualToString:@""] ||    //按退格可以改变
        [string isEqualToString:@"\n"]) {  //按回车可以改变
        return YES;
    }
    //因为要匹配的是输入后字符,所以需要替换操作toString
    NSString *toString = [textField.text stringByReplacingCharactersInRange:range withString:string];
    //正则检测
    NSString *stringRegex = @"(([0]|(0[.]\\d{0,2}))|([1-9]\\d{0,7}(([.]\\d{0,2})?)))?";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", stringRegex];
    if ([predicate evaluateWithObject:toString]) {
        return YES;
    }
    return NO;
}