目录

正则表达式语法

值表达

`\`    将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,“n”匹配字符“n”。“\n”匹配换行符。序列“\\”匹配“\”,“\(”匹配“(”。

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

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

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

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

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

`\d `   匹配任意数字

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

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

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

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

`^`      匹配字符串的开头

`$`      匹配字符串的结尾

修饰表达

 `*`    零次或多次匹配前面的字符或子表达式。例如,zo* 匹配“z”和“zoo”。* 等效于 {0,}

 `+`    一次或多次匹配前面的字符或子表达式。例如,“zo+”与“zo”和“zoo”匹配,但与“z”不匹配。+ 等效于 {1,}

`? `     零次或一次匹配前面的字符或子表达式。例如,“do(es)?”匹配“do”或“does”中的“do”。? 等效于 {0,1}

`{n}`    n 是非负整数。正好匹配 n 次。例如,“o{2}”与“Bob”中的“o”不匹配,但与“food”中的两个“o”匹配。

`{n,}`  n 是非负整数。至少匹配 n 次。例如,“o{2,}”不匹配“Bob”中的“o”,而匹配“foooood”中的所有 o。“o{1,}”等效于“o+”。“o{0,}”等效于“o*”。

`{n,m}` 其中 n <= m。匹配至少 n 次,至多 m 次。例如,“o{1,3}”匹配“fooooood”中的头三个 o'o{0,1}' 等效于 'o?'。注意:您不能将空格插入逗号和数字之间。
?

`x|y` 匹配 x  y。例如,'z|food' 匹配“z”或“food”。'(z|f)ood' 匹配“zood”或“food”。

`[xyz]` 字符集。匹配包含的任一字符。例如,“[abc]”匹配“plain”中的“a”。

`[^xyz]` 反向字符集。匹配未包含的任何字符。例如,“[^abc]”匹配“plain”中的“p”。

`[a-z]` 字符范围。匹配指定范围内的任何字符。例如,“[a-z]”匹配“a”到“z”范围内的任何小写字母。

`[^a-z]` 反向范围字符。匹配不在指定的范围内的任何字符。例如,“[^a-z]”匹配任何不在“a”到“z”范围内的任何字符。

NSPredicate 的简单使用

创建NSPredicate

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == 'Herbie'"];

我们使用 NSPredicate 的类方法 +predicateWithFormat: 创建了一个 NSPredicate 的对象,并传入了一个字符串参数,这个类方法使用该字符串在后台构建对象,来计算谓词的值,简单来说,就是以这个字符串来当筛选过滤的条件。

计算谓词

BOOL match = [predicate evaluateWithObject:car];
NSLog(@"%s", (match) ? "YES" : "NO");

方法 -evaluateWithObject: 通知接收对象(即谓词)根据指定的对象计算自身的值。这里是以 name 作为键路径,使用 -valueForKeyPath: 方法来获取名称,然后将自身的这个名称(即 name)与 Herbie 进行比较,相同返回 YES,否则返回 NO。

以下是另外一个谓词:

predicate = [NSPredicate predicateWithFormat:@"engine.horsepower > 150"];
match = [predicate evaluateWithObject:car];
NSLog(@"%s", (match) ? "YES" : "NO");

这个谓词字符串的左侧是一个键路径,该键路径链接到 car 内部,查找 engine,然后再查找 engine 的 horsepower,然后将其与150进行比较,看它是否大于150。

数据过滤器

-filteredArrayUsingPredicate:,这个方法是 NSArray 数组中的一种类别方法,它会循环遍历数组中的对象,根据谓词来计算每个对象的值,如果是 YES,那么就将这个对象添加到将被返回的新数组中

NSArray *results = [cars filteredArrayUsingPredicate:predicate];
NSLog(@"%@", results);

这样的话,只需要一行代码就可以获取筛选之后的结果了,不过这里输出的是汽车的全部信息,而不是上面那样输出汽车的名字,不过我们可以使用 KVC(键值编码)来提取其中的名称:

NSArray *names = [results valueForKey:@"name"];
NSLog(@"%@", names);

上面的 cars 是不可变数组,如果我们的数据是存放在可变数组里面,而且我们需要剔除不满足条件的对象的话,可以使用 NSMutableArray 的 -filterUsingPredicate: 方法:

NSMutableArray *carsCopy = [cars mutableCopy];
[carsCopy filterUsingPredicate:predicate];
NSLog(@"%@", carsCopy);

因为 NSMutableArrayNSArray 的子类,所以也是可以用 -filteredArrayUsingPredicate: 方法来构建新的不可变数组的。使用谓词的确很便捷,但是它的运行速度并不会比自己编写全部代码要快,因为它无法避免要使用循环来遍历。

格式说明符

从方法 -predicateWithFormat: 可以看出来,传入的字符串是可以使用格式说明符的,比如:%d、%f 等。而且一般筛选条件都不是硬编码到项目里面的,都是根据用户的输入来进行筛选的。

除了可以使用 printf 说明符,还可以使用 %@ 来插入字符串,而 %@ 会被当做带引号的字符串:

predicate = [NSPredicate predicateWithFormat:@"name == %@", @"Herbie"];

NSPredicate 字符串中也可以使用 %K 来指定键路径:

predicate = [NSPredicate predicateWithFormat:@"%K == %@", @"name", @"Herbie"];

现在,我们有一个含有变量的谓词,可以使用 -predicateWithSubstitutionVariables: 来构造新的专用谓词,创建一个键/值对字典,其中键是变量名(不包含美元符号$),值是想要插入谓词的内容:

NSDictionary *varDict = [NSDictionary dictionaryWithObjectsAndKeys:@"Herbie", @"NAME", nil];

这里使用字符串 “Herbie” 作为键 “NAME” 的值,构造以下新谓词:

predicate = [predicateTemplate predicateWithSubstitutionVariables:varDict];

这个谓词跟之前的那些没什么不一样。

也可以用其他对象作为变量的值,例如 NSNumber,以下谓词用于过滤引擎的马力:

predicateTemplate = [NSPredicate predicateWithFormat:@:"engine.horsepower > `$POWER"`];

