Как быстро загрузить большой CSV файл целиком в 1С, минуя построчное чтение?

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

Часто разработчики сталкиваются с задачей импорта данных из объемных файлов формата CSV (с миллионами строк). Стандартный подход с построчным чтением через ТекстовыйДокумент занимает слишком много времени и неоправданно расходует ресурсы сервера. Разберем по шагам, как можно оптимизировать этот процесс и загрузить огромный файл целиком, быстро формируя готовую ТаблицаЗначений.

Использование ADODB (OLEDB / ODBC) для чтения файла как таблицы

Проанализируем одно из самых быстрых решений, доступных "из коробки" в среде Windows — использование COM-объектов ADO. Данный метод позволяет обратиться к текстовому файлу с разделителями с помощью SQL-запроса, как к обычной таблице базы данных. Скорость чтения при этом существенно возрастает.

Посмотрим на пример клиент-серверного кода, который реализует этот подход. Сначала мы выбираем файл, а затем читаем его содержимое через драйвер Microsoft Text Driver, выгружая данные напрямую в массив и далее в таблицу значений.


&НаКлиенте
Процедура ПрочитатьCSV(Команда)
    Режим = РежимДиалогаВыбораФайла.Открытие;
    ДиалогОткрытияФайла = Новый ДиалогВыбораФайла(Режим);
    ДиалогОткрытияФайла.Фильтр = "Текстовый документ(*.txt;*.csv;)|*.txt;*.csv;";
    ДиалогОткрытияФайла.МножественныйВыбор = Ложь;
    
    Если ДиалогОткрытияФайла.Выбрать() Тогда
        ВыбФайл = Новый Файл(ДиалогОткрытияФайла.ВыбранныеФайлы[0]);
        
        Connection = Новый ComОбъект("ADODB.Connection");
        Command = Новый ComОбъект("ADODB.Command");
        
        // Указываем строку подключения к папке с файлом
        СтрокаПодключения = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" + ВыбФайл.Путь + ";Extensions=csv;";
        Connection.ConnectionString = СтрокаПодключения;
        Connection.Open();
        
        Command.ActiveConnection = Connection;
        // Обращаемся к файлу с помощью SQL-запроса
        Command.CommandText = "SELECT * FROM [" + ВыбФайл.Имя + "]";
        
        Recordset = Command.Execute();
        
        Колонки = Новый Массив;
        Для Каждого Кол Из Recordset.Fields Цикл
            Колонки.Добавить(Кол.Name);
        КонецЦикла;
        
        // Передаем массив колонок и выгруженные данные на сервер
        ПрочитатьCSVНаСервере(Колонки, Recordset.GetRows().Выгрузить());
    КонецЕсли;
КонецПроцедуры

&НаСервере
Процедура ПрочитатьCSVНаСервере(Колонки, МассивСтрок)
    Таб = Новый ТаблицаЗначений;
    
    // Формируем колонки таблицы значений
    Для Инд = 0 По Колонки.ВГраница() Цикл
        Типы = Новый Массив;
        // Обход для определения типов колонок по первой строке
        НовыйТип = ТипЗнч(МассивСтрок[0][Инд]);
        Типы.Добавить(НовыйТип);
        Таб.Колонки.Добавить(Колонки[Инд], Новый ОписаниеТипов(Типы));
    КонецЦикла;
    
    // Заполняем таблицу значений данными
    Для Каждого Строка Из МассивСтрок Цикл
        НовСтр = Таб.Добавить();
        Для Инд = 0 По Колонки.ВГраница() Цикл
            НовСтр[Колонки[Инд]] = Строка[Инд];
        КонецЦикла;
    КонецЦикла;
КонецПроцедуры

Истинная причина медленного построчного чтения: ТекстовыйДокумент против ЧтениеТекста

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

