Как в 1С прочитать CSV-файл, если данные в колонках содержат символ-разделитель запятую?

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

При обмене данными через файлы формата CSV (Comma-Separated Values) часто возникает ситуация, когда в качестве разделителя колонок используется запятая, но при этом в самом тексте данных (например, в наименованиях номенклатуры, комментариях или адресах) также присутствуют запятые. Стандартное разбиение строки функцией СтрРазделить() в таких случаях приводит к ошибкам: одна колонка ошибочно разделяется на несколько частей, и вся структура таблицы сдвигается.

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

Стандарт RFC 4180 и правила экранирования данных

Проанализируем структуру корректно сформированного CSV-файла. Согласно стандарту RFC 4180, если текстовое поле содержит служебные символы (разделитель колонок, кавычки или перевод строки), оно обязательно должно быть обрамлено двойными кавычками (символ Quote Char):

1,Есть,"Если не утро, то день",20,Фабрика

Если внутри самого текста также встречаются кавычки, они удваиваются:

2,Есть,"ООО ""Рога и Копыта"", г. Москва",150,Склад

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

Вариант 1. Посимвольный разбор строки с учетом кавычек (алгоритм конечного автомата)

Это наиболее надежный алгоритм разбора, работающий на чистом коде 1С без использования сторонних библиотек и компонентов. Мы обходим строку символ за символом и отслеживаем состояние: находимся ли мы сейчас внутри текста в кавычках или в области обычных разделителей.

Посмотрим на пример универсальной функции, которая раскладывает CSV-строку в массив колонок с учетом кавычек и экранирования:


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

Разберем, как прочитать весь файл целиком с помощью объекта ЧтениеТекста:


Процедура ПрочитатьCSVФайл(ПутьКФайлу)
    Чтение = Новый ЧтениеТекста(ПутьКФайлу, КодировкаТекста.UTF8);
    СтрокаФайла = Чтение.ПрочитатьСтроку();
    
    Пока СтрокаФайла <> Неопределено Цикл
        // Пропускаем пустые строки
        Если Не ПустаяСтрока(СокрЛП(СтрокаФайла)) Тогда
            Колонки = РазложитьCSVСтроку(СтрокаФайла, ",");
            
            // Обработка полученных значений
            // Колонки[0], Колонки[1] и т.д.
        КонецЕсли;
        
        СтрокаФайла = Чтение.ПрочитатьСтроку();
    КонецЦикла;
    
    Чтение.Закрыть();
КонецПроцедуры

Вариант 2. Использование объекта ТабличныйДокумент

Платформа 1С содержит встроенный быстрый парсер табличных файлов, включая CSV. Метод ТабличныйДокумент.Прочитать() корректно понимает кавычки и запятые внутри значений ячеек.

Рассмотрим, как загрузить CSV через табличный документ:


Процедура ЗагрузитьCSVЧерезТабличныйДокумент(ПутьКФайлу)
    ТабДок = Новый ТабличныйДокумент;
    ТабДок.Прочитать(ПутьКФайлу, СпособЧтенияЗначенийТабличногоДокумента.Текст);
    
    ВсегоСтрок = ТабДок.ВысотаТаблицы;
    ВсегоКолонок = ТабДок.ШиринаТаблицы;
    
    Для НомерСтроки = 1 По ВсегоСтрок Цикл
        Для НомерКолонки = 1 По ВсегоКолонок Цикл
            ЗначениеЯчейки = ТабДок.Область(НомерСтроки, НомерКолонки).Текст;
            // Обработка данных ячейки
        КонецЦикла;
    КонецЦикла;
КонецПроцедуры

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

Вариант 3. Использование функций Библиотеки стандартных подсистем (БСП)

В типовых конфигурациях (1С:ERP, 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:Бухгалтерия) уже реализованы протестированные функции работы со строками в общем модуле СтроковыеФункцииКлиентСервер или в подсистеме «Загрузка данных из внешних файлов».

В зависимости от версии БСП можно использовать готовую функцию:


МассивЗначений = СтроковыеФункцииКлиентСервер.РазложитьСтрокуСУчетомКавычек(СтрокаФайла, ",", """");

Вариант 4. Что делать, если файл поврежден (запятая есть, а кавычек нет)?

Иногда выгрузка сформирована некорректно сторонней системой, и запятая попала в строку без экранирования кавычками, например: 1,Есть,Если не утро, то день,20,Фабрика (в строке 5 колонок по логике, но 6 фрагментов из-за разделителя внутри текста).

Если общее количество колонок строго фиксировано (например, 50 колонок), а проблемное поле только одно и находится на известной позиции (например, колонка 3), применим двунаправленный алгоритм разбора:

  1. Разбиваем строку стандартной функцией СтрРазделить(Строка, ",").
  2. Если общее число полученных элементов больше ожидаемого, извлекаем первые известные колонки (до проблемной) с начала массива.
  3. Извлекаем гарантированно корректные колонки с конца массива (справа налево).
  4. Все элементы, оставшиеся в середине массива, объединяем обратно через запятую — это и будет исходное значение поврежденного текстового поля.

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


Функция СобратьПоврежденнуюСтроку(Знач СтрокаCSV, Знач ОжидаемоКолонок, Знач ИндексПроблемнойКолонки)
    Части = СтрРазделить(СтрокаCSV, ",");
    
    Если Части.Количество() <= ОжидаемоКолонок Тогда
        Возврат Части;
    КонецЕсли;
    
    Результат = Новый Массив;
    
    // 1. Заполняем колонки до проблемной
    Для Сч = 0 По ИндексПроблемнойКолонки - 1 Цикл
        Результат.Добавить(Части[Сч]);
    КонецЦикла;
    
    // 2. Склеиваем среднюю часть, разбитую лишними запятыми
    ЛишнихЗапятых = Части.Количество() - ОжидаемоКолонок;
    ИндексКонцаПроблемной = ИндексПроблемнойКолонки + ЛишнихЗапятых;
    
    ПроблемныйТекст = "";
    Для Сч = ИндексПроблемнойКолонки По ИндексКонцаПроблемной Цикл
        ПроблемныйТекст = ПроблемныйТекст + ?(ПустаяСтрока(ПроблемныйТекст), "", ",") + Части[Сч];
    КонецЦикла;
    Результат.Добавить(ПроблемныйТекст);
    
    // 3. Заполняем оставшиеся колонки с конца
    Для Сч = ИндексКонцаПроблемной + 1 По Части.Количество() - 1 Цикл
        Результат.Добавить(Части[Сч]);
    КонецЦикла;
    
    Возврат Результат;
КонецФункции

Рекомендации по выбору решения

  1. Для стандартных CSV-файлов с кавычками используйте посимвольный разбор (конечный автомат) или метод ТабличныйДокумент.Прочитать().
  2. В типовых конфигурациях применяйте готовые функции модуля СтроковыеФункцииКлиентСервер.
  3. Для «битых» файлов без экранирующих кавычек применяйте реверсивный разбор по фиксированному количеству колонок.
← На главную