varDict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:150], @"POWER", nil];

predicate = [predicateTemplate predicateWithSubstitutionVariables:varDict];

除了使用 NSNumberNSString 之外,也可以使用 [NSNull null] 来设置 nil 值,甚至可以使用数组。要注意的是,不能使用 "$变量名" 作为键路径,它只能表示值。使用谓词格式字符串时,如果想在程序中通过代码改变键路径,需要使用 %K 格式说明符。谓词机制不进行静态类型检查。

运算符

比较和逻辑运算符

运算符   	       比较作用<br />
`>`	              大于某数<br />
`>= 和 =>`	       大于或等于某数<br />
`<`	              小于某数<br />
`<= 和 =<`	       小于或等于某数<br />
`!= 和 <>`	       不等于某数<br />

此外,谓词字符串语法还支持括号表达式、AND、OR 和 NOT 逻辑运算符,以及用 C 语言样式表示具有相同功能的 “&&””||”“!” 符号。

我们可以筛选出马力在某个范围内的汽车:

predicate = [NSPredicate predicateWithFormat:@"(engine.horsepower > 50) AND (engine.horsepower < 200)"];
results = [cars filteredArrayUsingPredicate:predicate];
NSLog(@"oop %@", results);

上面是筛选出马力在50和200之间的汽车。

谓词字符串中的运算符不区分大小写,比如上面的 AND,可以随便写,比如:And、aNd等。

不等号既适用于数字值也适用于字符串值,比如按照字母表顺序查看所有名字排在 “Newton” 之前的汽车

predicate = [NSPredicate predicateWithFormat:@"name < 'Newton'"];
results = [cars filteredArrayUsingPredicate:predicate];
NSLog(@"%@", [results valueForKey:@"name"]);

数组运算符

上面筛选马力在50到200之间的汽车使用的谓词字符串为 “(engine.horsepower > 50) AND (engine.horsepower < 200)”,我们还可以写得更加简洁:

predicate = [NSPredicate predicateWithFormat:@"engine.horsepower BETWEEN { 50, 200 }"];

花括号表示数组,BETWEEN 将数组中的第一个元素看成数组的下限,第二个元素看成数组的上限。

可以使用 %@ 格式说明符向 NSArray 数组中插入对象:

NSArray *betweens = [NSArray arrayWithObjects:[NSNumber numberWithInt:50], [NSNumber numberWithInt:200], nil];
predicate = [NSPredicate predicateWithFormat:@"engine.horsepower BETWEEN %@", betweens];

也可以使用变量:

predicateTemplate = [NSPredicate predicateWithFormat:@"engine.horsepower BETWEEN `$`POWERS"];
varDict = [NSDictionary dictionaryWithObjectsAndKeys:betweens, @"POWERS", nil];
predicate = [predicateTemplate predicateWithSubstitutionVariables:varDict];

数组不仅可以用来指定某个区间的端点值,还可以配合 IN 运算符来查找数组中是否含有某个特定值,跟 SQL 很像哈:

predicate = [NSPredicate predicateWithFormat:@"name IN { 'Herbie', 'Snugs', 'Badger', 'Flap' }"];
results = [cars filteredArrayUsingPredicate:predicate];
NSLog(@"%@", [results valueForKey:@"name"]);

