Как убрать лишние пробелы в наименованиях справочника в 1С?

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

При наведении порядка в справочниках 1С (особенно в справочнике Номенклатура) часто возникает задача очистки наименований от лишних пробелов. Задвоенные пробелы между словами, пробелы в начале или конце строки, а также скрытые спецсимволы мешают корректному поиску и портят внешний вид печатных форм. Проанализируем типичную ситуацию: при попытке написать обработку для массовой очистки разработчики сталкиваются с тем, что пробелы не удаляются до конца, а система выдает ошибку «Реквизит недоступен для группы». Разберем по шагам, почему это происходит и как правильно решить данную задачу несколькими способами.

Исправление ошибки: Реквизит недоступен для группы

Рассмотрим первую проблему, с которой можно столкнуться при иерархическом обходе справочника. Если вы используете метод Справочники.Номенклатура.ВыбратьИерархически(), в выборку попадают не только сами элементы (товары), но и группы (папки). У групп справочника Номенклатура обычно нет реквизита НаименованиеПолное.

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


Выборка = Справочники.Номенклатура.ВыбратьИерархически();
Пока Выборка.Следующий() Цикл
    Объект = Выборка.ПолучитьОбъект();
    Объект.НаименованиеПолное = СтрЗаменить(Объект.НаименованиеПолное, "  ", " "); // Ошибка здесь!
    Объект.Записать();
КонецЦикла;

Чтобы избежать прерывания обработки, перед обращением к специфичным для элементов реквизитам, выясним, является ли текущая запись группой. Для этого воспользуемся свойством ЭтоГруппа:


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

Проблема стандартной замены пробелов

Стандартный подход начинающих разработчиков — использовать функцию СтрЗаменить для замены двух пробелов на один. Посмотрим, почему это работает плохо:

НаименованиеНоменклатуры = СтрЗаменить(НаименованиеНоменклатуры, " ", " ");

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

Решение 1: Использование СтрРазделить и СтрСоединить (Элегантный метод)

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


// Разбиваем строку на массив слов
// Третий параметр "Ложь" указывает платформе не добавлять в массив пустые строки, 
// которые образуются из-за идущих подряд пробелов.
МассивСлов = СтрРазделить(Объект.Наименование, " ", Ложь);

// Собираем слова обратно, разделяя их ровно одним пробелом
Объект.Наименование = СтрСоединить(МассивСлов, " ");

Однако на практике в наименованиях часто скрываются неразрывные пробелы и знаки табуляции, которые визуально выглядят как пустое место, но символ пробела ими не является. Обычный СтрРазделить их пропустит. Доработаем наш алгоритм, добавив в строку разделителей спецсимволы Символы.НПП (неразрывный пробел) и Символы.Таб (знак табуляции):


СтрокаРазделителей = " " + Символы.НПП + Символы.Таб;
МассивСлов = СтрРазделить(Объект.Наименование, СтрокаРазделителей, Ложь);
Объект.Наименование = СтрСоединить(МассивСлов, " ");

Этот метод работает безотказно и удаляет любые скопления пробельных символов. Для выполнения написанного кода над объектами непосредственно в пользовательском режиме подойдёт шаблон обработки данных по произвольному алгоритму.

Решение 2: Использование регулярных выражений (Современный метод)

Разберем еще один, более профессиональный вариант. Исторически для сложных замен в 1С (под Windows) использовали COM-объект VBScript.RegExp. Однако, начиная с платформы 1С версии 8.3.23, разработчикам стал доступен нативный объект РегулярноеВыражение. Это значит, что теперь код будет кроссплатформенным и быстро отработает даже на серверах Linux.

Рассмотрим подробнее логику работы: нам нужно найти все последовательности пробельных символов (обозначаются метасимволом \s+, который автоматически включает обычные пробелы, табуляции и неразрывные пробелы) и заменить их на один пробел.


// Создаем объект регулярного выражения. 
// Шаблон "\s+" означает: найти один или более пробельных символов подряд.
РегВыражение = Новый РегулярноеВыражение("\s+");

// Применяем регулярное выражение к наименованию
ОчищенноеИмя = РегВыражение.Заменить(Объект.Наименование, " ");

// Удаляем пробелы по краям, если они остались
Объект.Наименование = СокрЛП(ОчищенноеИмя);

Решение 3: Готовые функции Библиотеки Стандартных Подсистем (БСП)

Если ваша конфигурация 1С построена на базе БСП (а это УТ 11, ERP, КА, БП 3.0, ЗУП 3.1 и другие современные типовые решения), нет необходимости писать код с нуля. Разберем использование готовых, отлаженных механизмов библиотеки.

В общем модуле СтроковыеФункцииКлиентСервер существует специальная процедура для решения в точности такой же задачи. Посмотрим на пример вызова:


// Функция удалит все дублирующиеся пробелы внутри строки
НовоеНаименование = СтроковыеФункцииКлиентСервер.УдалитьПовторяющиесяСимволы(Объект.Наименование, " ");

// Дополнительно применяем СокрЛП для очистки краев
Объект.Наименование = СокрЛП(НовоеНаименование);

Решение 4: Циклическая замена (Классический подход)

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

Не забудем сначала привести неразрывные пробелы к обычным, иначе цикл зациклится или не отработает корректно:


Текст = Объект.Наименование;

// Заменяем спецсимволы на обычный пробел
Текст = СтрЗаменить(Текст, Символы.НПП, " ");
Текст = СтрЗаменить(Текст, Символы.Таб, " ");

// Крутим цикл, пока в строке есть хотя бы два пробела подряд
Пока СтрНайти(Текст, "  ") > 0 Цикл
    Текст = СтрЗаменить(Текст, "  ", " ");
КонецЦикла;

// Очищаем края и возвращаем в объект
Объект.Наименование = СокрЛП(Текст);

Итоги: Собираем финальную обработку

Проанализировав все способы, соберем самую надежную и элегантную процедуру обработки элементов справочника Номенклатура, которая гарантированно очистит и Наименование, и НаименованиеПолное.


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

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

← На главную