При работе со строковыми данными в платформах 1С программисты регулярно сталкиваются с проблемой алфавитно-цифровой сортировки. Типовой метод сортировки ТаблицаЗначений.Сортировать() для текстового поля организует строки по символьным кодам. В результате номера квартир или домов выстраиваются в некорректном с точки зрения человека порядке: 1, 11, 12... 19, 2, 20, 21.
Прямое приведение значения к типу Число невозможно, так как в реальных базах данных часто встречаются адреса с литерами, дробями и суффиксами (например, 19Б, 12/1 или 15 пом. 2). В этой статье мы подробно разберем несколько эффективных алгоритмов естественной сортировки (Natural Sort Order) в 1С.
Проанализируем ситуацию на базовом уровне. Строковый тип данных сравнивает символы поочередно слева направо. Символ "1" имеет меньший код, чем символ "2". Поэтому строка "180" при стандартном сравнении считается "меньше", чем строка "2", так как первый символ первого числа меньше первого символа второго числа.
Чтобы организовать правильный (натуральный) порядок отображения, необходимо разделить значение на основной числовой блок и буквенно-символьный суффикс либо нормализовать ширину числовых блоков.
Наиболее популярный и наглядный подход — добавление в таблицу значений двух служебных колонок: для основной числовой части и для литеры (буквенного суффикса).
Рассмотрим по шагам реализацию этого метода:
ТаблицаЗначений две временные колонки: ЧисловаяЧасть (тип Число) и БуквеннаяЧасть (тип Строка).Посмотрим на пример кода на встроенном языке 1С:
// Добавляем служебные колонки для корректной сортировки
ТаблицаКвартир.Колонки.Добавить("ЧисловаяЧасть", Новый ОписаниеТипов("Число"));
ТаблицаКвартир.Колонки.Добавить("БуквеннаяЧасть", Новый ОписаниеТипов("Строка"));
Для Каждого Стр Из ТаблицаКвартир Цикл
НомерСтр = СокрЛП(Стр.НомерКвартиры);
ИндексСимвола = 1;
ДлинаСтр = СтрДлина(НомерСтр);
// Перебираем символы, пока идут цифры
Пока ИндексСимвола <= ДлинаСтр Цикл
КодСимвола = КодСимвола(НомерСтр, ИндексСимвола);
// Проверяем принадлежность коду ASCII цифр (48 - "0", 57 - "9")
Если Не (КодСимвола >= 48 И КодСимвола <= 57) Тогда
Прервать;
КонецЕсли;
ИндексСимвола = ИндексСимвола + 1;
КонецЦикла;
Если ИндексСимвола > 1 Тогда
Стр.ЧисловаяЧасть = Число(Лев(НомерСтр, ИндексСимвола - 1));
Стр.БуквеннаяЧасть = Сред(НомерСтр, ИндексСимвола);
Иначе
Стр.ЧисловаяЧасть = 0;
Стр.БуквеннаяЧасть = НомерСтр;
КонецЕсли;
КонецЦикла;
// Выполняем сортировку сначала по числу, затем по букве
ТаблицаКвартир.Сортировать("ЧисловаяЧасть Возр, БуквеннаяЧасть Возр");
После вызова метода Сортировать() данные расположатся в правильном порядке: 2, 19, 19А, 19Б, 180.
Если нет желания плодить несколько колонок, можно обойтись добавлением одного строкового поля-ключа. Суть метода заключается в приведении числовой части строки к фиксированной длине путем дополнения лидирующими нулями.
Разберем пример:
"2" превращается в "0000000002"."19Б" превращается в "0000000019Б"."180" превращается в "0000000180".При стандартной строковой сортировке строка "0000000002" встанет раньше "0000000019Б", а "0000000019Б" окажется раньше "0000000180", что полностью решает задачу.
Рассмотрим функцию формирования такого ключа:
Функция ПолучитьКлючСортировки(Знач НомерКвартиры)
НомерСтр = СокрЛП(НомерКвартиры);
Индекс = 1;
ДлинаСтр = СтрДлина(НомерСтр);
Пока Индекс <= ДлинаСтр Цикл
КодСимв = КодСимвола(НомерСтр, Индекс);
Если Не (КодСимв >= 48 И КодСимв <= 57) Тогда
Прервать;
КонецЕсли;
Индекс = Индекс + 1;
КонецЦикла;
ЧисловаяЧасть = Лев(НомерСтр, Индекс - 1);
БуквеннаяЧасть = Сред(НомерСтр, Индекс);
Если ЗначениеЗаполнено(ЧисловаяЧасть) Тогда
// Форматируем число до 10 знаков с лидирующими нулями
ЧислоВыровненное = Формат(Число(ЧисловаяЧасть), "ЧЦ=10; ЧДЦ=0; ЧЧ=0; ЧГ=0");
Возврат ЧислоВыровненное + БуквеннаяЧасть;
Иначе
Возврат НомерСтр;
КонецЕсли;
КонецФункции
Когда номера квартир содержат сложные комбинации с корпусами, строениями или дробями (например, 19-Б, 12/3, 15 пом. 2), обычного поиска символов может оказаться недостаточно. В таких случаях удобно задействовать объект COMObject("VBScript.RegExp").
Проанализируем применение регулярных выражений:
РегВыр = Новый COMОбъект("VBScript.RegExp");
РегВыр.IgnoreCase = Истина;
РегВыр.Global = Ложь;
// Шаблон находит начальную группу цифр и остаток строки
РегВыр.Pattern = "^(\d+)(.*)$";
Для Каждого Стр Из ТаблицаЗначений Цикл
Совпадения = РегВыр.Execute(СокрЛП(Стр.НомерКвартиры));
Если Совпадения.Количество() > 0 Тогда
Группы = Совпадения.Item(0).SubMatches;
ЧислоЧасть = Число(Группы.Item(0));
БукваЧасть = Группы.Item(1);
Стр.ИндексСортировки = Формат(ЧислоЧасть, "ЧЦ=10; ЧДЦ=0; ЧЧ=0; ЧГ=0") + БукваЧасть;
Иначе
Стр.ИндексСортировки = Стр.НомерКвартиры;
КонецЕсли;
КонецЦикла;
ТаблицаЗначений.Сортировать("ИндексСортировки Возр");
Если задача возникает при построении отчета в Системе компоновки данных (СКД), изменять таблицу значений в коде не требуется. Для вывода данных в естественном порядке применяют **Вычисляемые поля** СКД.
Разберем порядок действий в компоновщике макета:
ИндексСортировкиКвартиры.МойОбщийМодуль.ПолучитьКлючСортировки(НомерКвартиры).ИндексСортировкиКвартиры по возрастанию.В специализированных конфигурациях (например, в решениях для ЖКХ и управления недвижимостью) проблему решают на этапе проектирования структуры базы данных. Вместо единого текстового реквизита создают три независимых поля:
НомерКвартирыЧисло (тип Число) — основная числовая часть;Литера (тип Строка) — буква квартиры (А, Б, В);ДробьКвартиры (тип Число или Строка) — номер корпуса или дроби.Пользовательское представление (например, "19Б/1") сшивается программно при выводе на форму с помощью метода СформироватьПредставление(). Такой подход гарантирует мгновенную сортировку средств СУБД с использованием стандартных индексов таблицы без дополнительных вычислений в runtime.