Objective-C的正则表达式

1,220 阅读2分钟

Objective-C的正则表达式

一、工具

二、正则

2.1 使用场景

  • 用来批量提取或替换有规律的字符串;
  • 在高级文本编辑器中使用;
  • 在各类办公软件(office等)中使用;
  • 检测用户的输入是否合法;
  • 在各种开发语言中使用;(C#,java,JS,PHP等)
  • 网络爬虫;
  • 批量文本处理等;
  • xcode中快使用

2.2 语法

2.2.1 元字符

元字符描述
.句号匹配任意单个字符除了换行符.
[ ]字符种类. 匹配方括号内的任意字符.
[^ ]否定的字符种类. 匹配除了方括号里的任意字符
*匹配>=0个重复的在*号之前的字符.
+匹配>=1个重复的+号前的字符.
?标记?之前的字符为可选.
{n,m}匹配num个大括号之前的字符 (n <= num <= m).
(xyz)字符集, 匹配与 xyz 完全相等的字符串.
|或运算符,匹配符号前或后的字符.
\转义字符,用于匹配一些保留的字符 `[ ] ( ) { } . * + ? ^ $ \`
从开始行开始匹配.
$从末端开始匹配.

2.2.2 简写字符集

简写描述
.除换行符外的所有字符
\w匹配所有字母数字, 等同于 [a-zA-Z0-9_]
\W匹配所有非字母数字, 即符号, 等同于: [^\w]
\d匹配数字: [0-9]
\D匹配非数字: [^\d]
\s匹配所有空格字符, 等同于: [\t\n\f\r\p{Z}]
\S匹配所有非空格字符: [^\s]
\f匹配一个换页符
\n匹配一个换行符
\r匹配一个回车符
\t匹配一个制表符
\v匹配一个垂直制表符
\p匹配 CR/LF (等同于 \r\n),用来匹配 DOS 行终止符

2.2.3 零宽度断言(前后预查)

先行断言和后发断言都属于非捕获簇(不捕获文本 ,也不针对组合计进行计数). 先行断言用于判断所匹配的格式是否在另一个确定的格式之前, 匹配结果不包含该确定格式(仅作为约束).

例如, 我们想要获得所有跟在 $ 符号后的数字, 我们可以使用正后发断言 (?<=\$)[0-9\.]*. 这个表达式匹配 $ 开头, 之后跟着 0,1,2,3,4,5,6,7,8,9,. 这些字符可以出现大于等于 0 次.

零宽度断言如下:

符号描述
?=正先行断言-存在
?!负先行断言-排除
?<=正后发断言-存在
?<!负后发断言-排除

2.2.4 标志

标志也叫模式修正符, 因为它可以用来修改表达式的搜索结果. 这些标志可以任意的组合使用, 它也是整个正则表达式的一部分.

标志描述
i忽略大小写.
g全局搜索.
m多行的: 锚点元字符 ^ $ 工作范围在每行的起始.

2.2.5 优先级

在这些运算符同时出现时,按照下面的优先级进行操作。

优先级符号
最高\
( )、(?: )、(?= )、[ ]
*、+、?、{n}、{n,}、{n,m}
^、$、中介字符
最低|

2.2.6 贪婪匹配与惰性匹配

正则表达式默认采用贪婪匹配模式,在该模式下意味着会匹配尽可能长的子串。我们可以使用 ? 将贪婪匹配模式转化为惰性匹配模式。

贪婪模式

(.*nt) => People want to try something different.

惰性模式

(.*?nt) => People want to try something different.

常见的惰性限定符:

符号说明
*?重复任意次,但尽可能少重复
+?重复1次或更多次,但尽可能少重复
??重复0次或1次,但尽可能少重复
{n,m}?重复n到m次,但尽可能少重复
{n,}?重复n次以上,但尽可能少重复

三. iOS中的应用

3.1 NSPredicate

3.1.1 概念

谓词:一种逻辑条件的定义,用于约束查找或者内存中筛选的搜索条件,一个用来匹配查询结果的条件,类似于SQL中的where语句.

3.1.2 API

// 通过表达式创建谓词实例
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;

// 表达式中的%@从arguments中取值
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat argumentArray:(nullable NSArray *)arguments;

// 表达式中的%@占位符将从argList中取值
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat arguments:(va_list)argList;

// 通过一个元数据查询字符串来创建谓词实例,仅macos可以使用
+ (nullable NSPredicate *)predicateFromMetadataQueryString:(NSString *)queryString API_AVAILABLE(macos(10.9)) API_UNAVAILABLE(ios, watchos, tvos);

// 创建一个固定结果为BOOL值的谓词实例
+ (NSPredicate *)predicateWithValue:(BOOL)value;    

// 根据block的回调的返回值创建一个结果为YES/NO的谓词实例
+ (NSPredicate*)predicateWithBlock:(BOOL (^)(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

// 获取谓词的表达式,只读属性
@property (readonly, copy) NSString *predicateFormat;    

// 用常量值代替变量,用字典中的键值对替换用$声明的变量
- (instancetype)predicateWithSubstitutionVariables:(NSDictionary<NSString *, id> *)variables;    // substitute constant values for variables

// 比较评估对象是否符合谓词
- (BOOL)evaluateWithObject:(nullable id)object;    

// 比较评估对象是否符合谓词
- (BOOL)evaluateWithObject:(nullable id)object substitutionVariables:(nullable NSDictionary<NSString *, id> *)bindings API_AVAILABLE(macos(10.5), ios(3.0), watchos(2.0), tvos(9.0)); 

// 强制使用已安全解码的谓词以进行评估
- (void)allowEvaluation API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0)); 

3.1.3 常用方法

// 创建谓词
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"SELF CONTAINS 'world'"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", @"world"];
NSPredicate *predicate3 = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@" argumentArray:@[@"world"]];
NSLog(@"predicate1谓词表达式:%@", predicate1.predicateFormat);
NSLog(@"predicate2谓词表达式:%@", predicate2.predicateFormat);
NSLog(@"predicate3谓词表达式:%@", predicate3.predicateFormat);

// 使用
NSString *str = @"Hello, world";
NSLog(@"谓词评估结果:%hhd",[predicate1 evaluateWithObject:str]); // 打印 谓词评估结果:1

NSString *regex = @"^[0-9]+$";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
NSString *str = @"1314";
if ([predicate evaluateWithObject:str]) {
    NSLog(@"Condition Matched");
}

3.2 NSString

只能匹配第一个,可指定是否区分大小写,可用于替换字符串

NSString *searchText = @"targetString";
NSRange range = [searchText rangeOfString:@"^[0-9]+$" options:NSRegularExpressionSearch];
if (range.location != NSNotFound) {
    NSLog(@"target range :%@", [searchText substringWithRange:range]);
}

3.3 NSRegularExpression

 /*
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标准作为词的边界,否则所有传统正则表达式的词边界都有效
};
*/
NSString *searchText = @"what do you want to match string";
NSError *error = NULL;

// 创建一个正则
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[0-9]+$" options:NSRegularExpressionCaseInsensitive error:&error];


/********************** 匹配方法 ************************/

//仅取出第一条匹配记录
NSTextCheckingResult *firstResult = [regex firstMatchInString:searchText options:0 range:NSMakeRange(0, [searchText length])];
if (firstResult) {
    NSLog(@"firstResult:%@", [searchText substringWithRange:firstResult.range]);
}

//遍历所有匹配记录
NSArray *matches = [regex matchesInString:searchText
                    options:0
                    range:NSMakeRange(0, searchText.length)];
for (NSTextCheckingResult *match in matches) {
    NSRange range = [match range];
    NSString *mStr = [searchText substringWithRange:range];
    NSLog(@"AllResult:%@", mStr);
}

// 返回匹配总数
NSUInteger count = [regex numberOfMatchesInString:searchText option:0 range:NSMakeRange(0,searchText.length)];


/********************** 替换方法 ************************/

// 返回替换后的新字符串, 源字符串不变
NSString *newString = [regex stringByReplacingMatchesInString:searchText options:0 range:NSMakeRange(0,searchText.length) withTemplate:@"11"];

// 直接在源字符串里替换
[regex replaceMatchesInString:searchText options:0 range:NSMakeRange(0,searchText.length) withTemplate:@"111"];