字符串比較中NSNumericSearch選項的工做原理

原文地址:蘋果梨的博客html

相信研究過怎麼在 ObjC 中進行版本字符串比對的朋友,大多都看過這一篇 StackOverflow 的問答:bash

Compare version numbers in Objective-Capp

裏面提到的 [versionStrA compare:versionStrB options:NSNumericSearch] 的方案應該是最優雅的方案了。測試

可是不理解這個 NSNumericSearch 的具體工做原理就去盲目使用是危險的,今天我就來研究下它的具體工做原理。ui

官方文檔

參照官方文檔裏的說明:spa

Numbers within strings are compared using numeric value, that is,
Name2.txt < Name7.txt < Name25.txt.

Numeric comparison only applies to the numerals in the string, not other characters
that would have meaning in a numeric representation such as a negative sign, a
comma, or a decimal point.
複製代碼

粗略的直譯一下:code

在字符串中的數字將被用數值進行比較,就是說,Name2.txt < Name7.txt < Name25.txt。

數值比較僅僅對字符串中的純數字(0-9)生效,而不對其它在數字表達中含有意義的字符生效,
例如負號,逗號或小數點。
複製代碼

這段說明略有歧義,致使不少人第一次看的時候被繞暈。例如剛剛那篇 StackOverflow 問答裏的 dooleyo 就理解成 "1.2.3""1.1.12" 進行比較時,會拋棄全部非數字的字符變成 1231112 進行比較,最後獲得 "1.2.3" < "1.1.12" 的結論。問答裏很多其它朋友也有相似的想法。cdn

探求真相

真相只有通過實驗才能獲得,因此寫了一些測試代碼來試一下具體的結果:htm

- (void)testExample {
    [self compareString:@"1.2.3" andString:@"1.1.12"];
    [self compareString:@"1.8" andString:@"1.7.2.3.55"];
    [self compareString:@"1.44" andString:@"1.5"];
    [self compareString:@"7.4.1" andString:@"7.5"];
}
    
- (void)compareString:(NSString *)stringA andString:(NSString *)stringB {
    NSComparisonResult result = [stringA compare:stringB options:NSNumericSearch];
    switch (result) {
        case NSOrderedDescending:
            NSLog(@"%@ > %@", stringA, stringB);
            break;
        case NSOrderedAscending:
            NSLog(@"%@ < %@", stringA, stringB);
            break;
        default:
            NSLog(@"%@ = %@", stringA, stringB);
            break;
    }
}
複製代碼

獲得的結果是:blog

1.2.3 > 1.1.12
1.8 > 1.7.2.3.55
1.44 > 1.5
7.4.1 < 7.5
複製代碼

看上去結果都是正確的,那麼看來 NSNumericSearch 並非粗暴的去掉全部非數字字符後進行數值比對。

結合原回答裏答主說的一句話:keeping in mind that "1" < "1.0" < "1.0.0",突然想到了一些什麼,繼續進行下一步的實驗,獲得的結果以下:

1 < 1.0
1.0 < 1.0.0
a10 < b2
a2 < b10
c10 > b2
c2 > b10
2a < 10b
10a > 2b
2c < 10b
10c > 2b
複製代碼

你們看到這裏應該能夠猜到官方文檔的意思是什麼了,其實文檔的意思是整個字串非數字的部分仍然進行常規的字符比較邏輯,只有在遇到數字的時候會把連續的數字轉換成數值再進行比對。具體的比較過程示例參照下圖:

28-A

這時候一些奇特的比對結果就能夠解釋明白了,好比使用這種比較模式會得出 "01" = "1"

繼續深刻

那麼還剩下一個問題,若是和數字字符比較的是非數字字符,會怎麼樣?咱們能夠挑一些 ASCII 碼在數字字符周圍的字符進行試驗,結果以下:

a/c < a100c
a:c > a100c
a/c < a1c
a:c > a1c
複製代碼

注意這裏出現的部分字符 ASCII 碼爲:

/ = 47
0 = 48
1 = 49
...
9 = 57
: = 58
複製代碼

能夠看出現了比較的字符一邊是數字,一邊是非數字時,是按照常規的 ASCII 碼進行比對的。

總結

那麼分析到這裏就基本結束了,剩下的一些場景類推一下都很容易理解。

其實在各大 OS 裏的文件系統下文件排序用的就是這種比較方法,一開始沒有想到這點因此理解上繞了一些彎路。

NSNumericSearch 來進行版本字符串的比對也是十分有效的,不是特殊須要的話就不再用傻傻的本身分割字符串再分段比較啦。

相關文章
相關標籤/搜索