Как разбить строку на массив подстрок с сохранением запятых внутри кавычек в 1С?

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

При интеграции и загрузке данных разработчики часто сталкиваются с разбором строк в формате CSV, где элементы разделены запятыми. Типовые функции разбиения (такие как платформенная функция СтрРазделить или библиотечная функция СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок) работают прямолинейно: они делят строку по каждому найденному символу-разделителю. Однако если внутри строки передаются числа с плавающей точкой или текстовые описания, заключенные в кавычки (например, "1425,00" или "Коррекция 'Проживание' от 30.04, 84526"), наивный алгоритм ошибочно разобьет такие поля на несколько частей.

Разберем подробно несколько надежных способов правильной обработки подобных строк в 1С:Предприятие — от создания универсального посимвольного парсера до использования встроенных механизмов платформы и регулярных выражений.

Способ 1. Универсальный посимвольный парсер (конечный автомат)

Рассмотрим наиболее надежный, производительный и полностью кроссплатформенный подход. Посимвольный обход строки с отслеживанием текущего состояния (находимся ли мы внутри кавычек) корректно работает на любых версиях платформы 1С:Предприятие, в любых клиентах и на серверах под управлением Windows и Linux.

Алгоритм пошагово выполняет следующие действия:

  1. Инициализируем результирующий массив, буфер для накопления символов текущего поля и логический флаг ВнутриКавычек = Ложь.
  2. В цикле от первого до последнего символа перебираем строку.
  3. Если встречаем символ кавычки, проверяем, не является ли он экранированной кавычкой (удвоенная кавычка по стандарту CSV). Если это экранирование, добавляем одну кавычку в буфер и сдвигаем счетчик. Если нет — инвертируем флаг ВнутриКавычек.
  4. Если встречаем символ-разделитель (запятую) и флаг ВнутриКавычек равен Ложь, фиксируем завершение текущего поля: добавляем накопленный буфер в массив и очищаем его. Если же мы находимся внутри кавычек, запятая просто дописывается в буфер как обычный символ.
  5. После завершения цикла добавляем последнее накопленное значение в массив.

Посмотрим на программную реализацию данной функции:


Функция РазложитьСтрокуСКавычками(Знач ИсходнаяСтрока, Знач Разделитель = ",") Экспорт
    
    Результат = Новый Массив;
    ДлинаСтроки = СтрДлина(ИсходнаяСтрока);
    ВнутриКавычек = Ложь;
    ТекущееЗначение = "";
    
    Индекс = 1;
    Пока Индекс <= ДлинаСтроки Цикл
        Символ = Сред(ИсходнаяСтрока, Индекс, 1);
        
        Если Символ = """" Тогда
            // Проверяем экранированную кавычку ("")
            Если Индекс < ДлинаСтроки И Сред(ИсходнаяСтрока, Индекс + 1, 1) = """" Тогда
                ТекущееЗначение = ТекущееЗначение + """";
                Индекс = Индекс + 1;
            Иначе
                ВнутриКавычек = НЕ ВнутриКавычек;
            КонецЕсли;
        ИначеЕсли Символ = Разделитель И НЕ ВнутриКавычек Тогда
            Результат.Добавить(СокрЛП(ТекущееЗначение));
            ТекущееЗначение = "";
        Иначе
            ТекущееЗначение = ТекущееЗначение + Символ;
        КонецЕсли;
        
        Индекс = Индекс + 1;
    КонецЦикла;
    
    Результат.Добавить(СокрЛП(ТекущееЗначение));
    
    Возврат Результат;
    
КонецФункции

Способ 2. Использование штатного объекта ЧтениеCSV (платформа 8.3.14+)

Начиная с версии платформы 8.3.14 в 1С доступен встроенный механизм работы с CSV-форматом. Объекты ЧтениеCSV и ПараметрыЧтенияCSV строго соблюдают стандарт RFC 4180, автоматически учитывая кавычки, переносы строк и экранирование.

Если исходные данные находятся в строковой переменной, а не в файле на диске, разберем, как прочитать их через поток в памяти (ПотокВПамяти):


Функция РазобратьСтрокуЧерезCSV(Знач ИсходнаяСтрока, Знач Разделитель = ",") Экспорт
    
    ПараметрыЧтения = Новый ПараметрыЧтенияCSV;
    ПараметрыЧтения.РазделительПолей = Разделитель;
    ПараметрыЧтения.СимволВыделенияСтрок = """";
    
    Поток = Новый ПотокВПамяти;
    Запись = Новый ЗаписьДанных(Поток);
    Запись.ЗаписатьСтроку(ИсходнаяСтрока);
    Запись.Закрыть();
    
    Поток.ТекущаяПозиция = 0;
    
    Чтение = Новый ЧтениеCSV(Поток, ПараметрыЧтения);
    МассивЗначений = Чтение.Прочитать();
    Чтение.Закрыть();
    
    Если МассивЗначений = Неопределено Тогда
        Возврат Новый Массив;
    КонецЕсли;
    
    Возврат МассивЗначений;
    
КонецФункции

Способ 3. Замена разделителей регулярными выражениями

Проанализируем подход, который часто используют для быстрой предобработки: найти все блоки текста, заключенные в кавычки, временно заменить внутри них разделители на специальные символы-маркеры (например, #;#), разбить строку стандартным методом, а затем вернуть исходные запятые на место.

В среде 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);
    КонецЕсли;
    МассивЭлементов[Индекс] = Значение;
КонецЦикла;

Выводы и рекомендации

При выборе подходящего метода ориентируйтесь на версию используемой платформы и операционную систему сервера:

  1. Посимвольный конечный автомат — универсальное и самое быстрое решение для разбора отдельных строк без привязки к внешним компонентам и версии платформы.
  2. Объект ЧтениеCSV — идеальный платформенный выбор для версий 8.3.14+, полностью соответствующий стандартам разбора табличных данных.
  3. Регулярные выражения — удобны при сложной нетиповой фильтрации и валидации строковых шаблонов.
← На главную