Udostępnij za pośrednictwem


Obsługa sortowania w aplikacjach

Niektóre aplikacje, takie jak Microsoft Active Directory, Microsoft Exchange i Microsoft Access, utrzymują sortowaną bazę danych ciągów regionalnych i językowych indeksowanych według nazwy (ciąg UTF-16) i skojarzonych z nimi wag sortowania.

Sortowanie jest zwykle intuicyjne dla użytkowników w ich własnych ustawieniach regionalnych. Jednak może to nie być intuicyjne dla deweloperów aplikacji. W tym temacie omówiono zagadnienia dotyczące obsługi sortowania w aplikacjach. Sortowanie może być lingwistyczną lub porządkową (nielingwistyczną).

Funkcje sortowania

W aplikacjach można używać różnych funkcji sortowania:

Zazwyczaj funkcje sortowania oceniają ciągi znak po znaku. Jednak wiele języków ma wieloznakowe elementy, takie jak dwuznak "CH" w tradycyjnym hiszpańskim. CompareString i CompareStringEx używają identyfikatora ustawień regionalnych lub nazwy dostarczonej przez aplikację, aby zidentyfikować elementy wieloznakowe. Natomiast lstrcmporaz lstrcmpi używają ustawień regionalnych użytkownika.

Innym przykładem jest wietnamski, który zawiera wiele dwuznakowych elementów, takich jak prawidłowe formy wielkich liter, tytułowych i małych liter "GI", które to są odpowiednio "GI", "Gi" i "gi". Każdy z tych formularzy jest traktowany jako pojedynczy element sortowania, a jeśli wielkość liter jest ignorowana, porównuje się jako równe. Jednak ponieważ "gI" jest nieprawidłowy jako pojedynczy element, CompareString, CompareStringEx, lstrcmp, i lstrcmpi traktują "gI" jako dwa oddzielne elementy.

Funkcje CompareString, CompareStringEx, lstrcmp, lstrcmpi, LCMapString, LCMapStringEx, FindNLSStringi FindNLSStringEx domyślnie używają techniki "sortowania wyrazów". W przypadku tego typu znaki interpunkcyjne i inne znaki niefanumeryczne, z wyjątkiem łącznika i apostrofu, są przed dowolnym znakiem alfanumerycznym. Łącznik i apostrof są traktowane inaczej niż inne znaki nieliczbowe, aby upewnić się, że wyrazy takie jak "coop" i "co-op" pozostają razem na posortowanej liście.

Zamiast sortowania wyrazów aplikacja może zażądać techniki sortowania ciągów z funkcji sortowania, określając flagę SORT_STRINGSORT. Sortowanie ciągów traktuje łącznik i apostrof tak samo jak każdy inny znak nienumeryczny. Ich pozycje w sekwencji sortowania są przed znakami alfanumerycznymi.

Poniższa tabela porównuje wyniki sortowania wyrazów z wynikami sortowania ciągów.

Sortowanie wyrazów Sortowanie ciągów
Pręt Billa
Rachunki Wlewki
rachunek Rachunki
nie może nie może
hipokryzja nie może
nie może żargon
oszustwo co-op
kurnik oszukać
co-op kurnik

 

Sortuj ciągi lingwistyczne

Test funkcji CompareString i CompareStringEx pod kątem równości językowej. Aplikacje powinny używać tych funkcji z poprawnym ustawieniem regionalnym do lingwistycznego sortowania ciągów.

Notatka

Aby uzyskać zgodność z unicode, aplikacja powinna preferować CompareStringEx lub wersję Unicode CompareString. Innym powodem preferowania CompareStringEx jest to, że firma Microsoft migruje do używania nazw ustawień regionalnych zamiast identyfikatorów ustawień regionalnych dla nowych ustawień regionalnych ze względów współdziałania. Każda aplikacja działająca tylko w systemie Windows Vista i nowszych powinna używać CompareStringEx.

 

Innym sposobem testowania równości językowej jest użycie lstrcmp lub lstrcmpi, które zawsze używają sortowania wyrazów. Funkcja lstrcmpi wywołuje CompareString z flagą NORM_IGNORECASE, podczas gdy lstrcmp wywołuje ją bez tej flagi. Aby zapoznać się z omówieniem korzystania z funkcji opakowujących, zobacz Strings.

