Импорт данных из формата CSV (Comma-Separated Values) — это одна из самых частых задач, с которыми сталкивается разработчик 1С. Этот формат универсален, его легко сформировать из Excel или сторонних систем. Несмотря на кажущуюся простоту (текст с разделителями), процедура импорта имеет множество нюансов: от выбора кодировки до корректной обработки кавычек и сложных переносов строк. В этой статье мы подробно разберем, как реализовать загрузку данных для различных версий платформы 1С.
Начнем с классического подхода для «семерки». Здесь работа строится на использовании объекта Текст. Проанализируем базовый алгоритм, который позволяет выбрать файл и прочитать его построчно.
Для начала нам необходимо предоставить пользователю возможность выбора файла. Используем для этого объект ФС (Файловая Система):
Перем ИмяПути, ИмяФайла;
ИмяФайла = "";
ИмяПути = "";
Если ФС.ВыбратьФайл(0, ИмяФайла, ИмяПути, "Выберите файл CSV", "*.csv", , ) <> 1 Тогда
Возврат;
КонецЕсли;
ТекстДок = СоздатьОбъект("Текст");
ТекстДок.Открыть(ИмяПути + ИмяФайла);
После открытия файла нам нужно организовать цикл по строкам. Основная сложность заключается в том, что в 1С 7.7 нет встроенной функции разделения строки в массив. Рассмотрим функцию РазобратьСтроку, которая разбивает текст по символу-разделителю (например, точке с запятой) и помещает результат в СписокЗначений:
Функция РазобратьСтроку(Стр, Разделитель)
Сп = СоздатьОбъект("СписокЗначений");
ТекСтр = Стр;
Пока Найти(ТекСтр, Разделитель) > 0 Цикл
Поз = Найти(ТекСтр, Разделитель);
Значение = СокрЛП(Лев(ТекСтр, Поз - 1));
Сп.ДобавитьЗначение(Значение);
ТекСтр = Сред(ТекСтр, Поз + 1);
КонецЦикла;
Сп.ДобавитьЗначение(СокрЛП(ТекСтр)); // Добавляем последний фрагмент
Возврат Сп;
КонецФункции
Проанализируем ситуацию, когда первая строка файла содержит заголовки. Мы можем использовать их для автоматического создания колонок в объекте ТаблицаЗначений. Это сделает импорт более универсальным:
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Для НомСтроки = 1 По ТекстДок.КоличествоСтрок() Цикл
Стр = ТекстДок.ПолучитьСтроку(НомСтроки);
Если ПустаяСтрока(Стр) = 1 Тогда Продолжить; КонецЕсли;
Спис = РазобратьСтроку(Стр, ";");
Если НомСтроки = 1 Тогда
// Создаем колонки по первой строке
Для н = 1 По Спис.РазмерСписка() Цикл
ТЗ.НоваяКолонка(Спис.ПолучитьЗначение(н));
КонецЦикла;
Иначе
// Заполняем данные
ТЗ.НоваяСтрока();
Для н = 1 По Спис.РазмерСписка() Цикл
ТЗ.УстановитьЗначение(ТЗ.НомерСтроки, н, Спис.ПолучитьЗначение(н));
КонецЦикла;
КонецЕсли;
КонецЦикла;
Важное замечание по оптимизации: создание объекта СписокЗначений внутри цикла может существенно замедлить работу при больших объемах данных. Рекомендуется создать один объект до начала цикла и очищать его методом УдалитьВсе() внутри итераций.
В платформе 8.3 работа с файлами стала значительно проще и быстрее. Мы будем использовать объект ТекстовыйДокумент (или ЧтениеТекста) и встроенную функцию СтрРазделить.
Рассмотрим пример загрузки данных на стороне сервера после того, как файл был выбран на клиенте:
&НаСервере
Процедура ИмпортCSVНаСервере(ПолноеИмяФайла)
ТекстДок = Новый ТекстовыйДокумент;
// Важно указать правильную кодировку, например КодировкаТекста.UTF8
ТекстДок.Прочитать(ПолноеИмяФайла, КодировкаТекста.ANSI);
КоличествоСтрок = ТекстДок.КоличествоСтрок();
Для НомерСтроки = 1 По КоличествоСтрок Цикл
ТекСтрока = ТекстДок.ПолучитьСтроку(НомерСтроки);
// Используем СтрРазделить для получения массива подстрок
// Третий параметр "Ложь" указывает не пропускать пустые значения
МассивКолонок = СтрРазделить(ТекСтрока, ";", Ложь);
// Пример создания элемента справочника
Если МассивКолонок.Количество() >= 2 Тогда
НовыйОбъект = Справочники.Номенклатура.СоздатьЭлемент();
НовыйОбъект.Артикул = МассивКолонок[0];
НовыйОбъект.Наименование = МассивКолонок[1];
// Попытка записи объекта
Попытка
НовыйОбъект.Записать();
Исключение
Сообщить("Ошибка в строке " + НомерСтроки + ": " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Начиная с версии платформы 8.3.6, в 1С появился объект ЧтениеCSV. Это самый правильный и надежный метод, так как он автоматически обрабатывает сложные случаи: наличие кавычек внутри текста, переносы строк внутри полей и экранирование спецсимволов.
Разберем, как им пользоваться:
&НаСервере
Процедура ЗагрузитьЧерезЧтениеCSV(ПутьКФайлу)
ФайлCSV = Новый ЧтениеCSV;
// Указываем параметры: разделитель, символ кавычек
ФайлCSV.Открыть(ПутьКФайлу, "UTF-8", ";", """");
// Пропускаем заголовки, если нужно
Заголовки = ФайлCSV.Прочитать();
Пока Истина Цикл
МассивДанных = ФайлCSV.Прочитать();
Если МассивДанных = Неопределено Тогда
Прервать; // Конец файла
КонецЕсли;
// МассивДанных содержит значения текущей строки
ОбработатьСтрокуИмпорта(МассивДанных);
КонецЦикла;
ФайлCSV.Закрыть();
КонецПроцедуры
Рассмотрим важные аспекты, которые часто упускают из виду при разработке механизмов импорта:
UTF-8, а 1С пытается прочитать его как Windows-1251 (ANSI). Всегда уточняйте формат источника. В 8.x используйте КодировкаТекста.UTF8 или КодировкаТекста.ANSI.
НачатьТранзакцию();
// Цикл загрузки...
ЗафиксироватьТранзакцию();
Справочники.Номенклатура.НайтиПоНаименованию() внутри цикла. Это создает колоссальную нагрузку на SQL. Выясним причину замедления: каждое такое обращение — это отдельный запрос к БД. Лучше предварительно выгрузить нужные данные в Соответствие или использовать временные таблицы в запросе.Число() для цен и количеств, предварительно удаляя неразрывные пробелы: СтрЗаменить(Значение, Символы.НПП, "").ТекстовыйДокумент может вызвать переполнение памяти (так как загружает файл целиком). В таких случаях использование ЧтениеТекста или ЧтениеCSV является обязательным, так как они читают данные потоково.Резюме: Для старых конфигураций 7.7 используйте ручной парсинг строк через СписокЗначений. Для современных систем на базе 8.3 всегда отдавайте предпочтение объекту ЧтениеCSV, так как он соответствует международным стандартам обработки текстовых данных с разделителями и наиболее устойчив к ошибкам в структуре файла.