版本:iOS13.7
一、簡介
NSRegularExpression是利用正則表達式匹配字符串的類,初始化成功后不可更改。
正則表達式的具體語法可查看iOS正則表達式語法全集。使用時若有\b這類字符,需要傳入\\b,如"aa\\b"表示查詢在在邊界的aa字符串,"aa\b"表示的是查詢aa\b這個字符串。
二、NSRegularExpression的api
@interface NSRegularExpression : NSObject <NSCopying, NSSecureCoding>
//通過模式和選頂創(chuàng)建正則表達式
//若模式無效,則返回nil,并通過引用返回error
//pattern 模式,參考 iOS正則表達式語法全集
//options 選項,詳見說明1
+ (nullable NSRegularExpression *)regularExpressionWithPattern:(NSString *)pattern
options:(NSRegularExpressionOptions)options error:(NSError **)error;
//實例初始化方法
- (nullable instancetype)initWithPattern:(NSString *)pattern
options:(NSRegularExpressionOptions)options error:(NSError **)error;
//返回正則表達式的模式 只讀
@property (readonly, copy) NSString *pattern;
//返回正則表達式的選項 只讀 詳見說明1
@property (readonly) NSRegularExpressionOptions options;
//返回正則表達式的捕獲組數(shù)量 只讀
@property (readonly) NSUInteger numberOfCaptureGroups;
//將字符串通過\來轉(zhuǎn)義
+ (NSString *)escapedPatternForString:(NSString *)string;
@end
- 說明1
若不想指定選項,可以傳入0
typedef NS_OPTIONS(NSUInteger, NSRegularExpressionOptions) {
//忽略大小寫 "AA"相當(dāng)于"aa"
NSRegularExpressionCaseInsensitive = 1 << 0,
//忽略空格和#后面的注釋 "A B#AA"相當(dāng)于"AB"
NSRegularExpressionAllowCommentsAndWhitespace = 1 << 1,
//將整個模式視為文字字符串 "AA\\b"其中的\\b不會當(dāng)成匹配邊界,而是字符串
NSRegularExpressionIgnoreMetacharacters = 1 << 2,
//允許.匹配任何字符,包括行分隔符。"a.b"可以匹配"a\nb"
NSRegularExpressionDotMatchesLineSeparators = 1 << 3,
//允許^和$匹配行的開頭和結(jié)尾(這個好像是一直生效的)
NSRegularExpressionAnchorsMatchLines = 1 << 4,
//僅將\n視為行分隔符,否則,將使用所有標(biāo)準(zhǔn)行分隔符
NSRegularExpressionUseUnixLineSeparators = 1 << 5,
//使用Unicode TR#29指定單詞邊界,否則,使用傳統(tǒng)的正則表達式單詞邊界
NSRegularExpressionUseUnicodeWordBoundaries = 1 << 6
};
三、NSRegularExpression的NSMatching擴展
@interface NSRegularExpression (NSMatching)
//匹配字符串,并在成功匹配時回調(diào)一次block
//string 需要匹配的字符串
//options 匹配選項 詳見說明2
//range 需要匹配的字符串的范圍
//回調(diào)block result 匹配的結(jié)果,若result.range不為{0, 0}則表示匹配成功
//flags 當(dāng)前狀態(tài) 詳見說明3
//stop 停止符 當(dāng)設(shè)為YES時,將停止匹配。
//詳見例2
- (void)enumerateMatchesInString:(NSString *)string
options:(NSMatchingOptions)options range:(NSRange)range
usingBlock:(void (NS_NOESCAPE ^)(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL *stop))block;
//匹配字符串,返回匹配結(jié)果的數(shù)組
//詳見例3
- (NSArray<NSTextCheckingResult *> *)matchesInString:(NSString *)string
options:(NSMatchingOptions)options range:(NSRange)range;
//匹配字符串,返回匹配結(jié)果的數(shù)量
//詳見例3
- (NSUInteger)numberOfMatchesInString:(NSString *)string
options:(NSMatchingOptions)options range:(NSRange)range;
//匹配字符串,返回第一個匹配結(jié)果
//詳見例3
- (nullable NSTextCheckingResult *)firstMatchInString:(NSString *)string
options:(NSMatchingOptions)options range:(NSRange)range;
//匹配字符串,返回第一個匹配結(jié)果的范圍
//詳見例3
- (NSRange)rangeOfFirstMatchInString:(NSString *)string
options:(NSMatchingOptions)options range:(NSRange)range;
@end
- 說明2
若不想指定,則傳入0,此時只有成功匹配時才會回調(diào)。
typedef NS_OPTIONS(NSUInteger, NSMatchingOptions) {
//在長時間的匹配操作期間,定期回調(diào)一次。
NSMatchingReportProgress = 1 << 0,
//當(dāng)匹配完成時,回調(diào)一次。
NSMatchingReportCompletion = 1 << 1,
//只能匹配查詢范圍開始處的字符串 "aa"只能匹配"aabcd",而不能匹配"baabcd"
NSMatchingAnchored = 1 << 2,
//允許匹配超出搜索范圍的范圍,例如文字邊界檢測,前瞻等。如果搜索范圍包含整個字符串,該選項將不起作用
NSMatchingWithTransparentBounds = 1 << 3,
//防止^和$自動匹配搜索范圍的開始和結(jié)束,如果搜索范圍包含整個字符串,該選項 將不起作用
//"^ab"默認(rèn)能匹配NSMakeRange(1, 3)]范圍上的"babcd"
//當(dāng)使用該選項時,則不能匹配
NSMatchingWithoutAnchoringBounds = 1 << 4
};
- 說明3
typedef NS_OPTIONS(NSUInteger, NSMatchingFlags) {
//還在長時間的匹配中
NSMatchingProgress = 1 << 0,
//匹配已經(jīng)完成
NSMatchingCompleted = 1 << 1,
//當(dāng)前匹配操作到達搜索范圍的末尾
NSMatchingHitEnd = 1 << 2,
//當(dāng)前匹配項取決于搜索范圍末端的位置
NSMatchingRequiredEnd = 1 << 3,
//由于內(nèi)部錯誤而導(dǎo)致匹配失敗而沒有檢查整個搜索范圍
NSMatchingInternalError = 1 << 4
};
- 例2
NSError *error;
NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:@"[a-z]{3}"
options:0 error:&error];
NSString *string = @"abcv1asdf";
[regular enumerateMatchesInString:string options:NSMatchingReportCompletion
range:NSMakeRange(0, string.length)
usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
NSLog(@"range = %@ str = %@ flag = %lu", NSStringFromRange(result.range),
[string substringWithRange:result.range], (unsigned long)flags);
}];
輸出:
range = {0, 3} str = abc flag = 0
range = {5, 3} str = asd flag = 0
range = {0, 0} str = flag = 6
其中的flag = 6實際為NSMatchingCompleted | NSMatchingHitEnd
- 例3
NSError *error;
NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:@"[a-z]{3}"
options:0 error:&error];
NSString *string = @"abcv1asdf";
NSArray *results = [regular matchesInString:string
options:0 range:NSMakeRange(0, string.length)];
for (NSTextCheckingResult *object in results) {
NSLog(@"range = %@ str = %@", NSStringFromRange(object.range),
[string substringWithRange:object.range]);
}
//相當(dāng)于results.count
NSInteger count = [regular numberOfMatchesInString:string options:0
range:NSMakeRange(0, string.length)] ;
//相當(dāng)于results.firstObject
NSTextCheckingResult *check = [regular firstMatchInString:string options:0
range:NSMakeRange(0, string.length)];
//相當(dāng)于check.range
NSRange range = [regular rangeOfFirstMatchInString:string options:0
range:NSMakeRange(0, string.length)];
輸出:
range = {0, 3} str = abc
range = {5, 3} str = asd
四、NSRegularExpression的NSReplacement擴展
@interface NSRegularExpression (NSReplacement)
//用templ替換匹配成功的字符串,返回已替換的字符串
//templ 要替換的字符串
//詳見例4
- (NSString *)stringByReplacingMatchesInString:(NSString *)string
options:(NSMatchingOptions)options range:(NSRange)range
withTemplate:(NSString *)templ;
//用templ替換匹配成功的字符串,將在傳入的字符串中直接更改,并返回替換的數(shù)量
//templ 要替換的字符串
//詳見例4
- (NSUInteger)replaceMatchesInString:(NSMutableString *)string
options:(NSMatchingOptions)options range:(NSRange)range
withTemplate:(NSString *)templ;
//自定義替換,返回傳入的templ
//一般傳入$0 表示匹配到的字符串 $1表示捕獲組的第一個 $2表示第二個
//詳見例5
- (NSString *)replacementStringForResult:(NSTextCheckingResult *)result inString:(NSString *)string offset:(NSInteger)offset template:(NSString *)templ;
//將字符串通過\來轉(zhuǎn)義
+ (NSString *)escapedTemplateForString:(NSString *)string;
@end
- 例4
NSError *error;
NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:@"[a-z]{3}"
options:0 error:&error];
NSString *string = @"abcv1asdf";
//用aa替換匹配的字符串 返回替換后的字符串
NSString *newStr = [regular stringByReplacingMatchesInString:string options:0
range:NSMakeRange(0, string.length) withTemplate:@"aa"];
NSLog(@"old = %@ ,newStr = %@", string, newStr);
NSMutableString *muta = string.mutableCopy;
NSLog(@"old = %@", muta);
//用bb替換匹配的字符串 返回替換的數(shù)量
NSInteger countt = [regular replaceMatchesInString:muta options:0
range:NSMakeRange(0, muta.length) withTemplate:@"bb"];
NSLog(@"new = %@ count = %ld", muta, countt);
輸出:
old = abcv1asdf
new = bbv1bbf count = 2
- 例5
NSRegularExpression *regular1 = [NSRegularExpression regularExpressionWithPattern:@"[a-z]"
options:0 error:nil];
NSString *string1 = @"abcd";
NSInteger offset = 0;
NSMutableString *muta1 = string1.mutableCopy;
for (NSTextCheckingResult *result in [regular1 matchesInString:muta1 options:0
range:NSMakeRange(0, muta1.length)]) {
NSRange range = [result range];
range.location += offset;
//$0表示匹配到的字符串 $1表示捕獲組的第一個 $2表示第二個
NSString *newStr = [regular replacementStringForResult:result
inString:muta1 offset:offset template:@"$0"];
NSString *replacement;
//將單字母變成雙字母
if ([newStr isEqualToString:@"a"]) {
replacement = @"aa";
} else if ([newStr isEqualToString:@"b"]) {
replacement = @"bb";
} else if ([newStr isEqualToString:@"c"]) {
replacement = @"cc";
} else if ([newStr isEqualToString:@"d"]) {
replacement = @"dd";
}
[muta1 replaceCharactersInRange:range withString:replacement];
//因為替換字符串的長度與原字符串不一致 需要設(shè)置偏移量
offset += ([replacement length] - range.length);
}
NSLog(@"%@", muta1);
輸出:
aabbccdd
五、子類NSDataDetector的使用
該類中集成了公用的正則表達式,支持查詢地址、日期、鏈接,號碼等
@interface NSDataDetector : NSRegularExpression
//通過檢查類型來初始化
// checkingTypes 詳見說明4
//詳見例6
+ (nullable NSDataDetector *)dataDetectorWithTypes:(NSTextCheckingTypes)checkingTypes
error:(NSError **)error;
//實例初始化方法
- (nullable instancetype)initWithTypes:(NSTextCheckingTypes)checkingTypes
error:(NSError **)error;
//類型 只讀
@property (readonly) NSTextCheckingTypes checkingTypes;
@end
- 說明4
NS_ENUM(NSTextCheckingTypes) {
//系統(tǒng)保留前32種類型
NSTextCheckingAllSystemTypes = 0xffffffffULL,
//其余可供用戶自定義的類型
NSTextCheckingAllCustomTypes = 0xffffffffULL << 32,
//所有類型
NSTextCheckingAllTypes = (NSTextCheckingAllSystemTypes | NSTextCheckingAllCustomTypes)
};
前32種類型為下面的選項
typedef NS_OPTIONS(uint64_t, NSTextCheckingType) {
//語言辯識
NSTextCheckingTypeOrthography = 1ULL << 0,
//拼寫檢查
NSTextCheckingTypeSpelling = 1ULL << 1,
//語法檢查
NSTextCheckingTypeGrammar = 1ULL << 2,
//日期時間檢測
NSTextCheckingTypeDate = 1ULL << 3,
//地址檢測
NSTextCheckingTypeAddress = 1ULL << 4,
//鏈接檢測
NSTextCheckingTypeLink = 1ULL << 5,
//智能引用
NSTextCheckingTypeQuote = 1ULL << 6,
//智能破折號
NSTextCheckingTypeDash = 1ULL << 7,
//固定替換
NSTextCheckingTypeReplacement = 1ULL << 8,
//自動改正
NSTextCheckingTypeCorrection = 1ULL << 9,
//自定義正則表達式
NSTextCheckingTypeRegularExpression = 1ULL << 10,
//電話號碼檢測
NSTextCheckingTypePhoneNumber = 1ULL << 11,
//過境(例如航班)信息檢測
NSTextCheckingTypeTransitInformation
};
- 例6
NSDataDetector *detactor = [NSDataDetector dataDetectorWithTypes:NSTextCheckingAllSystemTypes error:nil];
NSString *detaStr = @"我叫百度,我的主頁是https://www.baidu.com,我的手機號為13812345678";
NSArray *detaResults = [detactor matchesInString:detaStr options:0
range:NSMakeRange(0, detaStr.length)];
NSLog(@"%@", detaResults);
輸出:
(
"<NSLinkCheckingResult: 0x600003781220>{10, 21}{https://www.baidu.com}",
"<NSPhoneNumberCheckingResult: 0x60000390ddd0>{38, 11}{13812345678}"
)
匹配出兩個字符串,一個是鏈接,一個是號碼。