Funkcje pobierają językowo odpowiednie wyniki dla wszystkich lokalizacji. Oczekiwania użytkowników dotyczące różnych ustawień regionalnych mogą się znacznie różnić w zachowaniu sortowania, jak pokazano w poniższych przykładach.

  • Wiele lokalizacji utożsamia ligaturę ae (æ) z literami ae. Jednak Islandia uważa ją za oddzielną literę i umieszcza po Z w sekwencji sortowania.
  • Litera Å zwykle jest sortowana jako A z jedynie różnicą diakrytyczną. Jednak język szwedzki (Szwecja) umieszcza literę Å po Z w sekwencji sortowania.

Funkcje próbują zweryfikować rygorystycznie, że punkty kodu zdefiniowane w standardzie Unicode są kanonicznie równe ciągowi równoważnych punktów kodu. Na przykład punkt kodu reprezentujący małą literę "u" z dieresis (ü) jest kanonicznie równy małej literze "u" w połączeniu z dieresis (¨). Należy jednak pamiętać, że równoważność kanoniczna nie zawsze jest możliwa.

Ponieważ prawie wszystkie dane wprowadzone przy użyciu klawiatur systemu Windows i edytorów metod wejściowych (IME) są zgodne z formą normalizacji języka C zdefiniowaną w standardzie Unicode, konwertowanie danych przychodzących z innych platform przy użyciu funkcji normalizacji UNICODE NLS zapewnia najbardziej spójne wyniki, zwłaszcza dla ustawień regionalnych korzystających ze skryptu tybetańskiego lub skryptu Hangul dla nowoczesnego Hangul. Aby uzyskać więcej informacji na temat obsługi normalizacji Unicode w systemie Windows Vista i nowszych, zobacz Używanie normalizacji Unicode do reprezentowania ciągów.

Gdy porównanie ciągów jest zgodne z preferencjami językowymi użytkownika, na przykład podczas sortowania elementów dla uporządkowanej kontrolki ListView, aplikacja może wykonać jedną z następujących czynności:

  • Wywołaj lstrcmp lub lstrcmpi przy użyciu ustawień regionalnych użytkownika.
  • Wywołaj CompareString lub CompareStringEx, aby zdefiniować ustawienia regionalne dla porównania, przekazać dodatkowe flagi, osadzić znaki null lub przekazać jawne długości w celu dopasowania ich do części ciągu.

Gdy wyniki porównania powinny być spójne niezależnie od ustawień regionalnych, na przykład podczas porównywania pobranych danych ze wstępnie zdefiniowanej listy lub wartości wewnętrznej, aplikacja powinna używać CompareString lub CompareStringEx z parametrem Ustawienia regionalne ustawione na LOCALE_INVARIANT. W przypadku CompareString jeden z następujących wywołań będzie zgodny, nawet jeśli ciąg mystr ma wartość "INLAP". W tym przypadku wywołanie funkcji uwzględniającej ustawienia regionalne do lstrcmpi zakończy się niepowodzeniem, jeśli bieżące ustawienia regionalne są wietnamskie.

W systemie Windows XP:

int iReturn = CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, mystr, -1, _T("InLap"), -1);

We wcześniejszych systemach operacyjnych:

DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
int iReturn = CompareString(lcid, NORM_IGNORECASE, mystr, -1, _T("InLap"), -1);

Sortowanie ciągów według porządku

W przypadku sortowania porządkowego (nielingwistycznego) aplikacje powinny zawsze używać funkcji CompareStringOrdinal.

Notatka

Ta funkcja jest dostępna tylko dla systemu Windows Vista i nowszych.

 

CompareStringOrdinal porównuje dwa ciągi Unicode w celu sprawdzenia równości binarnej, w przeciwieństwie do równości językowej. Przykłady takich ciągów nielingwistycznych to nazwy plików NTFS, zmienne środowiskowe i nazwy muteksów, nazwanych potoków lub mailslotów. Z wyjątkiem opcji ignorowania wielkości liter ta funkcja pomija wszystkie równoważności inne niż binarne. W przeciwieństwie do niektórych innych funkcji sortowania, testuje wszystkie punkty kodu pod kątem równości, w tym te, które nie mają żadnej wagi w schematach sortowania językowego.

