При работе с базами данных 1С мы часто сталкиваемся с необходимостью поддерживать чистоту и порядок в справочниках. Одной из распространенных проблем является наличие лишних пробелов или других "пробельных" символов в наименованиях элементов, которые могут мешать поиску, сортировке или формированию отчетов. В этой статье мы подробно разберем, как эффективно искать и удалять такие символы, используя возможности языка запросов и встроенного языка 1С.
Давайте вместе выясним, почему стандартные методы поиска могут не сработать с первого раза, какие типы пробелов существуют и как их корректно обрабатывать, а также как оптимизировать процесс массовой замены.
Рассмотрим типовую ситуацию, когда мы пытаемся найти элементы справочника, наименования которых содержат пробелы. Изначально, можно предположить, что достаточно просто указать пробел в качестве параметра для оператора ПОДОБНО.
Например, если мы формируем запрос следующим образом:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Состав.Наименование,
| Состав.Ссылка
|ИЗ
| Справочник.Состав КАК Состав
|ГДЕ
| Состав.Наименование ПОДОБНО &Наименование";
Запрос.УстановитьПараметр("Наименование", " ");
В этом случае, как показал опыт автора, запрос может возвращать пустую выборку, даже если визуально мы видим пробелы в наименованиях. Причина кроется в том, что оператор ПОДОБНО в сочетании с точным значением параметра, таким как просто " ", ищет строки, которые полностью совпадают с этим параметром или начинаются/заканчиваются на него, если не используются специальные символы-шаблоны. Нам же нужно найти строки, которые содержат пробел в любой части наименования. Для быстрой отладки условий запроса и пакетной обработки полученных данных можно использовать консоль запросов с поддержкой произвольных алгоритмов обработки.
Для того чтобы запрос корректно находил элементы, содержащие пробелы в любой части наименования, нам необходимо использовать специальные символы-шаблоны, которые предоставляет оператор ПОДОБНО. Главный из них – это знак процента (%), который означает любую последовательность символов (включая пустую).
Мы можем использовать его двумя основными способами.
Использование параметра запроса с метасимволами:
Мы формируем параметр таким образом, чтобы он содержал символы процента до и после искомого пробела. Это указывает системе, что до пробела и после него могут быть любые символы.
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Состав.Наименование,
| Состав.Ссылка
|ИЗ
| Справочник.Состав КАК Состав
|ГДЕ
| Состав.Наименование ПОДОБНО &Наименование";
// Правильное использование параметра для поиска строк с пробелом
Запрос.УстановитьПараметр("Наименование", "% %"); // Ищем хотя бы один обычный пробел
После такого изменения запрос успешно найдет все элементы, в наименовании которых присутствует хотя бы один обычный пробел.
Встраивание шаблона напрямую в текст запроса:
Если нет необходимости в динамическом изменении искомого символа, мы можем встроить шаблон прямо в текст запроса. При этом важно помнить о правилах экранирования кавычек в строке запроса: каждая кавычка внутри строкового литерала удваивается.
Запрос.Текст = "ВЫБРАТЬ
| Состав.Наименование,
| Состав.Ссылка
|ИЗ
| Справочник.Состав КАК Состав
|ГДЕ
| Состав.Наименование ПОДОБНО ""% %"""; // Обратите внимание на удвоенные кавычки
Оба этих метода эквивалентны и позволяют корректно выбрать нужные элементы.
Важно понимать, что "пробел" — это не всегда один и тот же символ. Помимо обычного пробела (U+0020), в строках 1С могут встречаться другие символы, которые визуально выглядят как пробелы, но имеют иные коды. Если запрос с "% %" ничего не находит, или замена пробелов не происходит, значит, в наименованиях присутствуют другие пробельные символы. Рассмотрим основные из них:
Неразрывный пробел (Символы.НПП):
Это очень распространенный символ в 1С. Он часто используется для форматирования чисел (например, "1 000 000") или для предотвращения переноса строк в определенных местах. Если вы пытались заменить Символы.НПП, а найден был обычный пробел (как в случае автора), это означает, что в базе присутствуют оба типа или только обычный пробел. Для поиска неразрывного пробела в запросе мы должны использовать Символы.НПП в параметре:
Запрос.УстановитьПараметр("Наименование", "%"+Символы.НПП+"%");
Табуляция (Символы.Таб):
Еще один пробельный символ, который может случайно попасть в наименование. Поиск осуществляется аналогично:
Запрос.УстановитьПараметр("Наименование", "%"+Символы.Таб+"%");
Различные Unicode-пробелы (шпации):
В стандарте Unicode существует множество пробельных символов (круглая, полукруглая, тонкая, волосяная шпация и др.). Их поддержка в 1С может быть ограничена, и найти их напрямую через ПОДОБНО может быть сложно. Для полного контроля и выявления всех возможных "пробельных" символов, включая экзотические Unicode-пробелы, мы можем перебирать строку посимвольно и анализировать код каждого символа.
// Пример посимвольного анализа (вне запроса)
Функция ЕстьПробельныйСимвол(СтрокаДляПроверки)
Для Каждого Символ Из СтрокаДляПроверки Цикл
КодСимвола = КодСимвола(Символ);
// Проверяем, является ли символ пробельным
// Диапазоны Unicode для различных пробельных символов
// Например, обычный пробел (U+0020), неразрывный пробел (U+00A0)
// Табуляция (U+0009), переводы строки (U+000A, U+000D)
// и другие различные шпации (U+2000 - U+200A, U+202F, U+205F, U+3000)
Если КодСимвола = 32 // Обычный пробел
Или КодСимвола = 160 // Неразрывный пробел (NBSP)
Или КодСимвола = 9 // Табуляция (TAB)
// ... можно добавить другие коды, если известно, что они могут присутствовать
Или (КодСимвола >= 8192 И КодСимвола <= 8202) // Диапазон различных шпаций (U+2000 - U+200A)
// ... и так далее
Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
Этот подход более трудоемкий, но дает полный контроль над поиском любого символа по его коду.
Регулярные выражения (начиная с платформы 8.3.23):
Это наиболее мощный инструмент для поиска и обработки строк, включая различные виды пробелов. С помощью регулярных выражений можно найти любой пробельный символ, используя метасимвол \s, который включает в себя обычные пробелы, табуляции, переводы строк и многие другие пробельные символы Unicode. Для этого в 1С доступны функции СтрНайтиПоРегулярномуВыражению, СтрЗаменитьПоРегулярномуВыражению, СтрПодобнаПоРегулярномуВыражению и объект РегулярноеВыражение.
Пример запроса с использованием регулярных выражений для выборки:
// Этот пример предполагает, что мы уже получили Наименования в выборку,
// и теперь фильтруем их или проверяем наличие пробелов.
// Прямое использование регулярных выражений в запросах 1С затруднено
// до появления функций типа СтрПодобнаПоРегулярномуВыражению
// для работы на уровне СУБД, если это реализовано.
// Поэтому чаще всего это делается в цикле по выборке.
// Предположим, мы уже получили РезультатЗапроса
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Если СтрНайтиПоРегулярномуВыражению(Выборка.Наименование, "\s") > 0 Тогда
Сообщить("Элемент с пробелом (любого типа): " + Выборка.Наименование);
// Здесь можно выполнить дальнейшую обработку
КонецЕсли;
КонецЦикла;
В более новых версиях платформы 1С мы можем использовать объект РегулярноеВыражение для более гибкого поиска и замены:
РегВыражение = Новый РегулярноеВыражение("\s"); // \s соответствует любому пробельному символу
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Если РегВыражение.Найти(Выборка.Наименование) Тогда
Сообщить("Элемент с пробельным символом: " + Выборка.Наименование);
КонецЕсли;
КонецЦикла;
После того как мы успешно нашли элементы, содержащие нежелательные пробельные символы, следующим шагом является их удаление или замена. Изначально, автор темы столкнулся с тем, что даже после успешного поиска, замена с Символы.НПП не работала, так как в наименованиях были обычные пробелы.
Давайте рассмотрим различные методы замены.
Замена обычных пробелов:
Если вы уверены, что в наименовании присутствуют только обычные пробелы, используйте следующую конструкцию:
НовоеЗначениеЭлемента.Наименование = СтрЗаменить(Выборка.Наименование, " ", "");
Это было именно то решение, которое помогло автору темы.
Замена неразрывных пробелов:
Если требуется удалить неразрывные пробелы:
НовоеЗначениеЭлемента.Наименование = СтрЗаменить(Выборка.Наименование, Символы.НПП, "");
Удаление табуляции:
Для удаления символов табуляции:
НовоеЗначениеЭлемента.Наименование = СтрЗаменить(Выборка.Наименование, Символы.Таб, "");
Удаление пробелов в начале/конце строки:
Для удаления начальных или конечных пробелов, которые могут мешать поиску или сортировке, используем специальные функции:
СокрЛ(Строка) — удаляет пробелы слева.СокрП(Строка) — удаляет пробелы справа.СокрЛП(Строка) — удаляет пробелы с обеих сторон строки.
НовоеЗначениеЭлемента.Наименование = СокрЛП(Выборка.Наименование);
Это очень полезно, так как система 1С может автоматически обрезать пробелы в начале и конце наименований при программном получении элемента, но они все равно могут существовать в базе данных и вызвать проблемы.
Комплексное удаление нескольких видов пробелов:
Для обеспечения чистоты данных рекомендуется последовательно заменять все известные вам пробельные символы. Мы можем создать вспомогательную функцию для такой очистки.
Функция ОчиститьНаименованиеОтПробелов(ИсходноеНаименование)
ОчищенноеНаименование = ИсходноеНаименование;
// 1. Заменяем неразрывные пробелы
ОчищенноеНаименование = СтрЗаменить(ОчищенноеНаименование, Символы.НПП, "");
// 2. Заменяем табуляции
ОчищенноеНаименование = СтрЗаменить(ОчищенноеНаименование, Символы.Таб, "");
// 3. Заменяем обычные пробелы (если нужно удалить их полностью или привести к одному)
// Если нужно удалить все пробелы:
ОчищенноеНаименование = СтрЗаменить(ОчищенноеНаименование, " ", "");
// Если нужно оставить только один пробел между словами:
// ОчищенноеНаименование = СокрЛП(СтрЗаменить(ОчищенноеНаименование, " ", " "));
// Цикл для удаления множественных пробелов (например, "текст с многими пробелами" -> "текст с многими пробелами")
Пока СтрНайти(ОчищенноеНаименование, " ") > 0 Цикл
ОчищенноеНаименование = СтрЗаменить(ОчищенноеНаименование, " ", " ");
КонецЦикла;
// 4. Удаляем начальные и конечные пробелы
ОчищенноеНаименование = СокрЛП(ОчищенноеНаименование);
Возврат ОчищенноеНаименование;
КонецФункции
И затем используем ее в цикле:
Пока Выборка.Следующий() Цикл
НовоеЗначениеЭлемента = Выборка.Ссылка.ПолучитьОбъект();
НовоеЗначениеЭлемента.Наименование = ОчиститьНаименованиеОтПробелов(Выборка.Наименование);
НовоеЗначениеЭлемента.Записать();
КонецЦикла;
Использование регулярных выражений для удаления всех пробельных символов (платформа 8.3.23+):
С помощью регулярных выражений мы можем удалить все виды пробельных символов (включая табуляции, переносы строк и различные Unicode-шпации) одним махом, используя метасимвол \s. Это наиболее универсальный и мощный метод.
РегВыражение = Новый РегулярноеВыражение("\s+"); // \s+ соответствует одному или более пробельным символам
// Если нужно удалить все пробельные символы без исключения:
НовоеЗначениеЭлемента.Наименование = РегВыражение.Заменить(Выборка.Наименование, "");
// Если нужно заменить последовательности пробелов на один обычный пробел:
// Сначала удаляем НПП и Таб отдельно, затем чистим обычные пробелы
ОчищенноеНаименование = СтрЗаменить(Выборка.Наименование, Символы.НПП, " ");
ОчищенноеНаименование = СтрЗаменить(ОчищенноеНаименование, Символы.Таб, " ");
// Затем заменяем все последовательности пробелов на один пробел
РегВыражениеДляОбычныхПробелов = Новый РегулярноеВыражение(" +"); // Один или более обычных пробелов
ОчищенноеНаименование = РегВыражениеДляОбычныхПробелов.Заменить(ОчищенноеНаименование, " ");
НовоеЗначениеЭлемента.Наименование = СокрЛП(ОчищенноеНаименование);
Использование регулярных выражений значительно упрощает код и делает его более надежным для обработки разнообразных "грязных" строк.
Когда нам предстоит обновить большое количество элементов справочника, каждая операция записи (НовоеЗначениеЭлемента.Записать()) может быть достаточно ресурсоемкой. Для ускорения процесса и обеспечения целостности данных при возможных сбоях мы настоятельно рекомендуем использовать механизм транзакций.
Транзакции позволяют объединить несколько операций записи в одну логическую единицу. Если внутри транзакции произойдет ошибка, все изменения можно будет отменить. Кроме того, групповая запись в транзакции часто выполняется быстрее, так как снижается нагрузка на СУБД.
УстановитьПривилегированныйРежим(Истина); // Внешняя обработка или некоторые сценарии могут потребовать привилегированный режим
н = 0; // Счетчик элементов в текущей транзакции
НачатьТранзакцию();
Пока Выборка.Следующий() Цикл
НовоеЗначениеЭлемента = Выборка.Ссылка.ПолучитьОбъект();
// Здесь применяем выбранный нами метод очистки наименования:
НовоеЗначениеЭлемента.Наименование = СтрЗаменить(Выборка.Наименование, " ", ""); // Пример: замена обычных пробелов
// Или используем нашу функцию ОчиститьНаименованиеОтПробелов()
// НовоеЗначениеЭлемента.Наименование = ОчиститьНаименованиеОтПробелов(Выборка.Наименование);
НовоеЗначениеЭлемента.Записать();
н = н + 1;
Если н = 100 Тогда // Коммитим транзакцию каждые 100 элементов
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
н = 0;
КонецЕсли;
КонецЦикла;
// Фиксируем оставшиеся элементы, если их было меньше 100 или они были последними
Если н > 0 Тогда
ЗафиксироватьТранзакцию();
КонецЕсли;
УстановитьПривилегированныйРежим(Ложь); // Возвращаем обычный режим, если устанавливали привилегированный
В этом примере мы фиксируем транзакцию каждые 100 записей. Число 100 является ориентировочным и может быть скорректировано в зависимости от производительности вашей системы и объемов данных. Слишком маленькое значение будет часто коммитить, слишком большое — увеличит риск потери данных при сбое внутри транзакции. Экспериментируйте для нахождения оптимального значения.
После выполнения всех операций по поиску и замене пробелов критически важно убедиться, что проблема действительно решена. Мы можем выполнить следующие шаги:
Повторный запуск запроса: Запустите тот же запрос на поиск пробелов (например, ПОДОБНО "% %"). Если выборка окажется пустой, это означает, что все обычные пробелы успешно удалены.
Визуальный осмотр: Откройте несколько элементов справочника, которые ранее содержали пробелы, и убедитесь, что их наименования теперь выглядят корректно.
Проверка на другие типы пробелов: Если вы подозреваете наличие других пробельных символов (Символы.НПП, Символы.Таб), выполните запросы на их поиск, чтобы убедиться, что они также были удалены.
Использование конфигуратора: В конфигураторе 1С можно включить отображение пробелов и табуляций (через настройки параметров конфигуратора, закладка "Тексты"). Это поможет "увидеть" непечатаемые символы, если они все еще присутствуют.
Поддержание чистоты данных в справочниках — это залог эффективной и безошибочной работы системы 1С. Мы рассмотрели все основные аспекты поиска, идентификации и удаления различных пробельных символов, а также методы оптимизации массовых операций. Используя эти знания, вы сможете обеспечить порядок в своих базах данных и избежать многих проблем, связанных с некорректными наименованиями. Для массовой корректировки наименований без написания программного кода можно использовать обработку для группового изменения текстовых реквизитов справочников.