字符串运算符

BEGINSWITH 检查某个字符串是否以另一个字符串开头 ENDSWITH 检查某个字符串是否以另一个字符串结尾 CONTAINS 检查某个字符串是否在另一个字符串内部

使用关系运算符可以执行一些有用的操作,例如使用 “name BEGINSWITH ‘Bad’” 匹配 Badger,使用 “name ENDSWITH ‘vis’” 匹配 Elvis,以及使用 “name CONTAINS udg” 匹配 Judge。

如果是 "name BEGINSWITH 'HERB'" 这样的呢?它不会与 Herbie 匹配,因为这些匹配是区分大小写的,同样,"name BEGINSWITH 'Hérb'" 也不会与之匹配,因为其中的 e 是含有重音符的。为了避免这些情况,可以为运算符添加 [c][d][cd] 修饰符,其中 c 表示 “不区分大小写”,d 表示 “不区分发音符号(diacritic insensitive,即忽略重音符)”,cd 表示 “即不区分大小写,也不区分发音符号”。通常情况下,都会使用这两个修饰符,除非有特殊需求需要区分大小写或者重音符号。

所以 Herbie“name BEGINSWITH[cd] ‘HERB’” 相匹配。

LIKE 运算符

了解 SQL 的同学看到 LIKE 应该能猜到了,没错,有时候匹配开头或者结尾,又或者是否包含还不够,所以谓词格式字符串还提供了 LIKE 运算符,问号表示与一个字符匹配,型号表示与任意个字符匹配,也可以称为 “通配符”。

谓词字符串 “name LIKE *er* 将会与任何含有 er 的名称相匹配,等效于 CONTAINS。

谓词字符串 “name LIKE ‘???er*’” 将会与 Pager Car 相匹配,因为其中的 er 前面有3个字符,后面也有一些字符。但是它与 Badger 不匹配,因为 Badger 的 er 前面有4个字符。

另外,LIKE 也接收 [cd] 修饰符,用户忽略大小写和发音符号。

MATCHES 运算符

如果你喜欢用正则表达式,可以使用 MATCHES 运算符,赋给它一个正则表达式,谓词将会计算出它的值。

NSString *regex = @"^A.+e$";   //A开头,e结尾
@"name MATCHES %@",regex
      

有 SELF 就足够了

有时候,我们可能需要将谓词应用于简单的值(例如纯文本的字符串),而不是那些通过键路径进行操作的复杂对象,比如我们需要从一汽车名称的数组中查询 name 时,就不能像之前那样子用了,这时候就轮到 SELF 出场了!

SELF 表示的是响应谓词计算的对象,事实上我们可以将谓词中所有的键路径表示成对应的 SELF 形式:

predicate = [NSPredicate predicateWithFormat:@"SELF.name IN { 'Herbie', 'Snugs', 'Badger', 'Flap' }"];

我们先获取所有的汽车名称,然后构造一个谓词,并计算该谓词的值:

names = [cars valueForKey:@"name"];
predicate = [NSPredicate predicateWithFormat:@"SELF IN { 'Herbie', 'Snugs', 'Badger', 'Flap' }"];
results = [names filteredArrayUsingPredicate:predicate];
NSLog(@"%@", results);

结果和前面的示例是一样的:Herbie 和 Badger。

这里提一个问题,以下代码会输出什么呢?

NSArray *names1 = [NSArray arrayWithObjects:@"Herbie", @"Badger", @"Judge", @"Elvis", nil];
NSArray *names2 = [NSArray arrayWithObjects:@"Judge", @"Paper Car", @"Badger", @"Phoenix", nil];
predicate = [NSPredicate predicateWithFormat:@"SELF IN %@", names1];
results = [names2 filteredArrayUsingPredicate:predicate];
NSLog(@"%@", results);

答案如下:

(
    Judge,
    Badger
)

对于取两个数组交集的运算来说,这种方式很巧妙。谓词包含了第一个数组的内容,因此和下面的形式类似:

SELF IN {"Herbie", "Badger", "Judge", "Elvis"}

现在,使用该谓词筛选第二个名称数组,在 name2 中如果有同时存在两个数组中的字符串,那么 SELF IN 会确定它是符合条件的,因此它就会保留在结果数组中,如果对象只存在于第二个数组中,那么它不会与谓词中的任何字符串匹配,所以该对象将被过滤掉,而只存在于第一个数组中的字符串因为要用来进行比较,所以将一直保留在原来的位置,不会出现在结果数组中。


如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!


hetaodie

Mobile development

简单,深入的研究移动客户端开发技术"