При наведении порядка в справочниках 1С (особенно в справочнике Номенклатура) часто возникает задача очистки наименований от лишних пробелов. Задвоенные пробелы между словами, пробелы в начале или конце строки, а также скрытые спецсимволы мешают корректному поиску и портят внешний вид печатных форм. Проанализируем типичную ситуацию: при попытке написать обработку для массовой очистки разработчики сталкиваются с тем, что пробелы не удаляются до конца, а система выдает ошибку «Реквизит недоступен для группы». Разберем по шагам, почему это происходит и как правильно решить данную задачу несколькими способами.
Рассмотрим первую проблему, с которой можно столкнуться при иерархическом обходе справочника. Если вы используете метод Справочники.Номенклатура.ВыбратьИерархически(), в выборку попадают не только сами элементы (товары), но и группы (папки). У групп справочника Номенклатура обычно нет реквизита НаименованиеПолное.
Посмотрим на пример кода, который вызывает ошибку:
Выборка = Справочники.Номенклатура.ВыбратьИерархически();
Пока Выборка.Следующий() Цикл
Объект = Выборка.ПолучитьОбъект();
Объект.НаименованиеПолное = СтрЗаменить(Объект.НаименованиеПолное, " ", " "); // Ошибка здесь!
Объект.Записать();
КонецЦикла;
Чтобы избежать прерывания обработки, перед обращением к специфичным для элементов реквизитам, выясним, является ли текущая запись группой. Для этого воспользуемся свойством ЭтоГруппа:
Пока Выборка.Следующий() Цикл
Объект = Выборка.ПолучитьОбъект();
// Очищаем обычное наименование (оно есть и у групп, и у элементов)
Объект.Наименование = СокрЛП(Объект.Наименование);
// Проверяем, что перед нами элемент справочника
Если НЕ Объект.ЭтоГруппа Тогда
Объект.НаименованиеПолное = СокрЛП(Объект.НаименованиеПолное);
КонецЕсли;
Попытка
Объект.Записать();
Исключение
Сообщить("Невозможно записать объект: " + Объект.Ссылка);
КонецПопытки;
КонецЦикла;
Стандартный подход начинающих разработчиков — использовать функцию СтрЗаменить для замены двух пробелов на один. Посмотрим, почему это работает плохо:
НаименованиеНоменклатуры = СтрЗаменить(НаименованиеНоменклатуры, " ", " ");
Проанализируем ситуацию: если между словами будет три пробела, функция заменит первые два на один, и в итоге останется два пробела. Если будет четыре пробела — останется два. То есть за один проход такой код не решает проблему многократных пробелов. Кроме того, стандартная функция СокрЛП() удаляет пробелы только слева (в начале) и справа (в конце) строки, но никак не влияет на пробелы внутри текста.
Рассмотрим очень красивое и эффективное решение проблемы множественных пробелов с использованием встроенных функций работы со строками и массивами. Суть метода заключается в том, чтобы разбить строку в массив по разделителю (пробелу), при этом игнорируя пустые подстроки, а затем собрать этот массив обратно в строку с одним пробелом.
// Разбиваем строку на массив слов
// Третий параметр "Ложь" указывает платформе не добавлять в массив пустые строки,
// которые образуются из-за идущих подряд пробелов.
МассивСлов = СтрРазделить(Объект.Наименование, " ", Ложь);
// Собираем слова обратно, разделяя их ровно одним пробелом
Объект.Наименование = СтрСоединить(МассивСлов, " ");
Однако на практике в наименованиях часто скрываются неразрывные пробелы и знаки табуляции, которые визуально выглядят как пустое место, но символ пробела ими не является. Обычный СтрРазделить их пропустит. Доработаем наш алгоритм, добавив в строку разделителей спецсимволы Символы.НПП (неразрывный пробел) и Символы.Таб (знак табуляции):
СтрокаРазделителей = " " + Символы.НПП + Символы.Таб;
МассивСлов = СтрРазделить(Объект.Наименование, СтрокаРазделителей, Ложь);
Объект.Наименование = СтрСоединить(МассивСлов, " ");
Этот метод работает безотказно и удаляет любые скопления пробельных символов. Для выполнения написанного кода над объектами непосредственно в пользовательском режиме подойдёт шаблон обработки данных по произвольному алгоритму.
Разберем еще один, более профессиональный вариант. Исторически для сложных замен в 1С (под Windows) использовали COM-объект VBScript.RegExp. Однако, начиная с платформы 1С версии 8.3.23, разработчикам стал доступен нативный объект РегулярноеВыражение. Это значит, что теперь код будет кроссплатформенным и быстро отработает даже на серверах Linux.
Рассмотрим подробнее логику работы: нам нужно найти все последовательности пробельных символов (обозначаются метасимволом \s+, который автоматически включает обычные пробелы, табуляции и неразрывные пробелы) и заменить их на один пробел.
// Создаем объект регулярного выражения.
// Шаблон "\s+" означает: найти один или более пробельных символов подряд.
РегВыражение = Новый РегулярноеВыражение("\s+");
// Применяем регулярное выражение к наименованию
ОчищенноеИмя = РегВыражение.Заменить(Объект.Наименование, " ");
// Удаляем пробелы по краям, если они остались
Объект.Наименование = СокрЛП(ОчищенноеИмя);
Если ваша конфигурация 1С построена на базе БСП (а это УТ 11, ERP, КА, БП 3.0, ЗУП 3.1 и другие современные типовые решения), нет необходимости писать код с нуля. Разберем использование готовых, отлаженных механизмов библиотеки.
В общем модуле СтроковыеФункцииКлиентСервер существует специальная процедура для решения в точности такой же задачи. Посмотрим на пример вызова:
// Функция удалит все дублирующиеся пробелы внутри строки
НовоеНаименование = СтроковыеФункцииКлиентСервер.УдалитьПовторяющиесяСимволы(Объект.Наименование, " ");
// Дополнительно применяем СокрЛП для очистки краев
Объект.Наименование = СокрЛП(НовоеНаименование);
Если вы работаете в старой конфигурации без БСП, на старой платформе без регулярных выражений и по какой-то причине не хотите использовать метод с массивами, разберем классический подход с циклом Пока. Мы будем заменять два пробела на один до тех пор, пока двойные пробелы не исчезнут из строки полностью.
Не забудем сначала привести неразрывные пробелы к обычным, иначе цикл зациклится или не отработает корректно:
Текст = Объект.Наименование;
// Заменяем спецсимволы на обычный пробел
Текст = СтрЗаменить(Текст, Символы.НПП, " ");
Текст = СтрЗаменить(Текст, Символы.Таб, " ");
// Крутим цикл, пока в строке есть хотя бы два пробела подряд
Пока СтрНайти(Текст, " ") > 0 Цикл
Текст = СтрЗаменить(Текст, " ", " ");
КонецЦикла;
// Очищаем края и возвращаем в объект
Объект.Наименование = СокрЛП(Текст);
Проанализировав все способы, соберем самую надежную и элегантную процедуру обработки элементов справочника Номенклатура, которая гарантированно очистит и Наименование, и НаименованиеПолное.
&НаСервере
Процедура ОчиститьПробелыНоменклатурыНаСервере()
Выборка = Справочники.Номенклатура.ВыбратьИерархически();
СтрокаРазделителей = " " + Символы.НПП + Символы.Таб;
Пока Выборка.Следующий() Цикл
Объект = Выборка.ПолучитьОбъект();
НужноЗаписать = Ложь;
// Очищаем Рабочее наименование
МассивСлов = СтрРазделить(Объект.Наименование, СтрокаРазделителей, Ложь);
НовоеИмя = СокрЛП(СтрСоединить(МассивСлов, " "));
Если Объект.Наименование <> НовоеИмя Тогда
Объект.Наименование = НовоеИмя;
НужноЗаписать = Истина;
КонецЕсли;
// Очищаем Полное наименование только для элементов
Если НЕ Объект.ЭтоГруппа Тогда
МассивСловПолного = СтрРазделить(Объект.НаименованиеПолное, СтрокаРазделителей, Ложь);
НовоеПолноеИмя = СокрЛП(СтрСоединить(МассивСловПолного, " "));
Если Объект.НаименованиеПолное <> НовоеПолноеИмя Тогда
Объект.НаименованиеПолное = НовоеПолноеИмя;
НужноЗаписать = Истина;
КонецЕсли;
КонецЕсли;
Если НужноЗаписать Тогда
Попытка
// ОбменДанными.Загрузка = Истина позволяет пропустить
// лишние проверки и ускорить запись, если это просто очистка данных
Объект.ОбменДанными.Загрузка = Истина;
Объект.Записать();
Исключение
Сообщить("Ошибка записи элемента: " + Объект.Ссылка);
КонецПопытки;
КонецЕсли;
КонецЦикла;
Сообщить("Обработка завершена!");
КонецПроцедуры
Используя представленные выше алгоритмы, вы сможете быстро и безопасно навести порядок в любых справочниках 1С, удалив весь лишний "пробельный мусор". Рекомендуем использовать методы работы с массивами или регулярные выражения, так как они работают быстрее и эффективнее циклических замен. При необходимости массовой корректировки данных без написания кода можно воспользоваться обработкой для группового изменения текстовых реквизитов справочников.