При интеграции 1С с внешними системами (интернет-магазинами, кассовыми аппаратами, SMS-шлюзами или банками) разработчики часто сталкиваются с необходимостью парсинга слабоформализованных текстовых строк. Типичный пример такой строки — позиция чека или заказа, записанная в одну строчку: "Лизобакт таб. д/рассас. №30 275.00 x 1 = 275.00".
В этой статье мы подробно проанализируем, как решить эту задачу в среде 1С. Мы разберем простое решение, предложенное на форуме, выявим его критические уязвимости, которые обязательно приведут к сбоям в реальной эксплуатации, и предложим два альтернативных профессиональных метода: кроссплатформенный поиск справа налево и использование регулярных выражений (RegExp).
Начнем с рассмотрения алгоритма, который часто выбирают начинающие разработчики. Суть метода заключается в разделении всей строки на массив подстрок с использованием пробела в качестве разделителя, а затем получении нужных данных по фиксированным индексам с конца полученного массива.
Рассмотрим пример кода, который решает эту задачу "в лоб" для нескольких строк, разделенных переносом строки:
// Пример простого парсинга через СтрРазделить
МассивСтрокДокумента = СтрРазделить(ИсходныйТекст, Символы.ПС, Ложь);
Для Каждого ТекущаяСтрока Из МассивСтрокДокумента Цикл
ЧастиСтроки = СтрРазделить(ТекущаяСтрока, " ", Ложь);
РазмерМассива = ЧастиСтроки.Количество();
// Проверяем, что в строке достаточно элементов для анализа
Если РазмерМассива > 4 Тогда
Попытка
// Склеиваем наименование товара из начальных элементов массива
Наименование = "";
Для Индекс = 0 По РазмерМассива - 6 Цикл
Наименование = СокрЛП(Наименование + " " + ЧастиСтроки[Индекс]);
КонецЦикла;
// Получаем числовые показатели по обратным индексам
Цена = Число(ЧастиСтроки[РазмерМассива - 5]);
Количество = Число(ЧастиСтроки[РазмерМассива - 3]);
Сумма = Число(ЧастиСтроки[РазмерМассива - 1]);
Сообщить(СтрШаблон("Успешно разобрано: %1 | Цена: %2 | Кол-во: %3 | Сумма: %4",
Наименование, Цена, Количество, Сумма));
Исключение
Сообщить("Ошибка формата в строке: " + ТекущаяСтрока);
КонецПопытки;
КонецЕсли;
КонецЦикла;
На первый взгляд, этот алгоритм выглядит вполне рабочим. Он успешно справляется с идеальной строкой, где наименование состоит из нескольких слов, разделенных пробелами, а в конце идут строго разделенные пробелами цена, знак умножения, количество, знак равенства и итоговая сумма.
Однако опытные разработчики называют такой код "хрупким". Давайте выясним, почему данное решение гарантированно сломается при реальной эксплуатации в информационной базе.
Проанализируем ключевые факторы риска, которые делают код на основе СтрРазделить(..., " ") крайне нестабильным:
"1 700.00" вместо "1700.00"). В этом случае функция СтрРазделить разрежет одну только цену на два отдельных элемента массива. Вся логика обратных индексов (РазмерМассива - 5) мгновенно сдвинется, и программа запишет некорректные данные или вызовет исключение."x", латинская заглавная "X", кириллическая "х", кириллическая "Х" или символ звездочки "*". Кроме того, вокруг знака умножения могут отсутствовать пробелы (например, "275.00x1"). В таком случае разделение по пробелу вообще не выделит количество как отдельный элемент.Число() для строки "275.00" в системе с русской локалью без предварительной замены точки на запятую приведет к падению в блок Исключение.Чтобы избежать этих проблем, рассмотрим значительно более устойчивые и профессиональные варианты реализации.
Если ваше решение должно работать в файловом и клиент-серверном вариантах как на Windows, так и на Linux (где использование COM-объектов невозможно), оптимальным выбором станет разбор строки с конца. Для этого мы будем использовать стандартные функции платформы 1С версии 8.3.6 и выше, поддерживающие поиск с конца строки.
Логика алгоритма базируется на том, что наименование номенклатуры (которое может содержать любые спецсимволы, цифры и пробелы) всегда находится слева. А вот структурированные данные (цена, количество, сумма) всегда идут в строгом порядке справа. Поэтому парсить строку нужно именно **справа налево**.
Разберем по шагам реализацию этого надежного алгоритма:
// Функция производит надежный разбор строки справа налево
Функция РазобратьСтрокуСправаНалево(Знач ВхСтрока) Экспорт
Результат = Новый Структура("Наименование, Цена, Количество, Сумма, Успех", "", 0, 0, 0, Ложь);
// Этап 1: Унификация символов умножения для упрощения поиска
ВхСтрока = СтрЗаменить(ВхСтрока, "x", "х"); // латиница в кириллицу
ВхСтрока = СтрЗаменить(ВхСтрока, "X", "х");
ВхСтрока = СтрЗаменить(ВхСтрока, "Х", "х"); // заглавная кириллица в строчную
ВхСтрока = СтрЗаменить(ВхСтрока, "*", "х"); // звездочка в единый символ "х"
// Этап 2: Выделение Суммы (ищем первый символ "=" с конца строки)
ПозицияРавно = СтрНайти(ВхСтрока, "=", НаправлениеПоиска.Конец);
Если ПозицияРавно = 0 Тогда
Возврат Результат; // Строка не соответствует формату
КонецЕсли;
ПодстрокаСуммы = СокрЛП(Сред(ВхСтрока, ПозицияРавно + 1));
ВхСтрока = СокрЛП(Лев(ВхСтрока, ПозицияРавно - 1));
// Этап 3: Выделение Количества (ищем наш унифицированный символ "х" с конца строки)
ПозицияУмножения = СтрНайти(ВхСтрока, "х", НаправлениеПоиска.Конец);
Если ПозицияУмножения = 0 Тогда
Возврат Результат;
КонецЕсли;
ПодстрокаКоличества = СокрЛП(Сред(ВхСтрока, ПозицияУмножения + 1));
ВхСтрока = СокрЛП(Лев(ВхСтрока, ПозицияУмножения - 1));
// Этап 4: Разделение оставшейся части на Наименование и Цену
// Цена — это последнее "слово" (число) перед знаком умножения, отделенное пробелом
ПозицияПробелаЦены = СтрНайти(ВхСтрока, " ", НаправлениеПоиска.Конец);
Если ПозицияПробелаЦены = 0 Тогда
Возврат Результат;
КонецЕсли;
ПодстрокаЦена = СокрЛП(Сред(ВхСтрока, ПозицияПробелаЦены + 1));
Наименование = СокрЛП(Лев(ВхСтрока, ПозицияПробелаЦены - 1));
// Этап 5: Нормализация данных и приведение к числовым типам
Попытка
Результат.Сумма = ПреобразоватьВЧисло1С(ПодстрокаСуммы);
Результат.Количество = ПреобразоватьВЧисло1С(ПодстрокаКоличества);
Результат.Цена = ПреобразоватьВЧисло1С(ПодстрокаЦена);
Результат.Наименование = Наименование;
Результат.Успех = Истина;
Исключение
Результат.Успех = Ложь;
ЗаписьЖурналаРегистрации("ПарсингСтроки", УровеньЖурналаРегистрации.Предупреждение, , ,
"Не удалось преобразовать числа в строке: " + ВхСтрока);
КонецПопытки;
Возврат Результат;
КонецФункции
// Вспомогательная функция очистки чисел от мусора и пробелов
Функция ПреобразоватьВЧисло1С(Знач СтрЗначение)
// Удаляем неразрывные пробелы (код 160), которые часто приходят из HTML или макетов
СтрЗначение = СтрЗаменить(СтрЗначение, Символ(160), "");
// Удаляем обычные пробелы (возможные разделители тысяч)
СтрЗначение = СтрЗаменить(СтрЗначение, " ", "");
// Приводим точку к запятой для универсальности преобразования в русской локали
СтрЗначение = СтрЗаменить(СтрЗначение, ".", ",");
Возврат Число(СтрЗначение);
КонецФункции
Этот алгоритм работает невероятно быстро, не использует сторонних библиотек и полностью защищен от любых нестандартных пробелов внутри наименования товара.
Для операционных систем семейства Windows идеальным и наиболее лаконичным решением является использование регулярных выражений через COM-объект VBScript.RegExp. Регулярные выражения позволяют проверить соответствие строки шаблону и мгновенно извлечь все группы данных (наименование, цену, количество и сумму) за один проход, полностью игнорируя вариативность пробелов.
Рассмотрим пример использования регулярных выражений для решения нашей задачи:
// Функция парсинга строки с использованием RegExp
Функция РазобратьСтрокуRegExp(Знач ВхСтрока) Экспорт
Результат = Новый Структура("Наименование, Цена, Количество, Сумма, Успех", "", 0, 0, 0, Ложь);
Попытка
RegExp = Новый COMОбъект("VBScript.RegExp");
Исключение
ВызватьИсключение "На сервере не установлен компонент работы с регулярными выражениями VBScript.RegExp!";
КонецПопытки;
RegExp.IgnoreCase = Истина;
RegExp.Global = Ложь;
RegExp.MultiLine = Ложь;
// Шаблон регулярного выражения:
// 1-я группа (.*?): ленивый захват наименования с начала строки
// 2-я группа ([\d\s, ]+(?:\.\d+)?): захват цены (цифры, пробелы, запятые, точки)
// [xхXХ*]: гибкий разделитель количества (латинские/русские х, звездочка)
// 3-я группа: количество
// 4-я группа: сумма после знака "="
RegExp.Pattern = "^(.*?)\s+([\d\s, ]+(?:\.\d+)?)\s*[xхXХ*]\s*([\d\s, ]+(?:\.\d+)?)\s*=\s*([\d\s, ]+(?:\.\d+)?)$";
Если RegExp.Test(ВхСтрока) Тогда
КоллекцияСовпадений = RegExp.Execute(ВхСтрока);
Если КоллекцияСовпадений.Количество() > 0 Тогда
НайденоСовпадение = КоллекцияСовпадений.Получить(0);
SubMatches = НайденоСовпадение.SubMatches;
Попытка
Результат.Наименование = СокрЛП(SubMatches.Item(0));
Результат.Цена = ПреобразоватьВЧисло1С(SubMatches.Item(1));
ResultQty = SubMatches.Item(2);
Результат.Количество = ПреобразоватьВЧисло1С(ResultQty);
Результат.Сумма = ПреобразоватьВЧисло1С(SubMatches.Item(3));
Результат.Успех = Истина;
Исключение
Результат.Успех = Ложь;
КонецПопытки;
КонецЕсли;
КонецЕсли;
Возврат Результат;
КонецФункции
Давайте подробно проанализируем структуру регулярного выражения ^(.*?)\s+([\d\s, ]+(?:\.\d+)?)...:
^ указывает на начало обрабатываемой строки.(.*?) — это нежадный (ленивый) поиск любых символов. Он заберет наименование товара ровно до того момента, пока регулярное выражение не встретит первый подходящий разделитель цены.\s+ требует наличия хотя бы одного пробельного символа между наименованием и числом цены.([\d\s, ]+(?:\.\d+)?) — группа захвата числа. Она способна корректно распознать цифры (\d), пробелы и неразрывные пробелы (\s, ), запятые и опциональную дробную часть, разделенную точкой.\s*[xхXХ*]\s* — гибкий парсер знака умножения, который допускает любые пробелы вокруг него (или их полное отсутствие) и поддерживает 5 различных вариантов написания самого символа умножения.Проанализируем плюсы и минусы всех рассмотренных нами подходов, чтобы вы могли выбрать наиболее подходящий под ваши задачи инструмент:
| Критерий сравнения | Разбиение по пробелам | Поиск справа налево | Регулярные выражения (RegExp) |
|---|---|---|---|
| Сложность реализации | Очень простая | Средняя | Средняя |
| Устойчивость к пробелам в числах | Низкая (код сломается) | Высокая (полная обработка) | Высокая (полная обработка) |
| Кроссплатформенность | Да | Да (1С 8.3.6+) | Нет (только под ОС Windows) |
| Производительность | Высокая | Очень высокая | Средняя |
| Устойчивость к разным знакам умножения | Низкая | Высокая | Максимальная |
Таким образом, для простых кроссплатформенных задач на современных конфигурациях 1С рекомендуется использовать Метод поиска справа налево. Если разработка ведется строго под Windows-инфраструктуру и требуется максимальная лаконичность кода — выбор за Регулярными выражениями.