В практике разработчика 1С часто возникает задача сортировки текстовых данных, которые содержат числовые значения. Типичный пример — адреса, номера домов или микрорайонов. Стандартная сортировка в 1С работает по строковым (лексикографическим) правилам: символы сравниваются по порядку слева направо. Из-за этого строка "36,11" оказывается «меньше» строки "36,5", так как единица во втором разряде после запятой меньше пятерки. В этой статье мы подробно разберем, как реализовать «естественную сортировку» (Natural Sort), чтобы данные выстраивались в логичном для человека порядке.
Разберем ситуацию на примере. У нас есть значения: "9 мкр. 36,1", "9 мкр. 36,5" и "9 мкр. 36,11". При вызове метода Сортировать() для объекта ТаблицаЗначений, система видит в этих данных обычные строки. Сравнивая "36,5" и "36,11", 1С видит, что первые два символа одинаковы ("3"), вторые тоже ("6"), точка/запятая совпадает, а вот в следующем разряде "1" (из 11) меньше, чем "5". Поэтому "36,11" всегда будет стоять выше. Чтобы это исправить, нам нужно изменить подход к хранению или сравнению данных.
Как справедливо заметили участники обсуждения, самый правильный архитектурный подход — не хранить «кашу» из текста и чисел, если по ним требуется сортировка. Проанализируем, как можно модифицировать структуру ТаблицаЗначений. Мы можем разбить адрес на составляющие: текстовую часть (микрорайон) и числовую (номер дома).
Рассмотрим по шагам, как это реализовать программно:
Число.Посмотрим на пример кода:
// Предположим, ТЗДанных уже заполнена строками
ТЗДанных.Колонки.Добавить("НомерМикрорайона", Новый ОписаниеТипов("Число"));
ТЗДанных.Колонки.Добавить("НомерДома", Новый ОписаниеТипов("Число"));
Для Каждого СтрокаТЗ Из ТЗДанных Цикл
// Здесь должна быть логика парсинга строки
// Например, для строки "9 мкр. 36,5"
ЧастиСтроки = СтрРазделить(СтрокаТЗ.ИсходныйАдрес, " ");
СтрокаТЗ.НомерМикрорайона = Число(ЧастиСтроки[0]);
// Очищаем номер дома от лишних символов и преобразуем в число
СтрНомерДома = СтрЗаменить(ЧастиСтроки[2], ",", ".");
СтрокаТЗ.НомерДома = Число(СтрНомерДома);
КонецЦикла;
// Теперь сортируем по двум колонкам
ТЗДанных.Сортировать("НомерМикрорайона Возр, НомерДома Возр");
Если мы не хотим менять структуру таблицы или данные слишком разнородны для разделения на колонки, можно использовать вспомогательную невидимую колонку для «индекса сортировки». Суть метода заключается в приведении всех чисел внутри строки к фиксированной длине путем добавления ведущих нулей.
Проанализируем ситуацию: если мы превратим "36,1" в "00036,10", а "36,11" в "00036,11", то обычная строковая сортировка расставит их правильно. Рассмотрим, как это сделать:
КлючСортировки.Пример функции для нормализации строки:
Функция ПолучитьКлючСортировки(ЗначениеСтроки)
Результат = "";
// Упрощенный пример: заменяем только номер дома
// В реальности лучше использовать Регулярные Выражения
МассивЧастей = СтрРазделить(ЗначениеСтроки, ".");
Если МассивЧастей.Количество() > 1 Тогда
НомерЧасть = СокрЛП(МассивЧастей[1]);
// Дополняем нулями до 10 знаков
Пока СтрДлина(НомерЧасть) < 10 Цикл
НомерЧасть = "0" + НомерЧасть;
КонецЦикла;
Результат = МассивЧастей[0] + "." + НомерЧасть;
КонецЕсли;
Возврат Результат;
КонецФункции
Для продвинутых разработчиков, работающих в среде Windows, существует возможность использовать системную функцию StrCmpLogicalW из библиотеки shlwapi.dll. Именно эта функция отвечает за «умную» сортировку имен файлов в проводнике Windows, где "2.txt" всегда идет перед "10.txt".
Выясним причину, почему это удобно: вам не нужно менять данные или создавать колонки. Однако этот метод требует написания внешней компоненты или использования ВызовВнешнейФункции, что делает код платформозависимым. В современной разработке на 1С этот метод используется редко, уступая место программной подготовке ключей сортировки.
Если адреса имеют сложную структуру (например, "д. 10к2 стр. 1"), простым методом СтрРазделить не обойтись. В таких случаях удобно использовать объект VBScript.RegExp. Мы можем извлечь все последовательности цифр, преобразовать их в числа или дополнить нулями, и собрать обратно в «сортировочный индекс».
Рассмотрим пример логики работы с регулярными выражениями:
РегЭкспо = Новый COMОбъект("VBScript.RegExp");
РегЭкспо.Global = Истина;
РегЭкспо.Pattern = "\d+"; // Ищем все вхождения цифр
Matches = РегЭкспо.Execute(ИсходнаяСтрока);
Ключ = ИсходнаяСтрока;
Для каждого Match Из Matches Цикл
ЧислоСтрокой = Match.Value;
НормализованноеЧисло = Формат(Число(ЧислоСтрокой), "ЧГ=0; ЧВН=");
// Заменяем в строке исходное число на дополненное нулями
Ключ = СтрЗаменить(Ключ, ЧислоСтрокой, НормализованноеЧисло);
КонецЦикла;
Выбор конкретного способа зависит от ваших условий:
ПОДСТРОКА и НАЙТИ (хотя в запросах 1С это реализовать сложнее всего).Важный совет: При преобразовании строк типа "36,5" в числа, всегда обращайте внимание на разделитель дробной части. В разных региональных настройках это может быть как точка, так и запятая. Используйте СтрЗаменить(Строка, ",", ".") перед вызовом функции Число(), чтобы избежать ошибок выполнения кода.