В платформе 1С:Предприятие 8 стандартные методы сортировки строк, такие как метод Сортировать() для таблиц значений, по умолчанию выполняют сравнение без учета регистра символов. Это может быть нежелательно в ситуациях, когда требуется строгое алфавитное упорядочивание с различением прописных и строчных букв (например, "АБВ" должно идти перед "абв"). Кроме того, могут возникать сложности с лингвистически корректной сортировкой для специфических символов, таких как русская буква "ё". Давайте подробно рассмотрим, как мы можем реализовать регистрозависимую сортировку, используя различные обходные пути и оптимизации, доступные на платформе 1С.
Авторы сообщений справедливо отмечают, что встроенный параметр функции Сортировать() – ОбъектСравнения (ComparisonObject) – предназначен для сравнения значений непримитивных типов и не позволяет переопределить собственное правило сравнения строк с учетом регистра, подобно тому, как это делается в языках программирования вроде C++ или C#.
Один из самых прямолинейных подходов к реализации регистрозависимой сортировки заключается в предварительной модификации исходной строки. Мы создадим новую "строку для сортировки", которая будет содержать дополнительную информацию о регистре каждого символа. Затем сортировка будет выполняться по этой вспомогательной строке.
Принцип работы:
Пример кода для реализации этого подхода:
Функция СтрокаДляСортировкиСУчетомРегистра(Стр)
СтрСортировки = "";
Для Сч = 1 По СтрДлина(Стр) Цикл
Симв = Сред(Стр, Сч, 1);
Если Симв = НРег(Симв) Тогда
// Если символ равен своей версии в нижнем регистре,
// это либо строчная буква, либо регистронезависимый символ
Суффикс = "1";
Иначе
// В противном случае, это прописная буква
Суффикс = "2";
КонецЕсли;
СтрСортировки = СтрСортировки + Симв + Суффикс;
КонецЦикла;
Возврат СтрСортировки;
КонецФункции
Преимущества этого метода:
Недостатки:
Другой, более продвинутый и, как показали тесты, потенциально более производительный подход, основан на использовании бинарного представления строк. В бинарном виде прописные и строчные буквы имеют разные числовые значения, что позволяет естественным образом реализовать регистрозависимую сортировку.
Общий принцип:
Подход с использованием UTF-32BE и корректировкой для 'Ё':
Рассмотрим гибридный подход, предложенный в обсуждении, который показал хорошую эффективность для кириллицы. Он использует преобразование в кодировку UTF-32BE, что обеспечивает фиксированный размер каждого символа (4 байта), а затем работает с HEX-строкой.
Функция ДДДляСортировкиСУчетомРегистра(Стр)
// 1. Преобразуем строку в двоичные данные в кодировке UTF-32BE.
// Эта кодировка дает 4 байта на символ, что упрощает дальнейшие манипуляции с HEX-строкой.
ДД = ПолучитьДвоичныеДанныеИзСтроки(Стр, "UTF-32BE");
// 2. Получаем HEX-строковое представление двоичных данных.
// Функция ПолучитьHexСтрокуИзДвоичныхДанных добавляет в начале "0000"
// и "0000" в конце, если строка пустая.
// Мы берем подстроку, начиная с 5-го символа, чтобы отбросить первые "0000" (BOM)
// и добавляем "0000" в конец для выравнивания или обработки коротких строк.
Ст = Сред(ПолучитьHexСтрокуИзДвоичныхДанных(ДД), 5) + "0000";
// 3. Выполняем специфическую корректировку для буквы 'Ё' и 'ё'.
// В большинстве кодировок 'Ё' и 'ё' сортируются после 'Е' и 'е'.
// Мы заменяем их коды так, чтобы они располагались после 'Е'/'е' в бинарном представлении.
// Оригинальные коды:
// 'Ё' (U+0401) -> 04010000 в UTF-32BE
// 'ё' (U+0451) -> 04510000 в UTF-32BE
// Мы хотим, чтобы они сортировались как "Е" + что-то, что выше "Е".
// Например, "0415" - это код для 'Е' (U+0415). Мы можем использовать "04150001"
// или "04050001" (если хотим после Е) как искусственный код.
// В данном примере: "04150001" для 'Ё' и "04350001" для 'ё'.
// Это смещает их в сортировке, делая их "как бы" после "Е" и "е".
Ст = СтрЗаменить(Ст, "04010000", "04150001"); // Замена кода для 'Ё'
Ст = СтрЗаменить(Ст, "04510000", "04350001"); // Замена кода для 'ё'
// 4. Обратно преобразуем модифицированную HEX-строку в двоичные данные.
// Этот шаг может быть избыточным, если сортировка происходит по строке.
Возврат ПолучитьДвоичныеДанныеИзHexСтроки(Ст);
КонецФункции
Анализ и уточнения к Решению 2 (на основе Сообщения 7):
Мы должны внимательно проанализировать последние шаги этого решения. Преобразование в HEX-строку функцией ПолучитьHexСтрокуИзДвоичныхДанных() уже дает строковое представление, по которому можно сортировать. Обратное преобразование из HEX-строки в двоичные данные (ПолучитьДвоичныеДанныеИзHexСтроки()) может быть лишним. В процессе сортировки колонки типа "Двоичные данные" в таблице значений платформа, вероятно, неявно преобразует их для сравнения, возможно, опять же к HEX-строковому представлению или бинарному сравнению байт за байтом.
Таким образом, если мы сортируем по колонке типа "Двоичные данные", то шаги с преобразованием в HEX-строку и обратно могут быть избыточными или неэффективными. Более прямой путь — это манипулировать непосредственно двоичными данными. Однако, поскольку платформенные функции для работы с двоичными данными менее гибки, чем для строк (например, нет прямой замены части двоичных данных по маске), преобразование в HEX-строку для корректировки 'ё' и 'Ё' становится прагматичным решением. Важно понимать, что сортироваться будут либо "Двоичные данные" (и тогда платформа сама решит, как их сравнивать), либо полученная HEX-строка, если мы сделаем колонку для сортировки строкового типа.
Производительность является критически важным фактором при работе с большими объемами данных. Мы сравнили различные подходы на примере задачи, где производится более миллиона преобразований строк и сотни тысяч операций сортировки.
Методология сравнения (Сообщение 9):
Полученные результаты:
ПолучитьHexСтрокуИзДвоичныхДанных() и последующей сортировке строк, время преобразования возрастает.Вывод по производительности:
Основываясь на проведенных тестах, наиболее оптимальным с точки зрения суммарной производительности оказалось использование функции ПолучитьДвоичныеДанныеИзСтроки() с кодировкой UTF-8 и последующая сортировка колонки с типом "Двоичные данные". Платформенные операции с двоичными данными (как при преобразовании, так и при сравнении) работают быстрее, чем промежуточные преобразования в строковые HEX-представления и сортировка строк. Это подтверждает общую тенденцию, что "внутриплатформенные" преобразования и операции с примитивными бинарными типами часто более эффективны.
Важно отметить, что мы рассматриваем программную реализацию регистрозависимой сортировки. В интерфейсе 1С пользователи могут настраивать сортировку списков, журналов, документов и отчетов, щелкая по заголовкам колонок или через меню "Настроить список". Однако эта интерактивная сортировка, как правило, не позволяет задать регистрозависимое сравнение строк по умолчанию, и наши программные решения необходимы именно для случаев, когда такая логика должна быть встроена в алгоритм работы программы.
Для реализации регистрозависимой сортировки строк в 1С мы приходим к следующему:
ПолучитьДвоичныеДанныеИзСтроки() с кодировкой UTF-8 и сортировку по колонке типа "Двоичные данные".Нам удалось найти эффективные программные способы обхода стандартных регистронезависимых механизмов сортировки в 1С, что позволяет достигать требуемой логики упорядочивания данных с учетом регистра символов.