В процессе разработки и поддержки конфигураций 1С программисты часто сталкиваются с необходимостью нормализации или изменения номеров документов для печатных форм, отчетов или выгрузок. Типичная задача — заменить буквенный префикс в начале номера документа (который может состоять из различного количества символов, например, "А", "ПВМ", "ГГ") на одну фиксированную цифру, например, "9".
Сложность этой задачи заключается в том, что длина буквенной части заранее неизвестна, а новые префиксы могут добавляться в систему пользователями без ведома разработчика. Простой перебор всех возможных комбинаций букв через функцию СтрЗаменить крайне неэффективен и усложняет поддержку кода. В этой статье мы подробно рассмотрим и проанализируем различные способы решения этой задачи — от простых циклов до использования регулярных выражений и встроенных функций платформы.
Допустим, у нас есть номера документов следующего вида:
Наша цель — убрать все нецифровые символы в начале строки и заменить их строго на одну девятку, сохранив при этом всю последующую цифровую часть номера.
---
Рассмотрим классический алгоритм, который будет гарантированно работать на любой версии платформы 1С (начиная с 7.7 и заканчивая самыми свежими релизами 8.3), независимо от используемого режима совместимости. Идея заключается в том, чтобы перебрать все символы строки и отделить цифры от букв.
Давайте разберем по шагам реализацию, предложенную в обсуждении:
НовыйНомер = "";
Для Счетчик = 1 По СтрДлина(Объект.Номер) Цикл
Символ = Сред(Объект.Номер, Счетчик, 1);
Если Символ = "1" или Символ = "2" или Символ = "3" или Символ = "4" или Символ = "5" или Символ = "6" или Символ = "7" или Символ = "8" или Символ = "9" или Символ = "0" Тогда
НовыйНомер = НовыйНомер + Символ;
КонецЕсли;
КонецЦикла;
НовыйНомер = "9" + НовыйНомер;
Сообщить(НовыйНомер);
Анализ метода: Этот код прекрасно справляется с очисткой номера от любых букв, однако у него есть существенный недостаток. Он удаляет абсолютно все буквы и спецсимволы (например, дефисы или косую черту) по всей длине строки, а не только в начале. Если исходный номер имел вид "ПВМ123-45", то на выходе мы получим "912345", потеряв важный разделитель внутри номера. Кроме того, перебор строки в цикле по одному символу может работать медленно на очень больших объемах данных.
---
Если ваша база данных работает на платформе 1С:Предприятие 8.3.10 и выше, и у вас отключен режим совместимости со старыми версиями, вы можете использовать мощную встроенную функцию СтрРазделить.
Выясним причину, как этот метод помогает решить задачу. Мы можем использовать строку со всеми цифрами в качестве разделителя, чтобы "отрезать" буквенный префикс. Посмотрим на следующий пример:
лкНомер = "ПМЗ12345";
лкЧасти = СтрРазделить(лкНомер, "0123456789", Ложь);
Если лкЧасти.Количество() > 0 Тогда
// Заменяем только найденную в начале буквенную часть на "9"
лкНомер = СтрЗаменить(лкНомер, лкЧасти[0], "9");
КонецЕсли;
Сообщить(лкНомер);
Важное примечание: Если при выполнении этого кода вы получаете ошибку "Процедура или функция с указанным именем не определена (СтрРазделить)", это означает, что в вашей конфигурации установлен режим совместимости с версией ниже 8.3.10 (например, база работает в режиме совместимости с 8.2 или используется старый толстый клиент). В таком случае данный метод запустить не удастся без изменения настроек конфигурации.
---
Для того чтобы обойти ограничения совместимости старых платформ и при этом не сломать структуру номера (сохранив внутренние дефисы и пробелы), проанализируем подход с использованием функции КодСимвола. Цифровые символы от "0" до "9" в таблице ASCII имеют коды с 48 по 57. Мы можем найти первый символ, являющийся цифрой, и отрезать все, что находится левее него.
Разберем этот алгоритм подробнее:
ИсходныйНомер = СокрЛП(ДанныеДляПечати.Номер);
ИндексПервойЦифры = 0;
// Определяем позицию, с которой начинается цифровая часть
Для Счетчик = 1 По СтрДлина(ИсходныйНомер) Цикл
КодСимвола = КодСимвола(ИсходныйНомер, Счетчик);
Если КодСимвола >= 48 И КодСимвола <= 57 Тогда
ИндексПервойЦифры = Счетчик;
Прервать; // Выходим из цикла, как только встретили первую цифру
КонецЕсли;
КонецЦикла;
Если ИндексПервойЦифры > 0 Тогда
// Отрезаем буквенный префикс и подставляем "9"
НовыйНомер = "9" + Сред(ИсходныйНомер, ИндексПервойЦифры);
Иначе
// Если в номере вообще не оказалось цифр
НовыйНомер = "9";
КонецЕсли;
Сообщить(НовыйНомер);
Преимущества данного решения:
---
Если база данных работает под управлением операционной системы Windows, отличным и элегантным решением будет использование COM-объекта регулярных выражений. Этот способ позволяет избежать циклов и написать решение буквально в несколько строк.
Посмотрим на пример реализации:
РегВыражение = Новый COMОбъект("VBScript.RegExp");
РегВыражение.Pattern = "^\D+"; // Шаблон означает: найти любые НЕ-цифры в начале строки
РегВыражение.IgnoreCase = Истина;
РегВыражение.Global = Ложь;
ИсходныйНомер = СокрЛП(ДанныеДляПечати.Номер);
НовыйНомер = РегВыражение.Replace(ИсходныйНомер, "9");
Сообщить(НовыйНомер);
Этот способ работает быстро, надежно и избавляет разработчика от необходимости писать сложные циклы обхода строк. Однако помните, что COM-объекты не будут работать на серверах под управлением ОС Linux или в некоторых облачных сервисах (например, 1С:Fresh) из-за соображений безопасности.
---
При выборе метода замены префиксов руководствуйтесь следующими правилами:
СтрРазделить или нативные регулярные выражения (доступны в последних релизах платформы).