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

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

В практике разработчика 1С часто возникает задача сортировки текстовых данных, которые содержат числовые значения. Типичный пример — адреса, номера домов или микрорайонов. Стандартная сортировка в 1С работает по строковым (лексикографическим) правилам: символы сравниваются по порядку слева направо. Из-за этого строка "36,11" оказывается «меньше» строки "36,5", так как единица во втором разряде после запятой меньше пятерки. В этой статье мы подробно разберем, как реализовать «естественную сортировку» (Natural Sort), чтобы данные выстраивались в логичном для человека порядке.

Причина проблемы: строковая vs числовая сортировка

Разберем ситуацию на примере. У нас есть значения: "9 мкр. 36,1", "9 мкр. 36,5" и "9 мкр. 36,11". При вызове метода Сортировать() для объекта ТаблицаЗначений, система видит в этих данных обычные строки. Сравнивая "36,5" и "36,11", 1С видит, что первые два символа одинаковы ("3"), вторые тоже ("6"), точка/запятая совпадает, а вот в следующем разряде "1" (из 11) меньше, чем "5". Поэтому "36,11" всегда будет стоять выше. Чтобы это исправить, нам нужно изменить подход к хранению или сравнению данных.

Решение 1: Разделение данных на отдельные колонки (самый надежный способ)

Как справедливо заметили участники обсуждения, самый правильный архитектурный подход — не хранить «кашу» из текста и чисел, если по ним требуется сортировка. Проанализируем, как можно модифицировать структуру ТаблицаЗначений. Мы можем разбить адрес на составляющие: текстовую часть (микрорайон) и числовую (номер дома).

Рассмотрим по шагам, как это реализовать программно:

  1. Создадим дополнительные колонки в таблице значений с типом Число.
  2. Используя строковые функции или регулярные выражения, извлечем числовую часть.
  3. Заполним новые колонки и выполним сортировку по ним.

Посмотрим на пример кода:


// Предположим, ТЗДанных уже заполнена строками
ТЗДанных.Колонки.Добавить("НомерМикрорайона", Новый ОписаниеТипов("Число"));
ТЗДанных.Колонки.Добавить("НомерДома", Новый ОписаниеТипов("Число"));

Для Каждого СтрокаТЗ Из ТЗДанных Цикл
    // Здесь должна быть логика парсинга строки
    // Например, для строки "9 мкр. 36,5"
    ЧастиСтроки = СтрРазделить(СтрокаТЗ.ИсходныйАдрес, " ");
    СтрокаТЗ.НомерМикрорайона = Число(ЧастиСтроки[0]); 
    
    // Очищаем номер дома от лишних символов и преобразуем в число
    СтрНомерДома = СтрЗаменить(ЧастиСтроки[2], ",", ".");
    СтрокаТЗ.НомерДома = Число(СтрНомерДома);
КонецЦикла;

// Теперь сортируем по двум колонкам
ТЗДанных.Сортировать("НомерМикрорайона Возр, НомерДома Возр");

Решение 2: Метод дополнения нулями (Padding)

Если мы не хотим менять структуру таблицы или данные слишком разнородны для разделения на колонки, можно использовать вспомогательную невидимую колонку для «индекса сортировки». Суть метода заключается в приведении всех чисел внутри строки к фиксированной длине путем добавления ведущих нулей.

Проанализируем ситуацию: если мы превратим "36,1" в "00036,10", а "36,11" в "00036,11", то обычная строковая сортировка расставит их правильно. Рассмотрим, как это сделать:

  1. Создаем служебную колонку КлючСортировки.
  2. Пишем функцию, которая находит числа в строке и заменяет их на формат типа "0000000001".
  3. Заполняем колонку и сортируем по ней.

Пример функции для нормализации строки:


Функция ПолучитьКлючСортировки(ЗначениеСтроки)
    Результат = "";
    // Упрощенный пример: заменяем только номер дома
    // В реальности лучше использовать Регулярные Выражения
    МассивЧастей = СтрРазделить(ЗначениеСтроки, ".");
    Если МассивЧастей.Количество() > 1 Тогда
        НомерЧасть = СокрЛП(МассивЧастей[1]);
        // Дополняем нулями до 10 знаков
        Пока СтрДлина(НомерЧасть) < 10 Цикл
            НомерЧасть = "0" + НомерЧасть;
        КонецЦикла;
        Результат = МассивЧастей[0] + "." + НомерЧасть;
    КонецЕсли;
    Возврат Результат;
КонецФункции

Решение 3: Использование Windows API (StrCmpLogicalW)

Для продвинутых разработчиков, работающих в среде Windows, существует возможность использовать системную функцию StrCmpLogicalW из библиотеки shlwapi.dll. Именно эта функция отвечает за «умную» сортировку имен файлов в проводнике Windows, где "2.txt" всегда идет перед "10.txt".

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

Решение 4: Регулярные выражения (VBScript.RegExp)

Если адреса имеют сложную структуру (например, "д. 10к2 стр. 1"), простым методом СтрРазделить не обойтись. В таких случаях удобно использовать объект VBScript.RegExp. Мы можем извлечь все последовательности цифр, преобразовать их в числа или дополнить нулями, и собрать обратно в «сортировочный индекс».

Рассмотрим пример логики работы с регулярными выражениями:


РегЭкспо = Новый COMОбъект("VBScript.RegExp");
РегЭкспо.Global = Истина;
РегЭкспо.Pattern = "\d+"; // Ищем все вхождения цифр

Matches = РегЭкспо.Execute(ИсходнаяСтрока);
Ключ = ИсходнаяСтрока;
Для каждого Match Из Matches Цикл
    ЧислоСтрокой = Match.Value;
    НормализованноеЧисло = Формат(Число(ЧислоСтрокой), "ЧГ=0; ЧВН="); 
    // Заменяем в строке исходное число на дополненное нулями
    Ключ = СтрЗаменить(Ключ, ЧислоСтрокой, НормализованноеЧисло);
КонецЦикла;

Рекомендации по выбору метода

Выбор конкретного способа зависит от ваших условий:

Важный совет: При преобразовании строк типа "36,5" в числа, всегда обращайте внимание на разделитель дробной части. В разных региональных настройках это может быть как точка, так и запятая. Используйте СтрЗаменить(Строка, ",", ".") перед вызовом функции Число(), чтобы избежать ошибок выполнения кода.

← На главную