При обработке данных, анализе зависимостей при загрузке номенклатуры или нормализации справочников (например, названий подразделений) часто возникает задача очистки текстовых строк от лишних символов. Одной из самых распространенных подзадач является интеллектуальное сравнение и удаление всех цифр из строки. На первый взгляд задача кажется тривиальной, но в платформе 1С существует несколько способов её решения (подобно тому как упрощается работа с СКД), различающихся по скорости работы, совместимости с версиями платформы и элегантности кода.
В этой статье мы подробно разберем, как правильно реализовать функцию удаления чисел, проанализируем ошибки в коде новичков (в чем поможет ИИ-отладчик кода) и рассмотрим современные методы, появившиеся в последних версиях платформы.
Допустим, у нас есть строка: "Отдел продаж №1 (сектор 7)". Нам необходимо получить строку: "Отдел продаж № (сектор )" (или с удалением лишних пробелов, если требуется, но базовая задача — убрать цифры 0-9).
Рассмотрим несколько подходов к решению, от классических до самых современных.
Самый простой и понятный алгоритм, который работает абсолютно на всех версиях платформы 1С:Предприятие (даже на 7.7, с поправкой на синтаксис). Идея заключается в том, чтобы просто перебрать все возможные цифры и заменить их на пустую строку.
В исходном сообщении темы автор предложил достаточно громоздкий код с вложенными циклами и массивами. Давайте проанализируем его ошибку. Автор использовал цикл по длине строки внутри цикла по массиву цифр. Это избыточно, так как функция СтрЗаменить заменяет все вхождения подстроки, а не только первое.
Правильный и лаконичный вариант "в лоб" выглядит так:
Функция УдалитьЦифрыИзСтроки_Классика(Знач ИсходнаяСтрока) Экспорт
// Перебираем цифры от 0 до 9
Для Счетчик = 0 По 9 Цикл
// Преобразуем число в строку и заменяем его на пустоту
ИсходнаяСтрока = СтрЗаменить(ИсходнаяСтрока, Строка(Счетчик), "");
КонецЦикла;
Возврат ИсходнаяСтрока;
КонецФункции
Преимущества:
Недостатки:
СтрЗаменить, даже если цифр в строке нет вообще.Начиная с версии платформы 8.3.6, функция СтрРазделить получила возможность принимать в качестве разделителя не один символ, а строку, где каждый символ считается отдельным разделителем. Это позволяет нам использовать очень изящный метод, который часто применяется в типовых конфигурациях (в БСП).
Суть метода: мы разбиваем строку на массив подстрок, используя все цифры как разделители. При этом сами цифры "исчезают". Затем мы просто склеиваем полученный массив обратно в строку.
Разберем реализацию:
Функция УдалитьЦифрыИзСтроки_БСП(Знач ИсходнаяСтрока) Экспорт
// Определяем строку со всеми символами, которые нужно удалить
Разделители = "0123456789";
// 1. СтрРазделить разбивает текст.
// Параметр Истина/Ложь (третий) отвечает за пропуск пустых строк.
// Если поставить Ложь, то мы сохраним структуру пробелов (если цифры стояли рядом).
МассивПодстрок = СтрРазделить(ИсходнаяСтрока, Разделители, Ложь);
// 2. СтрСоединить склеивает массив обратно.
Возврат СтрСоединить(МассивПодстрок, "");
КонецФункции
В одной строке это выглядит так:
Результат = СтрСоединить(СтрРазделить(Строка, "0123456789", Ложь), "");
Обратите внимание на параметр Ложь в функции СтрРазделить. Если использовать значение Истина (включать пустые строки), то поведение может измениться в зависимости от того, стоят ли цифры подряд или нет, но для задачи простого удаления чаще всего используют именно такую конструкцию.
Почему это быстрее?
Как отметили участники обсуждения, такой подход работает быстрее цикла с СтрЗаменить (примерно на 5-15% на коротких строках и значительно быстрее на длинных), потому что основные операции выполняются внутри платформы на уровне C++, минимизируя интерпретацию кода встроенного языка 1С.
Долгое время для использования регулярных выражений (RegExp) программистам 1С приходилось подключать внешние COM-объекты (например, VBScript.RegExp), что создавало проблемы при работе на серверах Linux. Однако, начиная с версии платформы 8.3.23, разработчики добавили нативные функции для работы с регулярными выражениями.
Это самый современный и правильный способ работы со сложными заменами.
Функция УдалитьЦифрыИзСтроки_RegExp(Знач ИсходнаяСтрока) Экспорт
// [0-9] - шаблон, означающий "любая цифра"
// Заменяем любое вхождение шаблона на пустую строку
Возврат СтрЗаменитьПоРегулярномуВыражению(ИсходнаяСтрока, "[0-9]", "");
КонецФункции
Если у вас платформа ниже 8.3.23, но сервер работает на Windows, можно использовать старый метод через COM-объект, хотя сейчас это уже считается "легаси":
// ТОЛЬКО ДЛЯ WINDOWS и старых платформ
Функция УдалитьЦифрыCOM(Знач ИсходнаяСтрока)
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.Global = Истина; // Искать все вхождения
RegExp.Pattern = "\d"; // \d - это цифра
Возврат RegExp.Replace(ИсходнаяСтрока, "");
КонецФункции
Существует еще один алгоритм, предложенный в обсуждении: проход по строке и проверка каждого символа. Этот метод редко используется для простого удаления, так как он медленнее встроенных функций (из-за большого количества итераций цикла в 1С), но он полезен, если логика удаления сложная (например, "удалить цифру, только если она не стоит в начале слова").
Пример реализации (с оптимизацией "с конца"):
Функция УдалитьЦифрыПосимвольно(Знач ИсходнаяСтрока) Экспорт
// Идем с конца строки к началу, чтобы удаление символов не сбивало индексы
Для Сч = -СтрДлина(ИсходнаяСтрока) По -1 Цикл
Индекс = -Сч; // Получаем положительный индекс (1..N)
ТекущийСимвол = Сред(ИсходнаяСтрока, Индекс, 1);
// Проверяем, входит ли символ в строку цифр
Если СтрНайти("0123456789", ТекущийСимвол) > 0 Тогда
// Удаляем символ склеиванием левой и правой части
ИсходнаяСтрока = Лев(ИсходнаяСтрока, Индекс - 1) + Прав(ИсходнаяСтрока, СтрДлина(ИсходнаяСтрока) - Индекс);
КонецЕсли;
КонецЦикла;
Возврат ИсходнаяСтрока;
КонецФункции
В данном примере используется интересный прием с отрицательным счетчиком цикла, чтобы использовать конструкцию По ... Цикл, так как в 1С шаг цикла всегда равен 1 (или -1 через обходной путь). Однако проще использовать цикл Пока.
Давайте проанализируем ошибки, которые допустил автор темы в своем коде, чтобы не повторять их.
Строка и менял его внутри. Если переменная передается не по значению (Знач), то изменения внутри функции отразятся на переменной, переданной в функцию извне. Это плохой тон, если функция заявлена как возвращающая результат. Всегда используйте Знач для примитивных типов или локальную переменную."0123456789".Подведем итог, какой метод лучше использовать в вашей практике:
СтрЗаменитьПоРегулярномуВыражению. Это читаемо, современно и универсально.СтрСоединить(СтрРазделить(...)). Это стандарт "де-факто" в типовых конфигурациях (БСП).СтрЗаменить (Способ 1). Он самый надежный для старых платформ.Пример использования функции из БСП (библиотеки стандартных подсистем), если она внедрена в вашу конфигурацию. Обычно функционал находится в общем модуле СтроковыеФункции или СтроковыеФункцииКлиентСервер:
ЧистаяСтрока = СтроковыеФункцииКлиентСервер.УдалитьСимволы(ГрязнаяСтрока, "0123456789");
// Или иногда встречается метод
ЧистаяСтрока = СтроковыеФункцииКлиентСервер.ТолькоЦифры(ГрязнаяСтрока); // для обратной задачи
Использование готовых функций общих модулей всегда предпочтительнее написания собственных "велосипедов", так как это облегчает чтение кода другими разработчиками.