某些應用程式,例如 Microsoft Active Directory、Microsoft Exchange 和 Microsoft Access,會維護依名稱索引的地區設定和語言字串排序資料庫(UTF-16 字串),以及其相關聯的排序權數。
排序 通常對使用者來說是符合其地區習慣的直覺操作。 不過,這對於應用程式開發人員來說可能不好理解。 本主題討論在應用程式中處理排序的考慮。 排序可以是語言學或序數(非語言學)。
排序函式
您可以在應用程式中使用各種排序函式:
- NLS 字串比較函式。 範例包括 CompareString 和 CompareStringEx、CompareStringOrdinal、LCMapString、LCMapStringEx、FindNLSString、FindNLSStringEx,以及 FindStringOrdinal。 如需與字串比較函式相關的安全性問題的討論,請參閱 安全性考慮:國際功能。
- 內部呼叫字串比較函式的包裝函式。 最常見的函式是 lstrcmp 和 lstrcmpi,其會呼叫 compareString。
排序函式通常會依字元評估字串。 不過,許多語言都有多個字元元素,例如傳統西班牙文中的雙字元組 “CH”。 CompareString 和 CompareStringEx 使用應用程式提供的地區設定識別符或名稱來識別多字元元素。 相反地,lstrcmp和 lstrcmpi 使用使用者的地區設定。
另一個範例是越南文,其中包含許多雙字元素,例如字母 “GI” 所有效的大寫、標題大小寫和小寫形式,分別是 “GI”、“Gi” 和 “gi”。 這些形式中的任何一種都會被視為單一排序元素,如果忽略大小寫,則會比較為相等。 不過,因為 「gI」 無效為單一元素,CompareString、CompareStringEx、lstrcmp,lstrcmpi 將 “gI” 視為兩個不同的元素。
函式 CompareString、CompareStringEx、lstrcmp、lstrcmpi、LCMapString、LCMapStringEx、FindNLSString和 FindNLSStringEx 默認都會使用「單字排序」技術。 針對這種類型的排序,除了連字元和單引號之外,所有標點符號和其他非字母數字字符都會出現在任何字母或數字字符之前。 連字符號和單引號的處理方式與其他非字母數字符號不同,以確保像 "coop" 和 "co-op" 這樣的單字會保留在已排序的清單中。
應用程式可以藉由指定SORT_STRINGSORT旗標,從排序函式要求「字串排序」技術,而不是單字排序。 字串排序會將連字號和撇號視為其他非字母數字字元。 它們在排序序列中的位置位於英數位元之前。
下表比較文字排序的結果與字串排序的結果。
| 文字排序 | 字串排序 |
|---|---|
| 鋼坯 | 帳單的 |
| 帳單 | 鋼坯 |
| 帳單的 | 條例 草案 |
| 不能 | 不能 |
| 斜面 | 無法 |
| 不能 | 斜面 |
| con | co-op |
| 庫 珀 | con |
| co-op | 庫 珀 |
以語言方式排序字串
CompareString 和 CompareStringEx 功能檢測語言相等性。 您的應用程式應該使用這些函式搭配正確的地區設定,以語言方式排序字串。
注意
為了與 Unicode 相容,應用程式應該偏好 CompareStringEx 或 CompareString的 Unicode 版本。 偏好 CompareStringEx 的另一個原因是,Microsoft會移轉至使用地區設定名稱,而不是針對新的地區設定使用地區設定標識符,基於互作性考慮。 只有在 Windows Vista 和更新版本上執行的任何應用程式都應該使用 CompareStringEx。
另一種語言相等測試方式是使用 lstrcmp 或 lstrcmpi,一律使用單字排序。 lstrcmpi 函式會呼叫 compareString與 NORM_IGNORECASE 旗標CompareString,而 lstrcmp 呼叫該旗標。 如需使用包裝函式的概觀,請參閱 Strings。
功能會取得所有語言地區的適當結果。 不同地區設定的使用者期望在排序行為上可能會有很大的差異,如下列範例所示。
- 許多語言區域將字元合字 "æ" 視為與字母組合 "ae" 相同。 然而,冰島人(冰島)認為它是一個不同的字母,並將它放在Z之後的排序順序。
- A 後帶環(Å)通常只與 A 有變音符號的不同。然而,在用於瑞典語排序時,Å 位於 Z 之後。
函式會嘗試嚴格驗證 Unicode 標準中定義的代碼點是否與等價代碼點的字串在規範上相等。 例如,代表帶有變音符號的小寫「u」(ü)的程式代碼點等同於組合後的小寫「u」和變音符號(¨)。 不過,請注意,標準等價並不總是可能的。
由於幾乎所有使用 Windows 鍵盤和輸入法編輯器輸入的數據都符合 Unicode 標準中所定義的 C 正規化形式,所以使用 NLS Unicode 正規化函式轉換來自其他平台的數據,可提供最一致的結果,特別是針對使用藏文腳本或新式 Hangul 腳本的地區設定。 如需 Windows Vista 和更新版本中 Unicode 正規化支援的詳細資訊,請參閱 使用 Unicode 正規化來表示字串。
當字串比較遵循使用者的語言喜好設定時,例如,排列 ListView 控制項的專案時,應用程式可以執行下列其中一項動作:
- 使用使用者的地區設定,呼叫 lstrcmp 或者呼叫 lstrcmpi 。
- 呼叫 CompareString 或 CompareStringEx,以定義比較的地區設定、傳遞其他旗標、內嵌 Null 字元,或傳遞明確的長度以符合字符串的部分。
例如,無論地區設定為何,比較結果應該一致,例如,將擷取的數據與預先定義的清單或內部值進行比較時,應用程式應該使用 CompareString 或 CompareStringEx,並將 Locale 參數設定為 LOCALE_INVARIANT。 針對 CompareString,即使 mystr 為 “INLAP”,下列其中一個呼叫也會相符。 在此情況下,如果目前的地區設定是越南語,則對 lstrcmpi 的地區敏感呼叫將會失敗。
在 Windows XP 上:
int iReturn = CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, mystr, -1, _T("InLap"), -1);
在過去的作業系統上:
DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
int iReturn = CompareString(lcid, NORM_IGNORECASE, mystr, -1, _T("InLap"), -1);
以序數排序字串
針對序數(非語言)排序,您的應用程式應該一律使用 CompareStringOrdinal 函式。
注意
此函式僅適用於 Windows Vista 和更新版本。
CompareStringOrdinal 會比較兩個 Unicode 字串來測試二元相等,而不是語言相等。 這類非語言字串的範例包括NTFS檔名、環境變數,以及 mutex、命名管道或mailslots的名稱。 除了不區分大小寫的選項之外,此函式會忽略所有非二進位等價。 與其他排序函式不同的是,它會測試所有程式代碼點是否相等,包括語言排序配置中未指定任何權數的程式代碼點。
下列所有語句都適用於二進位比較 CompareStringOrdinal,但不適用於 CompareString、CompareStringEx、lstrcmp或 lstrcmpi。
- 在 Unicode 中,正規化等效序列,例如 LATIN SMALL LETTER A WITH RING ABOVE (U+00e5) 和 LATIN SMALL LETTER A + COMBINING RING ABOVE (U+0061 U+030a),即便它們看起來一樣(“å”),也不相等。
- Unicode 中的正規等效類似字串,例如拉丁字母小型大寫 Y(U+028f)和拉丁大寫字母 Y(U+0059),看起來非常相似(“ʏ”和 “Y”),僅在語言表中的一些特定案例權重上有所不同,但仍被視為完全不同的字元。 即使應用程式將 bIgnoreCase 設定為 TRUE,這些字串也會比較為不同。
- 已定義但沒有語言排序權重的碼位,例如零寬度連接符(U+200d),會被視為具有其碼位權重。
- 在 Unicode 的較新版本中定義但在當前語言數據表中沒有權重的碼點,會被視為其碼點本身的權重。
- Unicode 未定義的碼位會被視為擁有其自身的碼位權重。
- 當應用程式將 bIgnoreCase 設定為 TRUE時,函式會使用作業系統大寫數據表來映射大小寫,而不是語言排序表中的資訊。 因此,映射與地區設定無關。
如需 Unicode 中標準對等序列和 Unicode 中類似字串的詳細資訊,請參閱 使用 Unicode 正規化來表示字串。
排序代碼點
某些 Unicode 字碼點沒有權重,例如零寬度非聯結器、U+200c。 排序函式會刻意將無重量碼點評估為等同,因為在排序過程中它們沒有權重。 在 Windows Vista 和更新版本上,應用程式可以呼叫 NLS 字串比較函式來排序這些程式代碼點,特別是 CompareStringOrdinal,以評估常值、二進位感中的所有程式代碼點,例如在密碼驗證中。 在 Windows Vista 前作系統上,應用程式應該使用 C 執行時間函式,strcmp 或 wcscmp。
當應用程式指定 hlink_NONSPACE 旗標時,排序功能會忽略帶有變音符號的字母,例如 NON SPACING BREVE、U+0306。 同樣地,當指定hlink_SYMBOLS旗標時,這些函式會忽略符號,例如 EQUALS SIGN、U+003d。 在 Windows Vista 和更新版本上,應用程式會呼叫 CompareStringOrdinal,以字面上和二進位層面上評估發音符號和符號碼點。 在更早版本的 Windows 作業系統上,應用程式應該使用 strcmp 或 wcscmp。
某些碼位,例如0xFFFF和0x058b,目前未在 Unicode 中指派。 這些程式代碼點不會在排序中收到任何權數,而且不應該傳遞至排序函式。 應用程式應該使用 IsNLSDefinedString 來偵測數據流中的非 Unicode 字碼點。
注意
IsNLSDefinedString 的結果可能會因為某個字元在更新版本中被新增至 Unicode,隨後也被加入 Windows 排序資料表,而隨所使用的 Unicode 版本有所不同。 如需詳細資訊,請參閱 使用排序版本設定。
將數字按數值排序
在 Windows 7 和更新版本上,應用程式可以使用 SORT_DIGITSASNUMBERS 旗標呼叫 CompareString、CompareStringEx、LCMapString或 LCMapStringEx。 此旗標支援將數位視為數位的排序,例如,在 「10」 之前排序 「2」。
請注意,此旗標的使用不適用於十六進位數位,如下所示。
- 01AF
1BCD
002A
12FA
AB1C
AB02
AB12
在此情況下,「數位」會依序排序,但使用者會察覺到排序不佳的十六進位清單。
對應字串
如果未指定LCMAP_SORTKEY,應用程式會使用 LCMapString 或 LCMapStringEx 函式來對應字元串。 如果來源字串為 Null 終止,則對應的字串會以 Null 結束。
在大寫和小寫之間轉換時,函數不保證單一字元會對應至單一字元。 例如,LCMAP_LOWERCASE 和 LCMAP_UPPERCASE 標誌可能會將德國「夏普 S」字母(“ß”)對應至本身。 或者,LCMAP_UPPERCASE旗標可能會將 “ß” 對應至 “SS”,而LCMAP_LOWERCASE旗標可能會將 “SS” 對應至 “ß”。 行為取決於 NLS 版本。
在大寫和小寫之間轉換時,函式不會區分內容。 例如,雖然 LCMAP_UPPERCASE 標誌會正確地將希臘小寫希格瑪(「σ」)和希臘小寫最末希格瑪(「ς」)對應到希臘大寫希格瑪(「Σ」),LCMAP_LOWERCASE 標誌則總是將「Σ」對應到「σ」,永遠不會對應到「ς」。
根據預設,函式會將小寫 「i」 對應至大寫 「I」,即使 Locale 參數指定土耳其文或亞塞拜然文。 若要覆寫土耳其或亞塞拜然語的此行為,應用程式應指定使用 LCMAP_LINGUISTIC_CASING。 如果使用適當的地區設定指定此旗標,「ı」(小寫無點 I)是「I」(大寫無點 I)的對應小寫形式,而「i」(小寫有點 I)是「İ」(大寫有點 I)的對應小寫形式。
如果指定LCMAP_HIRAGANA旗標將片假名字符對應至平假名字符,且未指定LCMAP_FULLWIDTH,LCMapString 或 LCMapStringEx 只會將全角字元對應至平假名。 在此情況下,任何半角片假名字符都放在目的地字串中,且不會對應到平假名。 應用程式必須指定「LCMAP_FULLWIDTH」,以便將半形片假名字符對應至平假名。 此限制的原因是所有平假名字符都是全角字元。
如果應用程式需要從來源字串中移除字元,則可以設置 NORM_IGNORESYMBOLS 和 NORM_IGNORENONSPACE 旗標,並清除所有其他旗標,以此方式來呼叫對應函式。 如果應用程式以非 Null 終止的來源字串執行此作業,則函式可能會傳回空字串,而不會傳回錯誤。
建立排序鍵
當應用程式指定LCMAP_SORTKEY時,LCMapString 或 LCMapStringEx 產生排序索引鍵,這是位元組值的二進位數組。 排序索引鍵不是 true 字串,其值代表來源字串的排序行為,但不是有意義的顯示值。
注意
函式在產生排序索引鍵時不考慮阿拉伯文中的 kashida。 如果應用程式呼叫 函式來建立包含阿拉伯文 kashida 之字串的排序索引鍵,則此函式不會建立排序索引鍵值。
排序索引鍵可以包含奇數位節數。 LCMAP_BYTEREV旗標只會反轉偶數個字節。 排序鍵中的最後一個字節(位於奇數位置)不會被反轉。 如果終止0x00字节是奇數位置的字节,它會保留在排序鍵中的最後一個字节。 如果終止的0x00位元組是偶數位置的位元組,則會與之前的位元組交換位置。
產生排序索引鍵時,函式會以不同於其他標點符號的方式處理連字元和單引號,讓 “coop” 和 “co-op” 等字組保持在清單中。 除連字號和單引號外,所有標點符號在英數字元之前排序。 應用程式可以藉由設定SORT_STRINGSORT旗標來變更此行為,如排序函式 中所述。
在 memcmp中使用時,排序索引鍵會產生與使用原始字串在 CompareString 或 CompareStringEx中時相同的順序。 應該使用 memcmp 函數,而不是 strcmp,因為排序鍵可以有內嵌的 null 位元組。
使用排序版本控制
排序 數據表有兩個數位,可識別其版本:定義的版本和 NLS 版本。 這兩個數位都是 DWORD 值,由主要值和次要值組成。 保留值的第一個字節、接下來的兩個字節代表主要版本,最後一個字節代表次要版本。 在十六進位詞彙中,模式為 0xRRMMMMmm,其中 R 等於 Reserved、M 等於 major 和 m 等於 minor。 例如,具有次要版本 4 的主要版本 3 會表示為 0x304。
定義的版本會定義代碼點的集合,並且在所有地區設定中都是相同的。 主要版本會遞增,以表示現有程式代碼點的變更。 次要版本會遞增,表示已新增碼位,但之前已有的碼位並未更改。
NLS 版本專屬於 地區設定標識子 或 地區設定名稱,並追蹤受影響地區設定之程式代碼點權數的變更。 主要版本會在已可排序的代碼點變更權重時遞增。 當指派新的程式代碼點權數時,次要版本會遞增,但所有其他先前可排序的程式代碼點權數都會保持不變。
注意
針對主要版本,會變更一或多個代碼點,讓應用程式必須重新索引所有數據,以確保比較有效。 若為次要版本,則不會移動任何碼位,只會新增一些碼位。 針對這種類型的版本,應用程式只需要重新編製具有先前無法排序值的字串索引。
重要
主要版本已在 Windows 8 中變更。 在舊版 Windows 下建立的數據必須重新編製索引。
已定義和 NLS 版本都適用於使用 LCMapString 撷取的可排序程式代碼點,或 搭配 LCMAP_SORTKEY 旗標的 LCMapStringEx 函式, 和 也由 CompareString、CompareStringEx、FindNLSString和 FindNLSStringEx 函式使用。 如果字串中的一或多個代碼點無法排序,則當該字串作為參數被傳遞到 IsNLSDefinedString 函式時,會傳回 FALSE。
應用程式可以呼叫 GetNLSVersion 或 GetNLSVersionEx 來擷取排序數據表的已定義版本和 NLS 版本。
為資料庫編製索引
基於效能考慮,應用程式在編製資料庫索引時應該遵循此程式。
若要正確編製資料庫索引
- 針對每個函式,儲存 NLS 版本、該版本的排序索引鍵,以及每個索引字串的排序能力指示。
- 當次要版本遞增時,重新索引先前無法排序的字串。 在此更新中,受影響的字串應僅限於其先前由 IsNLSDefinedString 傳回 FALSE的字串。
- 當主要版本遞增時,重新編製所有字串的索引,因為更新的權數可能會變更任何字串的行為。 主要版本發行非常罕見。
資料庫索引編製問題可能會因為下列原因而發生:
- 較新的作業系統可以定義較舊作業系統未定義的碼位,從而改變排序。
- 程式碼點在不同的作業系統中可能會有不同的排序權重,這是由於語言支援中的修正。
若要將這些情況下重新編製資料庫索引的必要性降到最低,應用程式可以使用 IsNLSDefinedString 來區分定義的字串與未定義的字串,讓應用程式可以拒絕具有未定義字碼點的字串。 使用 GetNLSVersion 或 GetNLSVersionEx 可讓應用程式判斷 NLS 變更是否會影響特定索引數據表所使用的地區設定。 如果變更對地區設定沒有任何影響,應用程式就不需要重新編製數據表的索引。
例子
下表說明與排序函式搭配使用之特定旗標的效果。 在每個案例中,旗標的選取會判斷兩個不同的字元是否被視為等於排序目的。
| 角色 1 | 字元 2 | 預設 | NORM_IGNOREWIDTH | NORM_IGNOREKANA | NORM_IGNOREWIDTH|NORMIGNOREKANA |
|---|---|---|---|---|---|
| "あ" U+3042 平假名字母 A |
"ガ" U+30A2 片假名字母 A |
不等 | 不平等 | 平等 | 相等 |
| "オ" U+FF75 半角片假名字母「O」 |
"オ" U+30AA 片假名字母 O |
不等 | 平等 | 不等 | 平等 |
| “B” U+FF22 FULLWIDTH 拉丁大寫字母 B |
“B” U+0042 拉丁大寫字母 B |
不等 | 平等 | 不等 | 平等 |
相關主題