通过判断手机号来分析NSPredicate谓词逻辑

先来一段代码,之前项目中用到,涉及到了NSPredicate和正则表达

//判断手机号是否正确
+(BOOL) isValidateMobile:(NSString *)mobile
{
    //手机号以13, 15,18 16 17开头,八个 \d 数字字符
    NSString *phoneRegex = @"^((13[0-9])|(15[^4,\\D])|(16[0-9])|(17[0-9])|(18[0,0-9]))\\d{8}$";
    NSPredicate *phoneTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",phoneRegex];
    return [phoneTest evaluateWithObject:mobile];
    
}

这是用来判断手机号码的真假,再来段判断银行卡号的:

//判断16-19位的银行卡号
+ (BOOL)judgeBankCardNumber:(NSString*)str
{
    //16到19位数以6开头的银联卡判断
//    NSString * pattern = @"";
//    NSRegularExpression * reglx = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:nil];
//    NSArray * array = [reglx]
    NSString * cardNumRegex = @"^[6]\\d{3}\\s*\\d{4}\\s*\\d{4}\\s*\\d{4}$|^[6]\\d{3}\\s*\\d{4}\\s*\\d{4}\\s*\\d{4}\\s*\\d{1,3}$";
    NSPredicate *numTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",cardNumRegex];
    return [numTest evaluateWithObject:str];
    
}
我们会发现,通过使用了NSPredicate,代码极大的简化了。下面来分析下,什么是NSPredicate。

iOS Predicate 即谓词逻辑。和数据库的SQL语句具有相似性,都是从数据堆中根据条件进行筛选。

它的使用主要集中在两个方法中:
NSArray

- (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate;

NSMutableArray

- (void)filterUsingPredicate:(NSPredicate *)predicate;

还有NSSet和NSMutableSet也可以用这个类筛选。

筛选用法
利用成员实例方法筛选出长度大于3的字符串

NSArray *array = @[@"jim", @"cook", @"jobs", @"sdevm"];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"length > 3"];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);

打印

( cook, jobs, sdevm)

lenght
就是对数组成员执行[xxxx lenght]然后判断返回的NSUInteger值是否大于3。扩展到NSString其他方法比如integerValue

NSArray *array = @[@"2", @"3", @"4", @"5"];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"integerValue >= %@", @3];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);

如果我不想用任何实例方法,想筛选成员本身应该怎么做呢。这时候就可以用self来代替

NSPredicate *pre = [NSPredicate predicateWithFormat:@"self CONTAINS %@", @3];

CONTAINS用法后面会讲到

再扩展到模型

Test.h

@interface Test : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSNumber *code;

@end

Test *test1 = [[Test alloc]init];
test1.name = @"西湖";
test1.code = @1;

Test *test2 = [[Test alloc]init];
test2.name = @"西溪湿地";
test2.code = @2;

Test *test3 = [[Test alloc]init];
test3.name = @"灵隐寺";
test3.code = @3;

NSArray *array = @[test1, test2, test3];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"code >= %@", @2];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);

筛选出数组成员[test code]方法(code属性的get方法)返回值>=2的成员。这里的比较运算符同样也可以使用==、!=、<=、<。

其实==不仅可以用来比较NSNumber对象,还可以用来判断NSString对象是否相同。

NSPredicate *pre = [NSPredicate predicateWithFormat:@"name == %@", @"西湖"];

筛选出name是"西湖"的对象数组。

NSString对象的操作

前面提到==比较运算符可以起到- (BOOL)isEqualToString:(NSString *)aString;方法的效果,来判断字符串是否相同。那么字符串中包含某个字符串应该如何判断呢,在NSPredicate中可以用CONTAINS(大小写都可以)来表示包含关系。

NSPredicate *pre = [NSPredicate predicateWithFormat:@"name CONTAINS %@", @"湖"];

当判断的时候需要忽略大小写可以使用[cd]

[c] 忽略大小写

[d] 忽略重音符号
[cd]既不区分大小写,也不区分发音符号。
使用:

NSPredicate *pre = [NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@", @"abc"];

再涉及到一些更复杂的查询语句,比如判断字符串以某个字符串开头或者结尾,通配符的使用。

BEGINSWITH(已某个字符串开头, begins with)

NSString *targetString = @"h"; NSPredicate *pre = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@",targetString];

ENDSWITH(已某个字符串结尾, ends with)

NSString *targetString = @"ing"; NSPredicate *pre = [NSPredicate predicateWithFormat:@"name ENDSWITH %@",targetString];

通配符 LIKE

*代表一个或者多个或者是空

?代表一个字符

Test *test1 = [[Test alloc]init];
test1.name = @"absr";
test1.code = @1;

Test *test2 = [[Test alloc]init];
test2.name = @"asb";
test2.code = @2;

Test *test3 = [[Test alloc]init];
test3.name = @"raskj";
test3.code = @3;

NSArray *array = @[test1, test2, test3];

NSPredicate *pre = [NSPredicate predicateWithFormat:@"name LIKE %@", @"?b*"];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);

结果是只有test1符合,like也可以接受[cd]

NSPredicate *pre = [NSPredicate predicateWithFormat:@"name LIKE[cd] %@", @"?b*"];

