Обоснование можно предложить такое: хочется, чтобы сортировка строк ИТ соответствовала отсортированному набору данных, возвращаемых SQL сервером. Тем более, что ИТ все более походит на переменную SQL сервера table (я имею ввод SQL-подобных методов InnerJoin, LeftJoin и т.п.)
Цитата:ИТ сортирует с учетом весов, которые возвращаются LCMapString/LCMAP_SORTKEY
Это весьма универсально, но, к сожалению, никто не может дать
ответ, почему с точки зрения LCMapString, "-" стоит перед " ".
Да, в этом ты прав и это весьма странно.
Цитата:К сожалению, вариантов решения, которые будут 100% совпадать с
strcmp/_stricmp, и позволят выполнять операции сравнения с обрезкой
пробелов и без учета регистра так же эффективно, как это сделано
в строчном компараторе ИТ, на горизонте не видно.
Вообще-то, решение есть и оно очень простое. Но в начале несколько тестов:
сравним сортировку SQL сервера и ИТ на примере одиночных символов:
скрипт SQL сервера:
CREATE TABLE [dbo].[t1] (
[sym] [char] (1) COLLATE Cyrillic_General_CI_AS NOT NULL ,
[code] [int] NOT NULL
) ON [PRIMARY]
GO
declare @cnt int
set @cnt = 1
while @cnt < 256
begin
insert into t1 values(char(@cnt), @cnt)
set @cnt = @cnt + 1
end
select code from t1 order by sym
и обработку 1С:
тз = СоздатьОбъект("ИндексированнаяТаблица");
тз.НоваяКолонка("кол");
Для й = 1 По 255 Цикл
тз.НоваяСтрока();
тз.кол = Симв(й);
КонецЦикла;
тз.Сортировать("кол");
тз.ВыбратьСтроки();
Пока тз.ПолучитьСтроку() = 1 Цикл
Сообщить(КодСимв(тз.кол));
КонецЦикла;
Сравнив оба листинга, можно увидеть что проблема в символе с кодом 32 (aka пробел) - SQL сервер поместил его на первое место, в товремя как ИТ (а точнее ф-ция LCMapString) поместила на 34-е. В остальном листинги одинаковы (для тех, у кого SQL сервер различает регистр символов).
Следующей будет протестирована LCMapString:
данная ф-ция выведет код символа и его sortkey
void testLCMapStringA()
{
const int dst_len = 20;
char dst[dst_len];
for(int i = 1; i < 256; ++i)
{
WORD w_unicode = 0xFFFF;
char ch = (char)i;
if (LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY|SORT_STRINGSORT, &ch, 1, dst, dst_len))
{
char* pc = dst;
char c;
while ((c = *pc++) && (c != 0x01))
w_unicode = MAKEWORD(*pc++, c);
std::cout << i << ": " << w_unicode << std::endl;
}
}
}
Отсортировав вывод этой ф-ции по sortkey, можно увидеть, что первым идет символ с кодом 1 (собственно говоря, это было видно еще в отсортированной ИТ, но мне хотелось посмотреть именно на результат работы LCMapString).
Итак, решение очевидно - нужно немного "помочь" функции CCompare::GetWeight, явно задав sortkey для кода 32, например определить sortkey для кода 1 и вычесть из него единицу:
// здесь идет код if (LCMapStringA(lcid, LCMAP_SORTKEY|SORT_STRINGSORT, &ch, 1, dst, dst_len)) {... }
if(ch == 32)
{
char ch1 = 1;
WORD w_unicode1 = 0xFFFF;
if (LCMapStringA(lcid, LCMAP_SORTKEY|SORT_STRINGSORT, &ch1, 1, dst, dst_len))
{
char* pc = dst;
char c;
while ((c = *pc++) && (c != 0x01))
w_unicode1 = MAKEWORD(*pc++, c);
}
if(w_unicode1 != 0xFFFF)
w_unicode = w_unicode1 - 1;
}
// порядок сортировки регистра: младшие w_case, старшие w_case (т.е. прописные сначала)
UINT weight = (w_unicode << 16) | (w_diacritic << 8) | (w_case ^ 0xFF);
return weight;
}
Повторные тесты с обновленной компонентой показывают, что теперь ИТ сортирует "правильно"
Как видишь, исправление небольшое и компаратор остался таким же быстрым как и раньше, но теперь в результате ИТ походит еще больше на переменную table.