В процессе интеграции 1С с внешними системами или при загрузке банковских выписок мы часто сталкиваемся с проблемой «грязных» данных. Пользователи или сторонние программы могут вводить информацию в произвольном формате: использовать разные разделители (точка вместо запятой), менять символы валют (KZT вместо тг), использовать разные префиксы (N вместо №) или вовсе пропускать спецсимволы. Наша задача — привести такие строки к единому каноническому виду, пригодному для дальнейшей обработки в системе.
В этой статье мы подробно рассмотрим, как программно обработать части текста по шаблону, разберем преимущества регулярных выражений и альтернативные методы парсинга строк.
Регулярные выражения — это, пожалуй, самый эффективный и лаконичный способ решения подобных задач. Несмотря на кажущуюся сложность, они позволяют одним выражением описать все возможные вариации написания строки. В среде Windows 1С работает с регулярными выражениями через COM-объект VBScript.RegExp.
Проанализируем ситуацию: нам нужно извлечь номер счета и сумму. Номер счета обычно начинается с «№» или «N», а сумма содержит цифры и разделитель (точку или запятую). Разберем по шагам создание такого парсера:
// Создаем объект регулярного выражения
RegExp = Новый COMОбъект("VBScript.RegExp");
// Описываем шаблон:
// (?:N|№) - ищем префикс N или № (без захвата в группу)
// (\d+) - первая группа: захватываем цифры номера счета
// (?:\s*[-= ]\s*) - ищем разделитель (тире, равно или пробел), игнорируя пробелы вокруг
// (\d+[\.,]\d+|\d+) - вторая группа: захватываем сумму (целую часть и дробную через . или ,)
RegExp.Pattern = "(?:N|№)\s*(\d+)\s*[-=\s]\s*(\d+[\.,]\d+|\d+)";
RegExp.Global = Истина;
RegExp.IgnoreCase = Истина;
ТекстИсточник = "№742020-996819,02тг №742019=993657.6";
Результаты = RegExp.Execute(ТекстИсточник);
Для Каждого Матч Из Результаты Цикл
НомерСчета = Матч.SubMatches(0);
СуммаСтрокой = Матч.SubMatches(1);
// Приводим сумму к числовому формату 1С
СуммаСтрокой = СтрЗаменить(СуммаСтрокой, ".", ",");
Сумма = Число(СуммаСтрокой);
Сообщить("Найдено: Счет №" + НомерСчета + ", Сумма: " + Сумма);
КонецЦикла;
Преимущества этого подхода: гибкость. Мы можем легко добавить новые варианты разделителей в шаблон, не переписывая программный код. Рассмотрим подробнее, как это работает: метод Execute находит все вхождения шаблона в строке, а коллекция SubMatches позволяет вытащить именно те части, которые были заключены в круглые скобки в регулярном выражении.
Если по каким-то причинам использование регулярных выражений нежелательно, мы можем применить стратегию последовательной замены «мусорных» символов на стандартные. Выясним причину, по которой этот метод часто выбирают новички: он более нагляден при отладке.
Алгоритм действий будет следующим:
Рассмотрим пример кода:
Функция НормализоватьСтроку(Знач ВходнаяСтрока)
// Убираем валюту
ВходнаяСтрока = СтрЗаменить(ВходнаяСтрока, "KZT", "");
ВходнаяСтрока = СтрЗаменить(ВходнаяСтрока, "тг", "");
// Унифицируем разделитель номера и суммы
ВходнаяСтрока = СтрЗаменить(ВходнаяСтрока, "-", " ");
ВходнаяСтрока = СтрЗаменить(ВходнаяСтрока, "=", " ");
// Убираем спецсимволы номера
ВходнаяСтрока = СтрЗаменить(ВходнаяСтрока, "№", "");
ВходнаяСтрока = СтрЗаменить(ВходнаяСтрока, "N", "");
// Теперь строка выглядит примерно так: "742020 996819,02 742019 993657.6"
Возврат СократитьЛП(ВходнаяСтрока);
КонецФункции
После такой подготовки мы можем использовать встроенную функцию СтрРазделить, чтобы получить массив подстрок и обработать их попарно (первый элемент — номер, второй — сумма).
Для наиболее сложных случаев, когда спецсимволы могут быть пропущены совсем, или когда структура текста крайне нестабильна, мы можем написать простейший конечный автомат. Мы будем перебирать строку по одному символу и менять состояние парсера в зависимости от того, что встретили.
Проанализируем ситуацию: наше состояние может быть либо «Ожидаем номер», либо «Читаем номер», либо «Читаем сумму». Посмотрим на пример реализации:
Текст = "№742020-996819,02тг";
ТекущийНомер = "";
ТекущаяСумма = "";
Состояние = "ПОИСК_НОМЕРА";
Для Инд = 1 По СтрДлина(Текст) Цикл
Символ = Сред(Текст, Инд, 1);
Если Состояние = "ПОИСК_НОМЕРА" Тогда
Если Найти("0123456789", Символ) > 0 Тогда
Состояние = "ЧТЕНИЕ_НОМЕРА";
ТекущийНомер = ТекущийНомер + Символ;
КонецЕсли;
ИначеЕсли Состояние = "ЧТЕНИЕ_НОМЕРА" Тогда
Если Найти("0123456789", Символ) > 0 Тогда
ТекущийНомер = ТекущийНомер + Символ;
Иначе // Встретили не цифру - значит номер закончился
Состояние = "ПОИСК_СУММЫ";
КонецЕсли;
ИначеЕсли Состояние = "ПОИСК_СУММЫ" Тогда
Если Найти("0123456789", Символ) > 0 Тогда
Состояние = "ЧТЕНИЕ_СУММЫ";
ТекущаяСумма = ТекущаяСумма + Символ;
КонецЕсли;
ИначеЕсли Состояние = "ЧТЕНИЕ_СУММЫ" Тогда
Если Найти("0123456789.,", Символ) > 0 Тогда
ТекущаяСумма = ТекущаяСумма + Символ;
Иначе // Встретили буквы валюты или пробел
// Обработка накопленных данных
Сообщить("Счет: " + ТекущийНомер + " Сумма: " + ТекущаяСумма);
// Сброс данных для следующей пары
ТекущийНомер = "";
ТекущаяСумма = "";
Состояние = "ПОИСК_НОМЕРА";
КонецЕсли;
КонецЕсли;
КонецЦикла;
Важный момент: при парсинге сумм всегда учитывайте, что в качестве разделителя может выступать как точка, так и запятая. Перед преобразованием строки в тип Число обязательно приводите разделитель к тому виду, который установлен в системных настройках вашей ОС (в России это обычно запятая).
Если ваша конфигурация построена на базе Библиотеки стандартных подсистем (БСП), вам доступны мощные инструменты в общем модуле СтроковыеФункцииКлиентСервер. Рассмотрим подробнее функцию РазложитьСтрокуВМассивПодстрок. Она примечательна тем, что в качестве разделителя может принимать не одну строку, а целый массив возможных разделителей.
Это позволяет нам в одну строку кода разделить «грязную» строку по всем возможным проблемным символам:
Разделители = Новый Массив;
Разделители.Добавить("=");
Разделители.Добавить("-");
Разделители.Добавить(" ");
Разделители.Добавить("№");
Разделители.Добавить("N");
ЧастиСтроки = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(ТекстИсточник, Разделители, Истина);
Параметр Истина в конце функции означает, что пустые подстроки будут автоматически удалены из результата. После этого нам останется только обойти массив и идентифицировать, где номер, а где сумма (например, по наличию разделителя дробной части или длине строки).
В заключение отметим, что выбор метода зависит от ваших требований к производительности и поддержке кода. Регулярные выражения — самый компактный и профессиональный способ, посимвольный перебор — самый надежный при полной непредсказуемости данных, а функции БСП — лучший выбор для тех, кто хочет минимизировать написание собственного кода и использовать стандартные возможности платформы 1С.