Рассмотрим правильный нативный способ потокового чтения с помощью объекта ЧтениеТекста. Этот объект не выгружает весь файл в память, а читает его последовательно, строка за строкой. Данный подход позволяет обрабатывать файлы размером в несколько гигабайт за считанные минуты, не вызывая нехватку памяти (Out of Memory).


Чтение = Новый ЧтениеТекста(ПутьКФайлу, КодировкаТекста.UTF8);
СтрокаCSV = Чтение.ПрочитатьСтроку();

Пока СтрокаCSV <> Неопределено Цикл
    // Разбор строки по разделителю
    // ... логика парсинга ...
    СтрокаCSV = Чтение.ПрочитатьСтроку();
КонецЦикла;
Чтение.Закрыть();

Возможности объекта ТабличныйДокумент

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

После успешного чтения мы можем использовать ПостроительЗапроса или ПостроительОтчета для быстрой конвертации области табличного документа в объект ТаблицаЗначений. Однако будьте осторожны: для файлов из миллионов строк этот метод не подходит, так как построение визуальной сетки табличного документа в памяти сервера израсходует все доступные ресурсы.

Прямая загрузка в СУБД (BULK INSERT / COPY)

Если ваша информационная база развернута на MS SQL Server или PostgreSQL, рассмотрим самый быстрый из существующих способов — загрузку на уровне самой СУБД. Мы можем полностью исключить сервер 1С из процесса первоначального парсинга файла.

  1. Формируем временную таблицу в базе данных.
  2. Через тот же COMОбъект("ADODB.Connection") отправляем серверу БД команду массовой загрузки: BULK INSERT (для MS SQL) или COPY (для PostgreSQL). СУБД "заглотит" миллионы строк за несколько секунд.
  3. Подключаем эту таблицу в 1С через механизм ВнешниеИсточникиДанных.
  4. Работаем с загруженными данными с помощью обычных запросов 1С, перенося нужные записи во внутренние регистры или справочники.

Продвинутые механизмы: Объекты Поток и ЧтениеДанных

Начиная с версии 8.3, в платформе появились низкоуровневые объекты Поток и ЧтениеДанных. Разберем их преимущества. Они позволяют считывать файл бинарными блоками (чанками). Это открывает возможность для распараллеливания задачи.

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

Внешние компоненты (Native API)

Поскольку встроенный язык 1С является интерпретируемым, операции со строками и массивами в огромных циклах всегда будут уступать по скорости компилируемым языкам. Разберем альтернативу: сообществом разработчиков создано множество внешних компонент (на C++, C#, Rust) специально для парсинга CSV (например, FastCSV).

Подключив такую компоненту по технологии Native API, мы просто передаем ей путь к нашему файлу на диске. Компонента самостоятельно парсит текст в многопоточном режиме и возвращает в 1С уже заполненную объектную ТаблицаЗначений. Это избавляет программиста от необходимости писать сложные алгоритмы обхода колонок.

Критический нюанс: Использование транзакций

Часто иллюзия «медленного чтения CSV» возникает вовсе не из-за парсинга файла, а из-за медленной записи объектов в базу. Если внутри цикла чтения файла сразу идет создание элементов справочника СправочникМенеджер или записей регистров счетов, скорость будет катастрофически падать с каждой новой строкой.

Запомните важное правило: обработку больших файлов обязательно нужно оборачивать в транзакции порциями.


НачатьТранзакцию();
Счетчик = 0;

// Цикл чтения строк
Пока СтрокаCSV <> Неопределено Цикл
    // Запись данных в базу
    
    Счетчик = Счетчик + 1;
    Если Счетчик % 1000 = 0 Тогда
        ЗафиксироватьТранзакцию();
        НачатьТранзакцию();
    КонецЕсли;
    
    СтрокаCSV = Чтение.ПрочитатьСтроку();
КонецЦикла;

ЗафиксироватьТранзакцию();

Запись порциями по 1000–5000 строк в десятки раз снижает нагрузку на дисковую подсистему СУБД, уменьшает рост журнала транзакций и кардинально ускоряет процесс импорта данных.

← На главную