При автоматической обработке входящих электронных писем, банковских уведомлений или SMS-сообщений часто возникает задача извлечь числовые показатели (например, сумму оплаты) из неструктурированного текста и записать их в реквизиты создаваемого документа. Неправильный разбор строки приводит к ошибкам приведения типов или некорректно заполненным данным.
Разберем типичные ошибки, возникающие при парсинге строк, выясним причины исключения «Преобразование к типу Число не может быть выполнено» и рассмотрим надежные варианты решения этой задачи на платформе 1С:Предприятие.
Проанализируем исходный пример текста входящего уведомления:
Vhodyaschiy platezh. Summa 2,222.22 RUB. Otpravitel ******** *******. 16.11.2017.
Попытка извлечь подстроку с помощью функций Сред() или Лев() с жестко фиксированными номерами символов (например, Сред(Текст, 27, 5)) является распространенной ошибкой. Выясним, почему такой подход приводит к сбоям:
10.00, 2,222.22 и 1,500,000.00 имеют разное количество символов. Фиксированное смещение будет либо обрезать значимые цифры, либо захватывать посторонний текст (например, буквы валюты).Даже если подстрока с суммой выделена, функция Число() может вызвать аварийное завершение программы. Рассмотрим основные причины:
2,222.22) или пробелами (включая неразрывный пробел Символы.НПП с кодом 160). Функция Число() не может обработать строку с такими символами."2222.22" в Число() при русской локализации сеанса, возникнет ошибка.СокрЛП().Рассмотрим надежный базовый алгоритм, в котором границы суммы вычисляются динамически относительно ключевых слов (маркеров начала и конца), после чего строка очищается и приводится к числу.
Разберем реализацию по шагам:
"Summa " с помощью функции СтрНайти()." RUB", начиная поиск от маркера начала.Сред().Число()) либо используем универсальные методы преобразования.Посмотрим на пример кода:
// Исходный текст сообщения
ИсходныйТекст = СтрТЗ.Тело;
МаркерНачала = "Summa ";
МаркерКонца = " RUB";
ПозНачала = СтрНайти(ИсходныйТекст, МаркерНачала);
Если ПозНачала > 0 Тогда
ИндексСтарта = ПозНачала + СтрДлина(МаркерНачала);
ПозКонца = СтрНайти(ИсходныйТекст, МаркерКонца, , ИндексСтарта);
Если ПозКонца > ИндексСтарта Тогда
// Извлекаем фрагмент с суммой
СтрСумма = Сред(ИсходныйТекст, ИндексСтарта, ПозКонца - ИндексСтарта);
СтрСумма = СокрЛП(СтрСумма);
// Очищаем от разделителей групп и спецсимволов
СтрСумма = СтрЗаменить(СтрСумма, ",", "");
СтрСумма = СтрЗаменить(СтрСумма, " ", "");
СтрСумма = СтрЗаменить(СтрСумма, Символы.НПП, "");
// Для платформенного Число() при русской локали заменяем точку на запятую
СтрСумма = СтрЗаменить(СтрСумма, ".", ",");
Попытка
дО.Сумма = Число(СтрСумма);
Исключение
дО.Сумма = 0;
ЗаписьЖурналаРегистрации("ЗагрузкаПисем", УровеньЖурналаРегистрации.Ошибка, , ,
"Не удалось преобразовать значение суммы: " + СтрСумма);
КонецПопытки;
КонецЕсли;
КонецЕсли;
Чтобы избежать зависимости от региональных настроек сеанса (где разделителем может быть как точка, так и запятая), рассмотрим альтернативные методы преобразования строки в число.
1. Преобразование через XMLЗначение
Функция XMLЗначение() принимает числа в формате стандарта XML, где разделителем дробной части всегда выступает точка. Если в строке удалены разделители тысяч и оставлена точка, этот метод преобразует строку независимо от языка системы:
// Строка должна содержать только цифры и десятичную точку, например: "2222.22"
СтрЧислаXML = "2222.22";
ЧисловоеЗначение = XMLЗначение(Тип("Число"), СтрЧислаXML);
2. Безопасное приведение через ОписаниеТипов
Метод ПривестиЗначение() объекта ОписаниеТипов удобен тем, что он не вызывает исключений при некорректном формате данных, а возвращает значение по умолчанию (для числа — 0):
ОписаниеЧисла = Новый ОписаниеТипов("Число");
ЧисловоеЗначение = ОписаниеЧисла.ПривестиЗначение(СтрСумма);
Если в конфигурации используется платформа 1С:Предприятие версии 8.3.23 и выше, мы можем применить встроенный механизм регулярных выражений. Это позволяет получить сумму в одну операцию без ручного вычисления индексов.
Посмотрим на пример с использованием встроенных объектов платформы:
// Шаблон ищет блок "Summa", захватывает цифры с точками/запятыми и находит "RUB"
Шаблон = "Summa\s+([\d,.]+)\s+RUB";
РезультатПоиска = ПоискРегулярныхВыражений(ИсходныйТекст, Шаблон);
Если РезультатПоиска <> Неопределено И РезультатПоиска.Количество() > 0 Тогда
НайденнаяГруппа = РезультатПоиска[0];
// Получаем значение первой группы захвата
СтрСумма = НайденнаяГруппа.Подгруппы[0].Значение;
// Удаляем разделители тысяч
СтрСумма = СтрЗаменить(СтрСумма, ",", "");
// Преобразуем число в формате XML (с точкой)
дО.Сумма = XMLЗначение(Тип("Число"), СтрСумма);
КонецЕсли;
Для более ранних версий платформы под управлением операционной системы Windows для аналогичной цели можно использовать COM-объект VBScript.RegExp.
Разберем контекст выполнения кода при загрузке данных:
Документы.Оплата.СоздатьДокумент(), заполняем реквизит Сумма числовым значением и вызываем метод Записать(РежимЗаписиДокумента.Запись).Объект.Сумма = ЧисловоеЗначение;, что сразу отобразит результат в пользовательском интерфейсе.