Переключение на Главную Страницу Страницы: 1 ОтправитьПечать
Обычная тема ИТЗ: баг при сортировке строк (число прочтений - 2636 )
aivanov
YaBB Newbies
*
Отсутствует


I Love YaBB 2!

Сообщений: 3
Зарегистрирован: 17. Декабря 2007
ИТЗ: баг при сортировке строк
21. Апреля 2008 :: 10:10
Печать  
Обнаружил такой баг: в ИТЗ при сортировке или при индексировании колонок строки, в которых есть дефис, неверно сортируются - символ дефиса считается меньше даже символа пробела. Тест прилагается.
  

TestSortITZ.ert ( 35 KB | Загрузки )
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: ИТЗ: баг при сортировке строк
Ответ #1 - 21. Апреля 2008 :: 10:18
Печать  
Прочитай ответ в багтракере: http://www.1cpp.ru/bugs/show_bug.cgi?id=3755

Вопрос возникает не в первый раз, так что тратить на него время не имеет большого смысла.
Это не баг, и 99% вероятность, что заявка будет закрыта со статусом WONT_FIX.

Тем не менее, если будет нормальное обоснование, что есть "правильная" сортировка, можно еще раз обсудить.
  

De quelle planète es-tu?
Наверх
 
IP записан
 
aivanov
YaBB Newbies
*
Отсутствует


I Love YaBB 2!

Сообщений: 3
Зарегистрирован: 17. Декабря 2007
Re: ИТЗ: баг при сортировке строк
Ответ #2 - 23. Апреля 2008 :: 12:59
Печать  
Обоснование можно предложить такое: хочется, чтобы сортировка строк ИТ соответствовала отсортированному набору данных, возвращаемых 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.
  
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: ИТЗ: баг при сортировке строк
Ответ #3 - 23. Апреля 2008 :: 14:44
Печать  
Блестяще.
Реально, решение еще проще:

m_tbl[' '] = 1;

в предпоследней строке CCompare::Init.
Спасибо за проведенное исследование. Улыбка
  

De quelle planète es-tu?
Наверх
 
IP записан
 
Переключение на Главную Страницу Страницы: 1
ОтправитьПечать