Wszystkie poniższe stwierdzenia dotyczą CompareStringOrdinal w porównaniach binarnych, ale nie dotyczą CompareString, CompareStringEx, lstrcmplub lstrcmpi.

  • Sekwencje równoważne kanonicznie w formacie Unicode, takie jak MAŁA LITERA ŁACIŃSKA A Z PIERŚCIENIEM POWYŻEJ (U+00e5) i MAŁA LITERA ŁACIŃSKA A + ŁĄCZENIE PIERŚCIENIA POWYŻEJ (U+0061 U+030a), nie są równe, mimo że wydają się identyczne ("å").
  • Kanonicznie podobne ciągi w Unicode, takie jak ŁACIŃSKA MAŁA WIELKA LITERA Y (U+028F) i ŁACIŃSKA WIELKA LITERA Y (U+0059), które wyglądają bardzo podobnie ("ʏ" i "Y") i różnią się tylko w zależności od niektórych specjalnych wag w tabelach lingwistycznych, są uważane za całkowicie różne znaki. Nawet jeśli aplikacja ustawia bIgnoreCase na TRUE, te ciągi są traktowane jako różne.
  • Punkty kodu, które są zdefiniowane, ale nie mają wagi sortowania językowego, takiego jak ZERO WIDTH JOINER (U+200d), traktowane są tak, jakby miały wagi swoich punktów kodu.
  • Punkty kodu zdefiniowane w nowszych wersjach Unicode, ale niemające wagi w aktualnych tabelach językowych, traktuje się tak, jakby miały wagę zgodną z ich punktami kodu.
  • Punkty kodu, które są niezdefiniowane przez Unicode, są traktowane jako mające wagi punktów kodu.
  • Gdy aplikacja ustawia bIgnoreCase na TRUE, funkcja mapuje wielkość liter przy użyciu tabeli wielkich liter systemu operacyjnego, a nie informacji z tabel sortowania językowego. W związku z tym mapowanie jest niezależne od ustawień lokalnych.

Aby uzyskać więcej informacji na temat kanonicznie równoważnych sekwencji w standardach Unicode i kanonicznie podobnych ciągów w standardach Unicode, zobacz Używanie normalizacji Unicode do reprezentowania ciągów.

Sortowanie punktów kodu

Niektóre punkty kodu Unicode nie mają wagi, na przykład ZERO WIDTH NON JOINER, U+200c. Funkcje sortowania celowo oceniają punkty kodu bez wagi jako równoważne, ponieważ nie mają wagi w sortowaniu. W systemie Windows Vista i nowszych aplikacja może sortować te punkty kodu, wywołując funkcje porównania ciągów NLS, szczególnie CompareStringOrdinal, do oceny wszystkich punktów kodu w dosłownym, binarnym sensie, na przykład w weryfikacji hasła. W systemach operacyjnych w wersji wstępnej Windows Vista aplikacja powinna używać funkcji środowiska uruchomieniowego języka C strcmp lub wcscmp.

Funkcje sortowania ignorują znaki diakrytyczne, takie jak NON SPACING BREVE, U+0306, gdy aplikacja określa flagę hlink_NONSPACE. Podobnie te funkcje ignorują symbole, na przykład EQUALS SIGN, U+003d , gdy określono flagę hlink_SYMBOLS. W systemie Windows Vista i nowszych aplikacja wywołuje CompareStringOrdinal do oceny znaków diakrytycznych i kodów symboli w ujęciu binarnym. Na systemach operacyjnych starszych niż Windows Vista, aplikacja powinna używać strcmp lub wcscmp.

Niektóre punkty kodu, takie jak 0xFFFF i 0x058b, nie są obecnie przypisane w formacie Unicode. Te punkty kodu nie otrzymują żadnej wagi w sortowaniu i nigdy nie powinny być przekazywane do funkcji sortowania. Aplikacja powinna używać IsNLSDefinedString do wykrywania punktów kodu innych niż Unicode w strumieniu danych.

