При интеграции и загрузке данных разработчики часто сталкиваются с разбором строк в формате CSV, где элементы разделены запятыми. Типовые функции разбиения (такие как платформенная функция СтрРазделить или библиотечная функция СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок) работают прямолинейно: они делят строку по каждому найденному символу-разделителю. Однако если внутри строки передаются числа с плавающей точкой или текстовые описания, заключенные в кавычки (например, "1425,00" или "Коррекция 'Проживание' от 30.04, 84526"), наивный алгоритм ошибочно разобьет такие поля на несколько частей.
Разберем подробно несколько надежных способов правильной обработки подобных строк в 1С:Предприятие — от создания универсального посимвольного парсера до использования встроенных механизмов платформы и регулярных выражений.
Рассмотрим наиболее надежный, производительный и полностью кроссплатформенный подход. Посимвольный обход строки с отслеживанием текущего состояния (находимся ли мы внутри кавычек) корректно работает на любых версиях платформы 1С:Предприятие, в любых клиентах и на серверах под управлением Windows и Linux.
Алгоритм пошагово выполняет следующие действия:
ВнутриКавычек = Ложь.ВнутриКавычек.ВнутриКавычек равен Ложь, фиксируем завершение текущего поля: добавляем накопленный буфер в массив и очищаем его. Если же мы находимся внутри кавычек, запятая просто дописывается в буфер как обычный символ.Посмотрим на программную реализацию данной функции:
Функция РазложитьСтрокуСКавычками(Знач ИсходнаяСтрока, Знач Разделитель = ",") Экспорт
Результат = Новый Массив;
ДлинаСтроки = СтрДлина(ИсходнаяСтрока);
ВнутриКавычек = Ложь;
ТекущееЗначение = "";
Индекс = 1;
Пока Индекс <= ДлинаСтроки Цикл
Символ = Сред(ИсходнаяСтрока, Индекс, 1);
Если Символ = """" Тогда
// Проверяем экранированную кавычку ("")
Если Индекс < ДлинаСтроки И Сред(ИсходнаяСтрока, Индекс + 1, 1) = """" Тогда
ТекущееЗначение = ТекущееЗначение + """";
Индекс = Индекс + 1;
Иначе
ВнутриКавычек = НЕ ВнутриКавычек;
КонецЕсли;
ИначеЕсли Символ = Разделитель И НЕ ВнутриКавычек Тогда
Результат.Добавить(СокрЛП(ТекущееЗначение));
ТекущееЗначение = "";
Иначе
ТекущееЗначение = ТекущееЗначение + Символ;
КонецЕсли;
Индекс = Индекс + 1;
КонецЦикла;
Результат.Добавить(СокрЛП(ТекущееЗначение));
Возврат Результат;
КонецФункции
Начиная с версии платформы 8.3.14 в 1С доступен встроенный механизм работы с CSV-форматом. Объекты ЧтениеCSV и ПараметрыЧтенияCSV строго соблюдают стандарт RFC 4180, автоматически учитывая кавычки, переносы строк и экранирование.
Если исходные данные находятся в строковой переменной, а не в файле на диске, разберем, как прочитать их через поток в памяти (ПотокВПамяти):
Функция РазобратьСтрокуЧерезCSV(Знач ИсходнаяСтрока, Знач Разделитель = ",") Экспорт
ПараметрыЧтения = Новый ПараметрыЧтенияCSV;
ПараметрыЧтения.РазделительПолей = Разделитель;
ПараметрыЧтения.СимволВыделенияСтрок = """";
Поток = Новый ПотокВПамяти;
Запись = Новый ЗаписьДанных(Поток);
Запись.ЗаписатьСтроку(ИсходнаяСтрока);
Запись.Закрыть();
Поток.ТекущаяПозиция = 0;
Чтение = Новый ЧтениеCSV(Поток, ПараметрыЧтения);
МассивЗначений = Чтение.Прочитать();
Чтение.Закрыть();
Если МассивЗначений = Неопределено Тогда
Возврат Новый Массив;
КонецЕсли;
Возврат МассивЗначений;
КонецФункции
Проанализируем подход, который часто используют для быстрой предобработки: найти все блоки текста, заключенные в кавычки, временно заменить внутри них разделители на специальные символы-маркеры (например, #;#), разбить строку стандартным методом, а затем вернуть исходные запятые на место.
В среде Windows для этой цели традиционно применяется COM-объект VBScript.RegExp. В платформе 1С:Предприятие 8.3.23 и старше доступен нативный объект РегулярноеВыражение.
Посмотрим на пример извлечения и обработки строк с использованием COM-объекта:
ВходСтрока = "Яблоко, Апельсин, Грузовик, 1247587, ""1425,00"", Улица, Фонарь,""Коррекция 'Проживание' от 30.04 - трансфер на счет ОЛТА Групп, 84526, 28/04/17 ИРИНА УСЕНКО УЖИНЫ!!!!"", Аптека.";
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Истина;
RegExp.Global = Истина;
RegExp.MultiLine = Истина;
// Шаблон находит все подстроки, обрамленные кавычками
RegExp.Pattern = """[^""]+""";
Совпадения = RegExp.Execute(ВходСтрока);
Для Счетчик = 0 По Совпадения.Count - 1 Цикл
Элемент = Совпадения.Item(Счетчик);
// Заменяем разделители внутри найденного закавыченного фрагмента
ФрагментБезЗапятых = СтрЗаменить(Элемент.Value, ",", "#COMMA#");
ВходСтрока = СтрЗаменить(ВходСтрока, Элемент.Value, ФрагментБезЗапятых);
КонецЦикла;
// Теперь можно безопасно разбить строку по запятой
МассивЭлементов = СтрРазделить(ВходСтрока, ",");
// Возвращаем запятые на место и очищаем лишние кавычки
Для Индекс = 0 По МассивЭлементов.ВГраница() Цикл
Значение = СтрЗаменить(МассивЭлементов[Индекс], "#COMMA#", ",");
Значение = СокрЛП(Значение);
Если Лев(Значение, 1) = """" И Прав(Значение, 1) = """" Тогда
Значение = Сред(Значение, 2, СтрДлина(Значение) - 2);
КонецЕсли;
МассивЭлементов[Индекс] = Значение;
КонецЦикла;
При выборе подходящего метода ориентируйтесь на версию используемой платформы и операционную систему сервера: