前言
我們經常需要對一個字符串內容的類型進行判斷,是否是地址
,電話
或者URL
.如果手動去處理,經常容易忽略一些判斷規則的細節,導致結果不準確.
系統給我們提供了一個類NSDataDetector
,它可以用于判斷字符串內部內容的類型,而且,一段字符串中如果同時含有電話
,地址
,URL
等,也可以區分出來,真是神奇的字符串翻譯,下面我們就對這個magic class進行詳細介紹.
NSDataDetector
NSDataDetector
是NSRegularExpression
的子類.看一下官方文檔的介紹
A specialized regular expression object that matches natural language text for predefined data patterns
NSDataDetector
是用于匹配自然語言字符串來確定對象類型的特殊的正則表達式對象.
NSDataDetector用法
目前NSDataDetector
類可以用于匹配的類型有:日期, 地址, 鏈接, 手機號, 物流信息.匹配結果通過返回 NSTextCheckingResult 對象來表述.
NSTextCheckingResult
NSTextCheckingResult
是表示匹配結果的類,需要注意的是NSTextCheckingResult
可以用來表示NSRegularExpression
和NSDataDetector
的結果,但是通過NSDataDetector
匹配返回的NSTextCheckingResult
和父類NSRegularExpression
返回的是不同的.
必要屬性,存在于所有的解析結果中.
@property (readonly) NSTextCheckingType resultType;
@property (readonly) NSRange range;
可選屬性,只有在匹配到特定類型時,某些屬性才會有值.
@property (nullable, readonly, copy) NSOrthography *orthography;
@property (nullable, readonly, copy) NSArray<NSDictionary<NSString *, id> *> *grammarDetails;
@property (nullable, readonly, copy) NSDate *date;
@property (nullable, readonly, copy) NSTimeZone *timeZone;
@property (readonly) NSTimeInterval duration;
@property (nullable, readonly, copy) NSDictionary<NSTextCheckingKey, NSString *> *components API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0));
@property (nullable, readonly, copy) NSURL *URL;
@property (nullable, readonly, copy) NSString *replacementString;
@property (nullable, readonly, copy) NSArray<NSString *> *alternativeStrings API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0));
@property (nullable, readonly, copy) NSRegularExpression *regularExpression API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0));
@property (nullable, readonly, copy) NSString *phoneNumber API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0));
提到了匹配的類型,我們來看一下NSTextCheckingType resultType
都包含哪些類型.
typedef NS_OPTIONS(uint64_t, NSTextCheckingType) { // a single type
NSTextCheckingTypeOrthography = 1ULL << 0, // language identification
NSTextCheckingTypeSpelling = 1ULL << 1, // spell checking
NSTextCheckingTypeGrammar = 1ULL << 2, // grammar checking
NSTextCheckingTypeDate = 1ULL << 3, // date/time detection
NSTextCheckingTypeAddress = 1ULL << 4, // address detection
NSTextCheckingTypeLink = 1ULL << 5, // link detection
NSTextCheckingTypeQuote = 1ULL << 6, // smart quotes
NSTextCheckingTypeDash = 1ULL << 7, // smart dashes
NSTextCheckingTypeReplacement = 1ULL << 8, // fixed replacements, such as copyright symbol for (c)
NSTextCheckingTypeCorrection = 1ULL << 9, // autocorrection
NSTextCheckingTypeRegularExpression API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0)) = 1ULL << 10, // regular expression matches
NSTextCheckingTypePhoneNumber API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0)) = 1ULL << 11, // phone number detection
NSTextCheckingTypeTransitInformation API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0)) = 1ULL << 12 // transit (e.g. flight) info detection
};
NSDataDetector
能匹配的類型日期, 地址, 鏈接, 手機號, 物流信息.只有
NSTextCheckingTypeDate
NSTextCheckingTypeAddress
NSTextCheckingTypeLink
NSTextCheckingTypePhoneNumber
NSTextCheckingTypeTransitInformation
NSDataDetector
返回的結果肯定屬于dataDetectorTypes
類型的某一種,并且根據結果類型的不同,匹配對應的屬性.比如
日期類型NSTextCheckingTypeDate
會包含NSDate
和NSTimeZone
類型的timeZone
和duration
.
鏈接類型NSTextCheckingTypeLink
會包含NSURL
等.
示例
以下代碼通過NSDataDetector
來判斷手機號
和URL
,如果失敗會返回error
.
NSError *error = nil;
NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes: NSTextCheckingTypeLink | NSTextCheckingTypePhoneNumber error:&error];
在NSDataDetector
實例創建后,可以通過NSRegularExpression
的方法numberOfMatches(in:options:range:)
來獲得字符串在某個rang
內匹配的個數.
NSUInteger numberOfMatches = [detector numberOfMatchesInString:string options:0 range:NSMakeRange(0, [string length])];
如果只是想獲取整個字符串的第一個匹配對象,numberOfMatches(in:options:range:)
方法就可以.但是類型判斷不同于正則表達式的地方在于,客戶端會更注重分析后的附加信息.也就是NSTextCheckingResult
對應的可選屬性.
result
的附加信息取決于其所屬的類型.如果是NSTextCheckingTypeLink
類型,那么屬性URL
就是關鍵的附加信息.如果是NSTextCheckingTypePhoneNumber
類型,則phoneNumber
是附加信息.
NSDateDetector
的兩個方法 matches(in:options:range:)
和firstMatch(in:options:range:)
都可以用于獲取解析結果.不同的是matches(in:options:range:)
會返回所有的匹配結果,而firstMatch(in:options:range:)
只會返回第一個匹配結果.
下面的代碼段就是獲取字符串中所有的鏈接和電話號碼的匹配結果.
NSArray *matches = [detector matchesInString:string options:0 range:NSMakeRange(0, [string length])];
for (NSTextCheckingResult *match in matches) {
NSRange matchRange = [match range];
if ([match resultType] == NSTextCheckingTypeLink) {
NSURL *url = [match URL];
} else if ([match resultType] == NSTextCheckingTypePhoneNumber) {
NSString *phoneNumber = [match phoneNumber];
}
}
NSRegularExpression
類的enumerator
方法是匹配中最常用的方法.該方法運行我們多次去匹配一個字符串,在回調中自由地實現各種定制化需求,并且可以隨時控制遍歷停止的時機.通過block回調的屬性BOOL *stop
可以停止遍歷.
下面是一個在匹配了特定次數后停止遍歷的一個示例,可以用作參考.
__block NSUInteger count = 0;
[detector enumerateMatchesInString:string
options:0
range:NSMakeRange(0, [string length])
usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) {
NSRange matchRange = [match range];
if ([match resultType] == NSTextCheckingTypeLink) {
NSURL *url = [match URL];
} else if ([match resultType] == NSTextCheckingTypePhoneNumber) {
NSString *phoneNumber = [match phoneNumber];
}
if (++count >= 100) *stop = YES;
}];
注意
NSDataDetector
只適用于解析特定類型的自然語言.只使用一定的情況匹配,并不是全能的.
如果文本具有特殊的格式,一個使用對應的系統提供的類型轉換formatter
來處理.比如想解析時間戳類型的數據,就應該使用DateFormatter
類來解析,并輸出NSDate
對象.
如果文本是特定的類型如XML
或JSON
,那么應該先摘取自然語言,使用
XMLParser
或JSONSerialization
進行解析.