Notatka

Wyniki IsNLSDefinedString mogą się różnić w zależności od przekazanej wersji Unicode, jeśli znak jest dodawany do Formatu Unicode w nowszej wersji, a następnie jest dodawany do tabel sortowania systemu Windows. Aby uzyskać więcej informacji, zobacz Use Sort Versioning.

 

Sortuj cyfry jako liczby

W systemie Windows 7 lub nowszym aplikacja może wywoływać CompareString, CompareStringEx, LCMapStringlub LCMapStringEx przy użyciu flagi SORT_DIGITSASNUMBERS. Ta flaga obsługuje sortowanie, które traktuje cyfry jako liczby, na przykład sortowanie "2" przed "10".

Należy pamiętać, że użycie tej flagi nie jest odpowiednie dla cyfr szesnastkowej, takich jak poniżej.

01AF
1BCD
002A
12FA
AB1C
AB02
AB12

W tym przypadku "liczby" są uporządkowane, ale użytkownik postrzega niepoprawnie uporządkowaną listę szesnastkową.

Ciągi mapy

Aplikacja używa funkcji LCMapString lub LCMapStringEx do mapowania ciągów, jeśli nie określono LCMAP_SORTKEY. Zamapowany ciąg jest zakończony wartością null, jeśli ciąg źródłowy jest zakończony wartością null.

Podczas przekształcania między wielkimi i małymi literami funkcja nie gwarantuje, że pojedynczy znak zostanie odwzorowany na pojedynczy znak. Na przykład flagi LCMAP_LOWERCASE i LCMAP_UPPERCASE mogą mapować na siebie niemiecki Sharp S ("ß"). Alternatywnie flaga LCMAP_UPPERCASE może mapować "ß" na "SS", a flaga LCMAP_LOWERCASE może mapować "SS" na "ß". Zachowanie zależy od wersji NLS.

Podczas przekształcania między dużymi i małymi literami funkcja nie jest wrażliwa na kontekst. Na przykład, podczas gdy flaga LCMAP_UPPERCASE poprawnie mapuje zarówno greckie małe litery sigma ("σ"), jak i greckie małe litery sigma na końcu wyrazu ("ς") na grecką wielką literę sigma ("Σ"), flaga LCMAP_LOWERCASE zawsze mapuje "Σ" na "σ", nigdy nie, na "ς".

Domyślnie funkcja mapuje małą literę "i" na wielką literę "I", nawet jeśli parametr lokalizację określa jako turecką lub azerbejdżańską. Aby zastąpić to zachowanie dla języka tureckiego lub azerskiego, aplikacja powinna określić LCMAP_LINGUISTIC_CASING. Jeśli ta flaga jest określona z odpowiednimi ustawieniami regionalnymi, "ı" (małe I bez kropki) jest małą formą "I" (wielkie I bez kropki), a "i" (małe I z kropką) jest małą literą "İ" (wielkie I z kropką).

Jeśli flaga LCMAP_HIRAGANA jest określona tak, aby mapować znaki katakany na znaki hiragana, a LCMAP_FULLWIDTH nie jest określona, LCMapString lub LCMapStringEx mapuje tylko znaki o pełnej szerokości na hiragana. W takim przypadku wszelkie znaki katakana o połowie szerokości są umieszczane w ciągu docelowym bez mapowania na hiragana. Aplikacja musi określić LCMAP_FULLWIDTH, aby zamienić półszerokie znaki katakany na hiraganę. Powodem tego ograniczenia jest to, że wszystkie znaki hiragana są znakami o pełnej szerokości.

Jeśli aplikacja musi usuwać znaki z ciągu źródłowego, może wywołać funkcję mapowania z ustawionymi flagami NORM_IGNORESYMBOLS i NORM_IGNORENONSPACE, a wszystkie inne flagi wyczyszczone. Jeśli aplikacja wykonuje to z ciągiem źródłowym, który nie jest zakończony wartością null, funkcja może zwrócić pusty ciąg i nie zwracać błędu.

