Как реализовать регистрозависимую сортировку строк в таблицах и списках значений 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В платформе 1С:Предприятие 8 стандартные методы сортировки строк, такие как метод Сортировать() для таблиц значений, по умолчанию выполняют сравнение без учета регистра символов. Это может быть нежелательно в ситуациях, когда требуется строгое алфавитное упорядочивание с различением прописных и строчных букв (например, "АБВ" должно идти перед "абв"). Кроме того, могут возникать сложности с лингвистически корректной сортировкой для специфических символов, таких как русская буква "ё". Давайте подробно рассмотрим, как мы можем реализовать регистрозависимую сортировку, используя различные обходные пути и оптимизации, доступные на платформе 1С.

Авторы сообщений справедливо отмечают, что встроенный параметр функции Сортировать() – ОбъектСравнения (ComparisonObject) – предназначен для сравнения значений непримитивных типов и не позволяет переопределить собственное правило сравнения строк с учетом регистра, подобно тому, как это делается в языках программирования вроде C++ или C#.

Решение 1: Преобразование строки для сортировки с добавлением суффиксов

Один из самых прямолинейных подходов к реализации регистрозависимой сортировки заключается в предварительной модификации исходной строки. Мы создадим новую "строку для сортировки", которая будет содержать дополнительную информацию о регистре каждого символа. Затем сортировка будет выполняться по этой вспомогательной строке.

Принцип работы:

  1. Мы перебираем каждый символ исходной строки.
  2. Для каждого символа определяем его регистр: строчный (нижний), прописной (верхний) или регистронезависимый (например, цифры, знаки препинания).
  3. К каждому символу добавляем специальный суффикс (например, "1" для нижнего регистра/регистронезависимых символов и "2" для верхнего регистра).
  4. Эти символы с суффиксами конкатенируются в новую строку, которая и будет использоваться для сортировки.

Пример кода для реализации этого подхода:


Функция СтрокаДляСортировкиСУчетомРегистра(Стр)
    СтрСортировки = "";
    Для Сч = 1 По СтрДлина(Стр) Цикл
        Симв = Сред(Стр, Сч, 1);
        Если Симв = НРег(Симв) Тогда
            // Если символ равен своей версии в нижнем регистре,
            // это либо строчная буква, либо регистронезависимый символ
            Суффикс = "1"; 
        Иначе
            // В противном случае, это прописная буква
            Суффикс = "2"; 
        КонецЕсли;
        СтрСортировки = СтрСортировки + Симв + Суффикс;
    КонецЦикла;
    Возврат СтрСортировки;
КонецФункции

Преимущества этого метода:

Недостатки:

Решение 2: Сортировка по двоичным данным и HEX-строкам с корректировкой

Другой, более продвинутый и, как показали тесты, потенциально более производительный подход, основан на использовании бинарного представления строк. В бинарном виде прописные и строчные буквы имеют разные числовые значения, что позволяет естественным образом реализовать регистрозависимую сортировку.

Общий принцип:

  1. Мы преобразуем исходную строку в двоичные данные, используя определенную кодировку (например, UTF-32BE).
  2. Далее, если требуется, мы можем получить HEX-строковое представление этих двоичных данных.
  3. Для специфических символов (например, русской буквы "ё") может потребоваться ручная корректировка их HEX-представления, чтобы обеспечить желаемый порядок сортировки.
  4. Сортировка выполняется по полученным двоичным данным или модифицированным HEX-строкам.

Подход с использованием 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):

Полученные результаты:

  1. Кодировки: Кодировка Windows-1251 показала наибольшее время преобразования. Все остальные кодировки (UTF-8, UTF-16BE, UTF-32BE) были примерно на 3-4% быстрее, с минимальными различиями между собой (в пределах погрешности измерений).
  2. Время сортировки: Само время сортировки двоичных данных во всех случаях было практически одинаковым, независимо от используемой кодировки при преобразовании.
  3. Сортировка по HEX-строкам vs. Двоичным данным:
    • При переводе двоичных данных в HEX-строку методом ПолучитьHexСтрокуИзДвоичныхДанных() и последующей сортировке строк, время преобразования возрастает.
    • Однако, при этом время самой сортировки *сокращается*.
    • Суммарные затраты времени на преобразование и сортировку в случае использования HEX-строк оказались выше в среднем на 12-15% по сравнению с сортировкой напрямую двоичных данных.

Вывод по производительности:

Основываясь на проведенных тестах, наиболее оптимальным с точки зрения суммарной производительности оказалось использование функции ПолучитьДвоичныеДанныеИзСтроки() с кодировкой UTF-8 и последующая сортировка колонки с типом "Двоичные данные". Платформенные операции с двоичными данными (как при преобразовании, так и при сравнении) работают быстрее, чем промежуточные преобразования в строковые HEX-представления и сортировка строк. Это подтверждает общую тенденцию, что "внутриплатформенные" преобразования и операции с примитивными бинарными типами часто более эффективны.

Пользовательская сортировка в интерфейсе

Важно отметить, что мы рассматриваем программную реализацию регистрозависимой сортировки. В интерфейсе 1С пользователи могут настраивать сортировку списков, журналов, документов и отчетов, щелкая по заголовкам колонок или через меню "Настроить список". Однако эта интерактивная сортировка, как правило, не позволяет задать регистрозависимое сравнение строк по умолчанию, и наши программные решения необходимы именно для случаев, когда такая логика должна быть встроена в алгоритм работы программы.

Заключительные рекомендации

Для реализации регистрозависимой сортировки строк в 1С мы приходим к следующему:

  1. Встроенных средств напрямую нет. Придется использовать обходные пути.
  2. Для простых случаев и небольших объемов: Метод с добавлением суффиксов к символам (Решение 1) прост в реализации и понимании, но не самый производительный.
  3. Для высоких требований к производительности и больших объемов: Используйте преобразование строк в двоичные данные. Рекомендуем функцию ПолучитьДвоичныеДанныеИзСтроки() с кодировкой UTF-8 и сортировку по колонке типа "Двоичные данные".
  4. Особенности языка: Для русского языка может потребоваться специальная обработка буквы "ё" и "Ё". Если вы работаете с двоичными данными, вам может потребоваться промежуточное преобразование в HEX-строку для выполнения замещений кодов символов, как было показано в Решении 2, а затем обратное преобразование в двоичные данные для оптимизированной сортировки.

Нам удалось найти эффективные программные способы обхода стандартных регистронезависимых механизмов сортировки в 1С, что позволяет достигать требуемой логики упорядочивания данных с учетом регистра символов.

← На главную