关系运算,包括了IN、BETWEEN、AND、OR、NOT

IN(之中)

NSPredicate *pre = [NSPredicate predicateWithFormat:@"code IN %@", @[@1, @3]];

判断code是否@1或者是@2,也就是是否在数组中。

OR(或,可以用||代替)
OR可以用来代替IN达到同样的效果,但是OR更灵活。

NSPredicate *pre = [NSPredicate predicateWithFormat:@"code == %@ OR code == %@ ", @1, @3];

效果和IN一样,但是OR可以判断不只一个属性

NSPredicate *pred = [NSPredicate predicateWithFormat:@"code == %@ OR name == %@ ", @1, @"asb"];

BETWEEN(之间)

通常用于判断NSNumber对象

NSPredicate *pred = [NSPredicate predicateWithFormat:@"code BETWEEN {1, 3}"];

判断code是否>=1且<=3
AND(且,可以用&&代替)

NSPredicate *pred = [NSPredicate predicateWithFormat:@"code >= %@ AND code <=%@", @1, @3];

NOT(非,可以用!代替)

NOT
最常见的用法就是从一个数组中剔除另外一个数组的数据,可能有点绕,举个例子就很明朗了。

NSArray *arrayFilter = @[@"abc1", @"abc2"];
NSArray *arrayContent = @[@"a1", @"abc1", @"abc4", @"abc2"];
NSPredicate *thePredicate = [NSPredicate predicateWithFormat:@"NOT (SELF in %@)", arrayFilter];
NSLog(@"%@",[arrayContent filteredArrayUsingPredicate:thePredicate]);

打印
( a1, abc4)

比起循环比较再加到新数组中,简单的不止一两点。
前面提到的都是用+ (NSPredicate *)predicateWithFormat:(NSString )predicateFormat, ...;方法创建,还有另一种常用的方法:+ (NSPredicate)predicateWithBlock:(BOOL (^)(id evaluatedObject, NSDictionary *bindings))block,用Block形式创建

NSPredicate *pre = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
    Test *test = (Test *)evaluatedObject;
    if (test.code.integerValue > 2) {
        return YES;
    }
    else{
        return NO;
    }
}];

参数evaluatedObject表示数组成员,block必须返回YES或者NO,分别表示匹配还是不匹配。请忽略bindings参数,具体作用我也没搞清楚。

多重筛选

如果需要匹配数个属性的筛选,用AND或者OR来串联显然有点麻烦,NSCompoundPredicate类可以满足我们的需求,它可以将多个NSPredicate对象的组合,组合方式可以是AND或者OR。

NSPredicate *pre1 = [NSPredicate predicateWithFormat:@"code >= %@", @3];
NSPredicate *pre2 = [NSPredicate predicateWithFormat:@"code <= %@", @2];

//以AND形式组合
NSPredicate *pre = [NSCompoundPredicate andPredicateWithSubpredicates:@[pre1,pre2]];
//以OR形式组合
NSPredicate *pre = [NSCompoundPredicate orPredicateWithSubpredicates:@[pre1, pre2]];

匹配用法

其实NSPredicate不仅可以用于筛选,还可以用来判断匹配直接返回是否符合,主要方法是- (BOOL)evaluateWithObject:(id)object;,用法:

Test *test1 = [[Test alloc]init];
test1.name = @"absr";
test1.code = @1;

NSPredicate *pres = [NSPredicate predicateWithFormat:@"code == %@", @2];
BOOL match = [pres evaluateWithObject:test1];

当然最常用的还是配合配合正则表达式,列举几个常用的正则:

就像开头提到的2个用法,就是一个例子

是否以a开头以e结尾

NSString *string=@"assdbfe";
NSString *targetString=@"^a.+e$";
NSPredicate *pres = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", targetString];
BOOL match = [pres evaluateWithObject:string];

是否是邮箱

NSString *strRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{1,5}";

是否是手机号

NSString *strRegex = @"[0-9]{1,20}";

不得不说,在利用+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;方法创建的NSPredicate对象的时候有个坑。

在某些情况下,类似于上面例子中的code字符串不是很明确,创建的时候就会这样使用

Test *test1 = [[Test alloc]init];
test1.name = @"absr";
test1.code = @1;

Test *test2 = [[Test alloc]init];
test2.name = @"asb";
test2.code = @2;

Test *test3 = [[Test alloc]init];
test3.name = @"raskj";
test3.code = @3;

NSArray *array = @[test1, test2, test3];

NSPredicate *pre = [NSPredicate predicateWithFormat:@"%@ == %@", @"code", @2];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);

注意NSPredicate对象的初始化方式。运行这块代码你会发现test2对象没有被查询出来。打印pre发现"code" == 2,这说明查找的是"code"方法的返回值,这显然行不通。

如果查询的是属性,比如code是Test类的属性,那么利用下面的创建方式

NSPredicate *pre = [NSPredicate predicateWithFormat:@"%K == %@", @"code", @2];

就不会被坑
注意:%K的K必须是大写。

最后
NSPredicate几乎可以满足所有形式的查询,配合Core Data的数据库查询当然不在话下。NSPredicate用法不只这些,有兴趣的同学可以看下nshipster网站的这篇博文,里面提到了一些我没有涉及到的用法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容