Tworzenie kluczy sortowania

Gdy aplikacja określa LCMAP_SORTKEY, LCMapString lub LCMapStringEx generuje klucz sortowania, tablicę binarną wartości bajtów. Klucz sortowania nie jest prawdziwym ciągiem, a jego wartości reprezentują zachowanie sortowania ciągu źródłowego, ale nie są znaczącymi wartościami wyświetlanymi.

Notatka

Funkcja ignoruje arabski kashida podczas generowania klucza sortowania. Jeśli aplikacja wywołuje funkcję w celu utworzenia klucza sortowania dla ciągu zawierającego arabski kashida, funkcja nie tworzy żadnej wartości klucza sortowania.

 

Klucz sortowania może zawierać nieparzyste liczby bajtów. Flaga LCMAP_BYTEREV odwraca tylko parzystą liczbę bajtów. Ostatni bajt (w nieparzystej pozycji) w kluczu sortowania nie jest odwrócony. Jeśli bajt kończący 0x00 jest bajtem nieparzystym, pozostaje ostatnim bajtem w kluczu sortowania. Jeśli kończący bajt 0x00 jest bajtem znajdującym się na parzystej pozycji, wymienia pozycję z bajtem, który go poprzedza.

Podczas generowania klucza sortowania funkcja traktuje łącznik i apostrof inaczej niż inne symbole interpunkcyjne, dzięki czemu wyrazy takie jak "coop" i "co-op" pozostają razem na liście. Wszystkie symbole interpunkcyjne inne niż myślnik i apostrof są umieszczane przed znakami alfanumerycznymi w kolejności sortowania. Aplikacja może zmienić to zachowanie, ustawiając flagę SORT_STRINGSORT, jak opisano w Funkcje Sortowania.

W przypadku użycia w memcmp klucz sortowania generuje taką samą kolejność, jak w przypadku użycia ciągu źródłowego w CompareString lub CompareStringEx. Funkcja memcmp powinna być używana zamiast strcmp, ponieważ klucz sortowania może mieć osadzone bajty null.

Użyj wersjonowania sortowania

Tabela sortowania zawiera dwie liczby identyfikujące jej wersję: zdefiniowaną wersję i wersję NLS. Obie liczby to wartości DWORD składające się z głównej wartości i wartości pomocniczej. Pierwszy bajt wartości jest zarezerwowany, następne dwa bajty reprezentują wersję główną, a ostatni bajt reprezentuje wersję pomocniczą. W terminach szesnastkowych wzorzec to 0xRRMMMMmm, gdzie R oznacza Zarezerwowany, M oznacza główny, a m oznacza pomocniczy. Na przykład wersja główna 3 z wersją pomocniczą 4 jest reprezentowana jako 0x304.

Zdefiniowana wersja identyfikuje repertuar punktów kodowych i jest taka sama dla wszystkich lokalizacji. Wersja główna zwiększa się, aby wskazać zmiany istniejących punktów kodu. Przyrosty wersji pomocniczej wskazują, że dodano punkty kodu, ale nie zmieniono poprzednio istniejących punktów kodu.

Wersja NLS jest specyficzna dla identyfikatora lokalizacji lub nazwy lokalizacji i śledzi zmiany wag punktów kodowych dla dotkniętej lokalizacji. Wersja główna jest zwiększana, gdy zmienia się wagi dla punktów kodu, które można już było sortować. Wersja pomocnicza zwiększa się, gdy nowe punkty kodu mają przypisane wagi, ale wszystkie inne wcześniej sortowalne wagi punktów kodu pozostają niezmienione.

Notatka

W przypadku wersji głównej co najmniej jeden punkt kodu jest zmieniany, aby aplikacja musiała ponownie indeksować wszystkie dane, aby porównania mogły być prawidłowe. W przypadku wersji mniejszej nic nie jest przenoszone, ale dodawane są punkty kodowe. W przypadku tego typu wersji aplikacja musi tylko ponownie indeksować ciągi z wcześniej niesortowalnymi wartościami.

 

Ważny

Wersja główna została zmieniona w systemie Windows 8. Dane utworzone we wcześniejszych wersjach systemu Windows muszą być ponownie indeksowane.

 

