Как извлечь размер из наименования номенклатуры и записать его в артикул?

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

При ведении каталога товаров часто возникает задача нормализации данных. Например, техническое задание требует извлечь размер изделия из наименования (вида «Картонная коробка р.-17,5» или «Болт д. 10») и поместить его в реквизит Артикул. Разберем по шагам, как правильно реализовать подобный алгоритм на встроенном языке 1С, избежать типичных ошибок при работе со строками и оптимизировать производительность при массовой обработке справочника.

Типичные ошибки при первичном написании алгоритма

Проанализируем распространенные недочеты, которые часто допускают разработчики при решении этой задачи:

  1. Некорректная строковая арифметика с функцией Прав(). Попытка вычислить длину остатка строки через разность общей длины и позиции вхождения часто приводит к смещению индексов или падению по ошибке выхода за границы строки, если префикс не найден. Намного проще и надежнее использовать функцию Сред().
  2. Обращение через точку в цикле (неявные запросы). Конструкция Выборка.Ссылка.Наименование заставляет платформу выполнять отдельный запрос к базе данных на каждой итерации цикла. Все необходимые поля следует выбирать сразу в запросе.
  3. Повторный вызов строковых функций. Вызов Найти() дважды для одной и той же проверки и последующего вырезания подстроки неоправданно расходует процессорное время. Результат поиска следует сохранять в переменную.
  4. Пустая запись объектов. Если в коде пропущено присвоение значения объекту, метод Записать() перезапишет элемент без изменений, потратив ресурсы СУБД впустую.

Вариант 1. Классический строковый разбор с поиском подстроки

Рассмотрим базовый алгоритм, в котором мы ищем заданный префикс (например, «р.-»), вычисляем позицию первого символа значения и обрезаем строку. Для надежности также используем функцию НРег(), чтобы не зависеть от регистра символов при проверке.

Посмотрим на пример оптимальной реализации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка,
    |	Номенклатура.Наименование КАК Наименование,
    |	Номенклатура.Артикул КАК Артикул
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |	Номенклатура.Ссылка В ИЕРАРХИИ(&Папка)
    |	И НЕ Номенклатура.ЭтоГруппа
    |	И НЕ Номенклатура.ПометкаУдаления";

Запрос.УстановитьПараметр("Папка", ПапкаНоменклатуры);
Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    ИмяНоменклатуры = Выборка.Наименование;
    ИмяВНижнемРегистре = НРег(ИмяНоменклатуры);
    
    Префикс = "р.-";
    Позиция = Найти(ИмяВНижнемРегистре, Префикс);
    
    Если Позиция > 0 Тогда
        // Смещаем позицию на длину префикса, чтобы получить сам размер
        НовыйАртикул = СокрЛП(Сред(ИмяНоменклатуры, Позиция + СтрДлина(Префикс)));
        
        // Проверяем, изменилось ли значение, чтобы не записывать лишний раз
        Если ЗначениеЗаполнено(НовыйАртикул) И Выборка.Артикул <> НовыйАртикул Тогда
            ОбъектНоменклатура = Выборка.Ссылка.ПолучитьОбъект();
            ОбъектНоменклатура.ОбменДанными.Загрузка = Истина;
            ОбъектНоменклатура.Артикул = НовыйАртикул;
            ОбъектНоменклатура.Записать();
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Вариант 2. Обработка множества префиксов через массив или список

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

Посмотрим, как выглядит универсальный код:


// Формируем список возможных префиксов в нижнем регистре
СписокПрефиксов = Новый Массив;
СписокПрефиксов.Добавить("диам. ");
СписокПрефиксов.Добавить("диам.");
СписокПрефиксов.Добавить("д.-");
СписокПрефиксов.Добавить("д-");
СписокПрефиксов.Добавить("д. ");
СписокПрефиксов.Добавить("р.-");
СписокПрефиксов.Добавить("р-");

Пока Выборка.Следующий() Цикл
    ИмяНоменклатуры = Выборка.Наименование;
    ИмяВНижнемРегистре = НРег(ИмяНоменклатуры);
    НовыйАртикул = "";
    
    Для Каждого Префикс Из СписокПрефиксов Цикл
        Позиция = Найти(ИмяВНижнемРегистре, Префикс);
        Если Позиция > 0 Тогда
            НовыйАртикул = СокрЛП(Сред(ИмяНоменклатуры, Позиция + СтрДлина(Префикс)));
            Прервать; // Префикс найден, выходим из внутреннего цикла
        КонецЕсли;
    КонецЦикла;
    
    Если ЗначениеЗаполнено(НовыйАртикул) И Выборка.Артикул <> НовыйАртикул Тогда
        ОбъектНоменклатура = Выборка.Ссылка.ПолучитьОбъект();
        ОбъектНоменклатура.ОбменДанными.Загрузка = Истина;
        ОбъектНоменклатура.Артикул = НовыйАртикул;
        ОбъектНоменклатура.Записать();
    КонецЕсли;
КонецЦикла;

Вариант 3. Использование регулярных выражений (RegExp)

Функция Сред() считывает весь остаток строки до самого конца. Если после размера в наименовании указаны другие параметры (например, «Коробка р.-17,5 белая 100 шт»), в артикул попадет весь оставшийся текст. Чтобы выделить исключительно числовое значение размера (включая дробную часть с точкой или запятой), проанализируем использование регулярных выражений.

В современных версиях платформы (начиная с 8.3.23) можно применить функцию СтрНайтиПоРегулярномуВыражению(). Для более ранних платформ на операционных системах Windows задачу можно решить через COM-объект VBScript.RegExp:


RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Истина;
RegExp.Global = Ложь;
// Ищем префиксы р, д, диам и захватываем само число (целое или дробное)
RegExp.Pattern = "(?:р|д|диам)\.?\s*[-–—]?\s*(\d+(?:[,\.]\d+)?)";

Пока Выборка.Следующий() Цикл
    Матчи = RegExp.Execute(Выборка.Наименование);
    Если Матчи.Count > 0 Тогда
        // Извлекаем значение первой захваченной группы
        Размер = Матчи.Item(0).SubMatches.Item(0);
        
        Если Выборка.Артикул <> Размер Тогда
            ОбъектНоменклатура = Выборка.Ссылка.ПолучитьОбъект();
            ОбъектНоменклатура.ОбменДанными.Загрузка = Истина;
            ОбъектНоменклатура.Артикул = Размер;
            ОбъектНоменклатура.Записать();
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Рекомендации по безопасной обработке справочника

  1. Отключение лишних проверок: флаг ОбменДанными.Загрузка = Истина отключает тяжелые подписки на события и встроенные проверки в модуле объекта при записи, ускоряя пакетную операцию в разы.
  2. Контроль длины реквизита: стандартная длина поля Артикул в типовых конфигурациях составляет от 25 до 50 символов. Если алгоритм по ошибке захватит длинную строку, платформа обрежет её.
  3. Проверка на совпадение: всегда сравнивайте вычисленный результат с текущим значением в базе (Выборка.Артикул <> НовыйАртикул). Это исключает бесполезную запись и перезапись блокировок.
  4. Обработка пробельных символов: при копировании текста из прайсов часто встречается неразрывный пробел (код 160). Для очистки строковых данных полезно предварительно заменить Символы.НПП на стандартный пробел.
← На главную