CompareString
- это одна из самых клёвых функций. Я думал так и до того, как стал её владельцем, и даже до того, как я встретил людей, которые раньше были её владельцами (или женщину, которая её написала).Но, как и любая другая API функция,
CompareString
имеет свои подводные камни.Итак, если вы подумаете о всей NLS информации как об огромной базе данных, то идентификатор локали (a.k.a. LCID) - это первичный ключ в ней. Это самый первый параметр к
CompareString
.Если вы вызываете не-Unicode версию (
CompareStringA
), то вместо того, чтобы конвертировать строку с использованием кодовой страницы системы по-умолчанию, функция конвертирует параметры, используя кодовую страницу по-умолчанию этой локали. Помимо других вещей, из этого также следует, что вы не можете использовать CompareStringA
для обработки строк в UTF-8.Окей, давайте теперь посмотрим на самый важный параметр: второй, который с флагами. Я опишу каждый из флагов:
Ну а третий и пятый параметры функцииNORM_IGNORECASE
- игнорировать регистр. Ну, лучшим именем для этого флага было быIGNORE_TERTIARYWEIGHT
, поскольку именно это он и делает (маскирует третичные веса), хотя, очевидно, уже слишком поздно вносить такое изменение. Этот флаг может вызвать нежелательные результаты, когда его используют при сравнении строк, которые зависят от этого веса - что, к счастью, происходит не так уж часто. Но если вы не ожидаете, что "ʏ", "Y" и "y" (U+028f, U+0059 и U+0079, a.k.a. LATIN LETTER SMALL CAPITAL Y, LATIN LETTER CAPITAL Y и LATIN LETTER SMALL Y) окажутся равными, то вам лучше бы дважды подумать, прежде чем указывать этот флаг. Вы также потеряете отличия между конечными буквами для иврита (т.е. "מ" и "ם", U+05de и U+05dd a.k.a. HEBREW LETTER MEM и HEBREW LETTER FINAL MEM), арабского (т.е. "ش" U+0634 a.k.a. ARABIC LETTER SHEEN и его изолированной, конечной, начальной и медиальной формами (ﺵ, ﺶ, ﺷ и ﺸ a.k.a. U+feb5, U+feb6, U+feb7 и U+feb8) и других языков.
NORM_IGNORENONSPACE
- игнорировать непробельные (nonspacing) символы. И снова, лучшим названием флага было быIGNORE_SECONDARYWEIGHT
, поскольку именно это он и делает (маскирует вторичный вес). Этот флаг может вызвать нежелательные результаты, когда его используют при сравнении строк, содержащих символы, зависящие от этого веса. Самый яркий пример можно увидеть в корейском, где слог U+ac00 (가, Hangul Syllable Kiyeok A) может рассматриваться равным всем этим символам: 伽 佳 假 價 加 可 呵 哥 嘉 嫁 家 暇 架 枷 柯 歌 珂 痂 稼 苛 茄 街 袈 訶 賈 跏 軻 迦 駕 仮 傢 咖 哿 坷 宊 斝 榎 檟 珈 笳 耞 舸 葭 謌. Для остальных слогов в хангыле, это будет где-то лучше, где-то хуже. Но проблема также существует и в некоторых других языках.
NORM_IGNORESYMBOLS
- игнорировать символы вроде "_", "#" и "*". Список символов "увеличивается", когда указываетсяSORT_STRINGSORT
, поскольку пунктуация также рассматривается как символы. Это иногда бывает полезно, но также создаст хаос, если вы ищете строки в тексте типа исходника C++ или C#.
SORT_STRINGSORT
- рассматривать пунктуацию наравне с символами. К примеру, сортировка типа STRING рассматривает'co-op'
и'co_op'
как строки, сортируемые вместе, потому что и дефис и подчёркивание оба рассматриваются как символы. С другой стороны, сортировка WORD рассматривает дефис и апостроф по-разному, поэтому'co-op'
и'co_op'
не будут расположены вместе, но зато'co-op'
и'coop'
будут рядом. Это документировано в заголовочном файлеwinnls.h
:// // Sorting Flags. // // WORD Sort: culturally correct sort // hyphen and apostrophe are special cased // example: "coop" and "co-op" will sort together in a list // // co_op <------- underscore (symbol) // coat // comb // coop // co-op <------- hyphen (punctuation) // cork // went // were // we're <------- apostrophe (punctuation) // // // STRING Sort: hyphen and apostrophe will sort with all other symbols // // co-op <------- hyphen (punctuation) // co_op <------- underscore (symbol) // coat // comb // coop // cork // we're <------- apostrophe (punctuation) // went // were //
NORM_IGNOREKANATYPE
- не делать различий между хираганой и катаканой. Соответствующие символы хираганы и катаканы рассматриваются равными (т.е. "げ" U+3052 HIRAGANA LETTER GE и "ゲ" U+30B2 KATAKANA LETTER GE). Аналогичный эффект имеет и вызовLCMapString
с флагомLCMAP_HIRAGANA
илиLCMAP_KATAKANA
для обеих строк. Есть много случаев, когда это отличие важно.
NORM_IGNOREWIDTH
- не делать различий между полу- и полноширинными вариантами одного и того же символа. Эти две формы символов существуют в Unicode из-за обратной совместимости с устаревшими стандартами CJK, которые кодировались в две формы. В этих старых стандартах половинная форма использовала один байт, а полноширинная - два байта. Кроме того, сам символ рисовался в два раза шире (например, "ヲ" U+30F2 KATAKANA LETTER WO и "ヲ", U+FF66 HALFWIDTH KATAKANA LETTER WO). Этот же эффект имеет вызовLCMapString
сLCMAP_FULLWIDTH
илиLCMAP_HALFWIDTH
для обеих строк. Вообще говоря, есть интересные случаи, когда какой-либо вариант используется для украшательства или функциональность. Т.е. хотя изначальная причина для этих двух форм была в стандартах, то сегодня эти формы используются более практично. К примеру, свойства в японском Access выводятся полноширинными, а их описания - в половинную ширину, т.к. это лучше выглядит визуально.
CompareString
- это сами строки для сравнения.Наконец, четвёртый и шестой параметры указывают длины соответствующих строк в символах.
Итак, для практического использования вам нужно: использовать осмысленные строки, у которых определены веса в таблицах Windows. Тогда у вас будут получаться лингвистически осмысленные результаты. Когда же вы выходите за эти рамки, то вы (или ваши пользователи) получаете результаты, которые вы не ожидаете. И, как указывает информация о флагах, безразборное употребление флагов - это крайне плохая идея, которая приведёт к не интуитивным результатам.
А что будет интуитивным? В моём понимании лучше всего смотрится такой подход:
- Указание потенциально опасных флагов, что даст вам какой-то результат поиска.
- Повторный вызов функции, но уже без флагов, чтобы получить меньший и более конкретный список.
- Используя эти "предпочтительные результаты" из пункта 2, приоритезировать пункт 1.
Итак, есть ещё несколько вопросов, которые я мог бы обсудить, но они могут подождать до другого раза...
This post brought to you by "ʏ" (U+028f, a.k.a. LATIN LETTER SMALL CAPITAL Y)
Комментариев нет:
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.