Как найти и извлечь год из произвольной строки в 1С?

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

При рефакторинге информационных баз или миграции данных часто возникает задача разбора неструктурированных строковых полей, в которые пользователи годами вводили даты вручную в произвольном виде — например, «2 квартал 2015 г.», «за период 2013-2014» или «15.03.2022». Разберем, какими способами можно программно найти четырехзначный год в тексте, выбрать наиболее актуальное значение и корректно преобразовать его в тип Дата.

Способ 1. Использование регулярных выражений через COM-объект VBScript.RegExp

Если конфигурация работает под управлением операционной системы Windows, одним из классических и быстрых вариантов поиска паттернов является вызов COM-объекта VBScript.RegExp. Разберем по шагам, как извлечь все четырехзначные числа из переданной строки:


RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Истина;
RegExp.Global = Истина;
RegExp.MultiLine = Истина;    
RegExp.Pattern = "\b(19|20)\d{2}\b"; // Поиск годов 20 и 21 веков

ИсходнаяСтрока = "Пример: 2 квартал 2015 г. или 2013-2014";
Matches = RegExp.Execute(ИсходнаяСтрока);

МаксимальныйГод = 0;
Для Сч = 0 По Matches.Count - 1 Цикл
    Match = Matches.Item(Сч);
    ГодЧислом = Число(Match.Value);
    Если ГодЧислом > МаксимальныйГод Тогда
        МаксимальныйГод = ГодЧислом;
    КонецЕсли;
КонецЦикла;

Если МаксимальныйГод > 0 Тогда
    ИтоговаяДата = Дата(МаксимальныйГод, 1, 1);
КонецЕсли;

Обратим внимание на шаблон регулярного выражения: вместо простого \d{4} безопаснее использовать шаблон с границами слов и префиксами веков \b(19|20)\d{2}\b. Это защитит алгоритм от ложных срабатываний при наличии длинных артикулов, номеров накладных или сумм (например, строка «12015» не будет ошибочно распознана как 2015 год).

Ограничения использования COM-объекта:

  1. Зависимость от операционной системы: COM-объекты поддерживаются только в среде Windows. На серверах 1С под управлением Linux, macOS или в веб-клиенте данный вызов вызовет исключение.
  2. Производительность в циклах: не следует инициализировать объект Новый COMОбъект("VBScript.RegExp") внутри цикла обхода строк табличной части или выборки документов. Создавайте экземпляр один раз перед началом массовой обработки данных.

Способ 2. Штатные регулярные выражения (платформа 1С:Предприятие 8.3.23 и выше)

Начиная с версии платформы 8.3.23, в 1С появилась встроенная кроссплатформенная поддержка регулярных выражений. Это рекомендуемый способ, так как он работает на любых ОС без использования внешних библиотек.

Посмотрим на пример использования функций СтрПодобнаПоРегулярномуВыражению и СтрНайтиПоРегулярномуВыражению:


ШаблонГода = "\b(19|20)\d{2}\b";
СтрокаДляПоиска = "2 квартал 2015 г.";

Если СтрПодобнаПоРегулярномуВыражению(СтрокаДляПоиска, ".*" + ШаблонГода + ".*") Тогда
    РезультатПоиска = СтрНайтиПоРегулярномуВыражению(СтрокаДляПоиска, ШаблонГода);
    Если РезультатПоиска <> Неопределено Тогда
        НайденнаяПодстрока = Сред(СтрокаДляПоиска, РезультатПоиска.НачальнаяПозиция, РезультатПоиска.Длина);
        НайденныйГод = Число(НайденнаяПодстрока);
        ДатаРезультат = Дата(НайденныйГод, 1, 1);
    КонецЕсли;
КонецЕсли;

Способ 3. Посимвольный разбор строки средствами встроенного языка

Если требуется универсальное решение без COM-объектов, работающее на старых версиях платформы 1С, применим алгоритм последовательного обхода строки с выделением непрерывных цифровых блоков:


Функция ИзвлечьМаксимальныйГодИзСтроки(Знач ИсходныйТекст)
    
    МаксГод = 0;
    Цифры = "0123456789";
    ТекущийБлок = "";
    
    Для Позиция = 1 По СтрДлина(ИсходныйТекст) Цикл
        Символ = Сред(ИсходныйТекст, Позиция, 1);
        
        Если СтрНайти(Цифры, Символ) > 0 Тогда
            ТекущийБлок = ТекущийБлок + Символ;
        Иначе
            Если СтрДлина(ТекущийБлок) = 4 Тогда
                ЧислоБлока = Число(ТекущийБлок);
                Если ЧислоБлока >= 1900 И ЧислоБлока <= 2099 Тогда
                    МаксГод = Макс(МаксГод, ЧислоБлока);
                КонецЕсли;
            КонецЕсли;
            ТекущийБлок = "";
        КонецЕсли;
    КонецЦикла;
    
    // Проверка блока, если строка заканчивается цифрой
    Если СтрДлина(ТекущийБлок) = 4 Тогда
        ЧислоБлока = Число(ТекущийБлок);
        Если ЧислоБлока >= 1900 И ЧислоБлока <= 2099 Тогда
            МаксГод = Макс(МаксГод, ЧислоБлока);
        КонецЕсли;
    КонецЕсли;
    
    Возврат МаксГод;
    
КонецФункции

Такой подход полностью автономен, не требует сторонних компонентов и отсекает пятизначные числа за счет проверки длины накопительного буфера.

Способ 4. Поиск вхождений по диапазону годов

Для разовых задач обработки данных можно воспользоваться методом перебора диапазона предполагаемых годов через функцию СтрНайти (или Найти в обычном приложении). Обязательно форматируем число без разделителей групп (параметр ЧГ=0), чтобы исключить неразрывные пробелы:


НайденныйГод = 0;
Для ПроверяемыйГод = 1990 По 2030 Цикл
    ПодстрокаГода = Формат(ПроверяемыйГод, "ЧГ=0");
    Если СтрНайти(СтрокаСГодом, ПодстрокаГода) > 0 Тогда
        НайденныйГод = ПроверяемыйГод;
    КонецЕсли;
КонецЦикла;

Рекомендации по пакетной обработке документов

При заполнении новых реквизитов в исторических документах рекомендуем соблюдать следующие правила:

  1. Отключение проверок: для ускорения записи и исключения блокировок устанавливайте свойство ДокументОбъект.ОбменДанными.Загрузка = Истина.
  2. Транзакции порциями: группируйте запись документов пачками (по 200–500 штук) в явных транзакциях для снижения нагрузки на журнал СУБД.
  3. Логирование ошибок: если в строке табличной части год не найден вовсе или встретились нестандартные сокращения (например, «15 г.»), выгружайте такие документы в отдельную таблицу или лог для последующего ручного контроля.
← На главную