歡迎你們前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~html
咱們知道計算機是不能直接處理文本的,而是和數字打交道。所以,爲了表示文本,就創建了一個字符到數字的映射表,叫作編碼。最著名的字符編碼就是ASCII了,它使用7-bit來表示應用字母表以及數字和其餘字符。這對於英語來講是夠用了,可是對於其餘語言,這個7-bit就不能知足條件了,由於字符遠遠超過了7-bit所能表示的最大個數。所以1987年,來自幾個大的科技公司的工程師開始合做開發一種致力於能在全世界的全部書寫系統中都能通用的字符編碼系統,並與1991年10發佈了Unicode的1.0.0標準。2018年6月發佈了Unicode的11.0版本。這裏就再也不對Unicode作過多的介紹,值得注意的是,在iOS開發中,常使用的的NSString是基於Unicode-16來開發的,這是由於當時開發這個的時候Unicode標準仍是以16bit固定長度來編碼,這就致使使用上的一些坑,建議你們閱讀下這篇文章:NSString and Unicode。github
#UCA和CLDR:最經常使用到的排序標準web
介紹完Unicode編碼以後,咱們就能夠來介紹UCA(Unicode Collation Algorithm)和CLDR(Common Locale Data Repository)了,由於蘋果的NSString的介紹文檔裏有這麼一句話:算法
Localized string comparisons are based on the Unicode Collation Algorithm, as tailored for different languages by CLDR (Common Locale Data Repository). Both are projects of the Unicode Consortium. Unicode is a registered trademark of Unicode, Inc.
複製代碼
說白了,蘋果系統的NSString字符串排序是基於UCA的,而且在不一樣語言下,通過CLDR來裁剪的。swift
UCA的介紹官方文檔介紹在這裏:UCA介紹。其中第一句話就寫的很清楚,app
Collation is the general term for the process and function of determining the sorting order of strings of characters. 複製代碼
對字符串排序的過程就是Collation,UCA就是Unicode表示的字符串進行排序的規則,制定這個規則的緣由是不一樣語種對字符串的排序規則要求是不同的,好比,德國、法國和瑞士對相同的字符排序的規則是不同的,甚至在同一個語言下好比中文,多音字這種在不一樣組合裏,排序的前後順序也是不同的。機器學習
所以能夠想象,UCA指定的規則比較複雜。感興趣的能夠讀下前面貼的UCA介紹,裏面有具體的排序規則介紹。學習
CLDR的官方文檔在這裏:CLDR介紹。CLDR是一堆語言數據倉庫,爲軟件提供各類世界語言版本提供了基礎,目前在使用CLDR的公司有:ui
Apple (macOS, iOS, watchOS, tvOS, and several applications; Apple Mobile Device Support and iTunes for Windows; …) Google (Web Search, Chrome, Android, Adwords, Google+, Google Maps, Blogger, Google Analytics, …) IBM (DB2, Lotus, Websphere, Tivoli, Rational, AIX, i/OS, z/OS,…) Microsoft (Windows, Office, Visual Studio, …)
其餘公司:
ABAS Software, Adobe, Amazon (Kindle), Amdocs, Apache, Appian, Argonne National Laboratory, Avaya, Babel (Pocoo library), BAE Systems Geospatial eXploitation Products, BEA, BluePhoenix Solutions, BMC Software, Boost, BroadJump, Business Objects, caris, CERN, Debian Linux, Dell, Eclipse, eBay, EMC Corporation, ESRI, Firebird RDBMS, FreeBSD, Gentoo Linux, GroundWork Open Source, GTK+, Harman/Becker Automotive Systems GmbH, HP, Hyperion, Inktomi, Innodata Isogen, Informatica, Intel, Interlogics, IONA, IXOS, Jikes, jQuery, Library of Congress, Mathworks, Mozilla, Netezza, OpenOffice, Oracle (Solaris, Java), Lawson Software, Leica Geosystems GIS & Mapping LLC, Mandrake Linux, OCLC, Perl, Progress Software, Python, QNX, Rogue Wave, SAP, Shutterstock, SIL, SPSS, Software AG, SuSE, Symantec, Teradata (NCR), ToolAware, Trend Micro, Twitter, Virage, webMethods, Wikimedia Foundation (Wikipedia), Wine, WMS Gaming, XyEnterprise, Yahoo!, Yelp
對於不一樣區域(local),能夠找到不一樣的數據CLDR,結合UCA對字符串進行排序,就作到了不一樣語言下的本地化排序。能夠去 cldr.unicode.org/ 下載最新的CLDR庫,後面將會用到裏面的一些內容。
Unicode把全部的字符分爲兩類:
一般狀況下,咱們是經過unicode 的UTF-16碼點值逐個進行比較大小的來進行排序的。
NSArray *rawArray = @[@"愛你", @"一輩子一世",@"㊀", @"上",@"㊤",@"μ",@"язык",@"..",@"123",@"@",@"AA",@"abc",@"abb"];
//1. 默認排序方式
NSArray *defaultedSortedArray = [rawArray sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
return [obj1 compare:obj2 options:NSCaseInsensitiveSearch];
}];
__block NSMutableArray *codeUnits = [NSMutableArray array];
[defaultedSortedArray enumerateObjectsUsingBlock:^(NSString* _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[codeUnits addObject:@([obj characterAtIndex:0])];
}];
NSLog(@"默認Unicode碼點值排序 %@ 對應的各個字符串的首字符碼點值是 %@", [defaultedSortedArray descriptionWithLocale:cnLocal], codeUnits);
複製代碼
輸出結果是
排序結果
.. 123 @ AA abb abc μ язык ㊀ ㊤ 一輩子一世 上 愛你
對應的各個字符串的首字符碼點值是
46 49 64 65 97 97 956 1103 12928 12964 19968 19978 29233
複製代碼
咱們經常使用的各類字符的碼點值範圍是:
在咱們前面下載的文件CLDR庫有個/common/uca/allkeys_CLDR.txt文件,它表示咱們指定locale爲「en」或者說是默認的排序規則。它的格式是
0000 ; [.0000.0000.0000] # <NULL>
0001 ; [.0000.0000.0000] # <START OF HEADING>
0002 ; [.0000.0000.0000] # <START OF TEXT>
0003 ; [.0000.0000.0000] # <END OF TEXT>
複製代碼
分號前的值表示碼點,分號後中括號裏面的值表示UCA算法權重,用.
號來區分,Unicode字符就是按照這個規則從上到下排序。
NSLocale *enLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en"];
defaultedSortedArray = [rawArray sortedArrayUsingComparator:^NSComparisonResult(NSString* _Nonnull obj1, NSString* _Nonnull obj2) {
return [obj1 compare:obj2 options:0 range:NSMakeRange(0, obj1.length) locale:enLocale];
}];
NSLog(@"默認排序規則或者指定地區爲locale後的排序結果是 %@", [defaultedSortedArray descriptionWithLocale:cnLocal]);
複製代碼
排序結果是
默認排序規則或者指定地區爲en後的排序結果是
.. (ch (en @ 123 AA abb abc μ язык ㊀ 一輩子一世 上 ㊤ 愛你
複製代碼
這種排序依次爲符號,數字,英文/漢字等script charaters。
在下載的CLDR文件中,有個common/bcp47/collation.xml文件,列出了可選的排序方式,有standard,pinyin, stroke(筆畫排序)等。
那如何肯定各個區域語言下,該使用哪一種排序規則呢,咱們能夠看到common/collation/文件夾下,有不少標記語言LDML文件,這些文件就是表示在不一樣區域語言下,採用的排序規則。
咱們打開zh.xml,這個就是咱們簡體中文的排序規則,能夠看到,裏面默認採用的排序是pinyin排序,而且在開頭還寫了各個聲調字母的排序前後順序。
假如咱們指定區域爲zh_CN,則對於字符串中出現的中文則排在其餘語言字符串前面。其餘script charater則按照allkeys_CLDR.txt的順序進行進行排序。值得注意的是,中文因爲多音字,在這裏不必定可以徹底按照咱們的習慣排序正確,好比「重逢(chong feng)」就沒有第一個拼音chong去排,而是按照zhong來排列的。
默認排序規則或者指定地區爲zh_CN後的排序結果是
.. (ch (en @ 0124 123 艾你 愛你 産 上 ㊤ ㊀ 一輩子一世 重逢 重要 aa AA abb μ язык
默認排序規則或者指定地區爲ru_CN後的排序結果是
.. (ch (en @ 0124 123 язык aa AA abb μ ㊀ 一輩子一世 上 ㊤ 愛你 産 艾你 重要 重逢
複製代碼
至此,咱們大體講清楚了幾種排序規則。
前面咱們已經說了,蘋果系統的NSString排序是UCA和CLDR規則的。NSString提供了不少的排序方法,但最終,全部的都是調用了compare:options:range:locale:
來進行處理,只是傳入的參數不一樣。能夠在NSString.swift 中查看具體的實現。這麼多排序方法中,其中之一是localizedStandardCompare:
, 這個方法是蘋果系統推薦的,在給用戶展現的列表數據的名字或者其餘字符串進行排序時所使用的方法。咱們看到,它的內部實現是
public func localizedStandardCompare(_ string: String) -> ComparisonResult {
return compare(string, options: [.caseInsensitive, .numeric, .widthInsensitive, .forcedOrdering], range: NSRange(location: 0, length: length), locale: Locale.current._bridgeToObjectiveC())
}
複製代碼
其中用到的四個Options參數是
NSCaseInsensitiveSearch //大小寫不敏感
NSNumericSearch //對字符串中出現的數字字符進行數字化的大小比較,好比Foo2.txt < Foo7.txt < Foo25.txt
NSWidthInsensitiveSearch //忽略寬度,按照實際表示的意思來對比,如'a' = UFF41
NSForcedOrderingSearch //強制返回Ascending或者Descending,和NSCaseInsensitiveSearch結合起來就是例如"aaa" > "AAA"
複製代碼
而且指定了當前的區域locale做爲參數,這就至關於指定使用CLDR進行排序,若是是在手機上,這個方法的調用和系統當前的區域設置是有很大關係的,這和咱們代碼中設置locale是一個道理。咱們能夠這樣理解,調用這個方法獲得的結果和在iOS Files中文件名選擇按照名稱排序獲得的結果是同樣的。在iOS中,當咱們的區域設置爲中國時,排序順序就是 標點符號等特殊符號>數字>中文>英文等其餘
。
自此,對localizedStandardCompare:
的使用,你們應該比較清楚了。
這裏單獨把數字字符串的比較列出來,是由於一些人對這裏比較迷惑。因爲localizedStandardCompare:
中有使用NSNumericSearch
選項,這裏簡單來講,就是假如目前兩個字符串是相等的,二者都出現了數字,則分別從二者種取出這段數字進行數字化來比較大小,按照數字大小排序。爲了驗證這裏的邏輯,我看了下CFString.c中CFStringCompareWithOptionsAndLocale
這個方法的實現,這個就是compare
實際調用的的比較方法。其中關於數字大小比較的代碼以下:
if (numerically && ((0 == strBuf1Len) && (str1Char <= '9') && (str1Char >= '0')) && ((0 == strBuf2Len) && (str2Char <= '9') && (str2Char >= '0'))) { // If both are not ASCII digits, then don't do numerical comparison here
uint64_t intValue1 = 0, intValue2 = 0; // !!! Doesn't work if numbers are > max uint64_t
CFIndex str1NumRangeIndex = str1Index;
CFIndex str2NumRangeIndex = str2Index;
do {
intValue1 = (intValue1 * 10) + (str1Char - '0');
str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, ++str1Index);
} while ((str1Char <= '9') && (str1Char >= '0'));
do {
intValue2 = intValue2 * 10 + (str2Char - '0');
str2Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, ++str2Index);
} while ((str2Char <= '9') && (str2Char >= '0'));
if (intValue1 == intValue2) {
if (forceOrdering && (kCFCompareEqualTo == compareResult) && ((str1Index - str1NumRangeIndex) != (str2Index - str2NumRangeIndex))) {
compareResult = (((str1Index - str1NumRangeIndex) < (str2Index - str2NumRangeIndex)) ? kCFCompareLessThan : kCFCompareGreaterThan);
numericEquivalence = true;
forcedIndex1 = str1NumRangeIndex;
forcedIndex2 = str2NumRangeIndex;
}
continue;
} else if (intValue1 < intValue2) {
if (freeLocale && locale) {
CFRelease(locale);
}
return kCFCompareLessThan;
} else {
if (freeLocale && locale) {
CFRelease(locale);
}
return kCFCompareGreaterThan;
}
}
複製代碼
這段代碼的含義就是,若是兩個字符串都是以數字開始(也多是字符串前面都相等,當前從數字部分開始比較),則取出兩個字符串的數字,按照數字大小進行對比。若是數字可以比較出大小,則直接返回兩個字符串的大小關係,再也不對後面的字符串進行對比。好比「0123aaa」 和「1bbbbbbbbb」,就直接返回「0123aaa」大於「1bbbbbbbbb」。固然,這裏取出的數字可能超出了uint64_t表示的最大值,可是這種機率很低,在咱們的名稱排序中,很難遇到這麼長的數字進行比較的。明白這個規則後,你們對字符串中出現的數字在進行排序時應該比較理解了。下面的名字排序是對着的。
本文主要講述由localizedStandardCompare:
這個蘋果系統方法所引起的對排序規則的深刻研究,簡單來講,設置中選擇區域爲中國時,排序順序爲 標點符號等特殊符號>數字>中文>英文等其餘
。中文自己是按照pinyin排序的,只是因爲多音字的關係,不可以作到100%按照中文習慣來排序,會有些沒法正確排序的問題,但大致已經符合咱們的習慣了。
developer.apple.com/library/arc…
raw.githubusercontent.com/larvit/larv…
此文已由做者受權騰訊雲+社區發佈,更多原文請點擊
搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!
海量技術實踐經驗,盡在雲加社區!