Wersje zdefiniowane i NLS są stosowane do punktów kodu sortowalnych, pobranych przy użyciu funkcji LCMapString lub LCMapStringEx z flagą LCMAP_SORTKEY, a także stosowane przez funkcje CompareString, CompareStringEx, FindNLSStringi FindNLSStringEx. Jeśli co najmniej jeden punkt kodu w ciągu jest niemożliwy do posortowania, wtedy funkcja IsNLSDefinedString zwraca FAŁSZ, gdy ten ciąg jest przekazywany jako parametr.

Aplikacja może wywoływać GetNLSVersion lub GetNLSVersionEx, aby pobrać zarówno zdefiniowaną wersję, jak i wersję NLS dla tabeli sortującej.

Indeksowanie bazy danych

Ze względu na wydajność aplikacja powinna postępować zgodnie z tą procedurą podczas indeksowania bazy danych.

Aby prawidłowo indeksować bazę danych

  1. Dla każdej funkcji należy przechowywać wersję nlS, klucze sortowania tej wersji i wskazanie możliwości sortowania dla każdego indeksowanego ciągu.
  2. Gdy wersja mniejsza zostaje zwiększona, ponownie indeksuj wcześniej niesortowalne ciągi. Ciągi, których dotyczy ta aktualizacja, powinny być ograniczone do tych, dla których IsNLSDefinedString wcześniej zwrócił false.
  3. Gdy wersja główna zwiększa się, ponownie zaindeksuj wszystkie ciągi, ponieważ zaktualizowane wagi mogą zmienić zachowanie dowolnego ciągu. Wydania wersji głównych są bardzo rzadkie.

Problemy z indeksowaniem bazy danych mogą wystąpić z następujących powodów:

  • Późniejszy system operacyjny może definiować punkty kodu, które są niezdefiniowane dla wcześniejszego systemu operacyjnego, zmieniając w ten sposób sortowanie.
  • Punkty kodu mogą mieć różne wagi sortowania w różnych systemach operacyjnych ze względu na poprawki w obsłudze języka.

Aby zminimalizować konieczność ponownego indeksowania bazy danych w tych okolicznościach, aplikacja może użyć IsNLSDefinedString, aby odróżnić zdefiniowane od niezdefiniowanych ciągów, aby aplikacja mogła odrzucać ciągi z niezdefiniowanymi punktami kodu. Użycie GetNLSVersion lub GetNLSVersionEx umożliwia aplikacji określenie, czy zmiana nlS wpływa na ustawienia regionalne używane dla określonej tabeli indeksów. Jeśli zmiana nie ma wpływu na ustawienia regionalne, aplikacja nie musi ponownie indeksować tabeli.

Przykłady

W poniższej tabeli przedstawiono efekty niektórych flag używanych z funkcjami sortowania. W każdym przypadku wybór flag określa, czy dwa różne znaki są uznawane za równe do celów sortowania.

Postać 1 Postać 2 Domyślny NORM_IGNOREWIDTH NORM_IGNOREKANA NORM_IGNOREWIDTH| NORMIGNOREKANA
"あ"
U+3042 LITERA HIRAGANA A
"ガ"
U+30A2 KATAKANA LETTER A
Nierówne Nierówne Równy Równy
"オ"
U+FF75 Połówkowy Katakana Litera O
"オ"
U+30AA KATAKANA LETTER O
Nierówne Równy Nierówne Równy
"B"
U+FF22 PEŁNOSZEROKA ŁACIŃSKA DUŻA LITERA B
"B"
U+0042 DUŻA LITERA B ŁACIŃSKA
Nierówne Równy Nierówne Równy

 

korzystanie z wsparcia języka narodowego

Sortowanie

Pobieranie i ustawianie informacji o lokalizacji

Używanie Normalizacji Unicode do Reprezentowania Ciągów

Zagadnienia dotyczące zabezpieczeń: Funkcje międzynarodowe

CompareString

CompareStringEx

CompareStringOrdinal

FindNLSString

FindNLSStringEx

LCMapString

LCMapStringEx