В практике разработчика 1С часто возникает задача переноса табличных данных между компьютерами или базами данных. Ситуация усложняется, когда на одном из компьютеров (например, на рабочем сервере или терминале пользователя) не установлен пакет Microsoft Office, а исходные данные приходят именно в формате Excel. При этом данные могут содержать сложную иерархическую структуру (группировки строк), которую необходимо распознать программно.
В данной статье мы подробно разберем, как решить эти две взаимосвязанные проблемы: как быстро и надежно перенести таблицу значений, содержащую простые типы данных, на другой компьютер, а также как прочитать иерархическую структуру файла Excel без использования COM-объекта Excel.Application.
Если на целевом компьютере нет возможности прочитать исходный файл Excel (или база данных там работает слишком медленно для тяжелого импорта), самым разумным решением будет обработать файл на быстром компьютере, сформировать ТаблицаЗначений и перенести ее на целевой ПК. Поскольку в таблице хранятся только простые типы данных (строки, числа, даты, булево) без ссылок на элементы справочников конкретной базы, перед нами открывается несколько простых и эффективных путей переноса.
Важное замечание о внутреннем формате 1С: Многие разработчики опасаются использовать функции сериализации 1С для обмена между разными информационными базами, полагая, что уникальные идентификаторы (UUID) не сопоставятся. Для ссылочных типов (справочники, документы) это действительно так. Однако для примитивных типов данных внутренний формат 1С абсолютно универсален. Строка, полученная на одном компьютере, будет однозначно прочитана на другом в любой конфигурации.
Этот способ идеально подходит, если вам нужно быстро передать таблицу значений через буфер обмена, текстовый файл или простое текстовое поле. Рассмотрим, как выгрузить таблицу значений в текстовый файл на исходном компьютере:
// На исходном компьютере (где есть Excel и создана ТаблицаЗначений)
ПутьКФайлу = "C:\temp\exported_table.txt";
ТекстовыйДокумент = Новый ТекстовыйДокумент;
// Преобразуем таблицу значений во внутреннюю строку 1С
СтрокаДанных = ЗначениеВСтрокуВнутр(ТаблицаДанных);
// Записываем строку в файл
ТекстовыйДокумент.УстановитьТекст(СтрокаДанных);
ТекстовыйДокумент.Записать(ПутьКФайлу, КодировкаТекста.UTF8);
Теперь рассмотрим, как прочитать этот файл на целевом компьютере, где Excel не установлен:
// На целевом компьютере
ПутьКФайлу = "C:\temp\exported_table.txt";
ТекстовыйДокумент = Новый ТекстовыйДокумент;
Если ФайлСуществует(ПутьКФайлу) Тогда
ТекстовыйДокумент.Прочитать(ПутьКФайлу, КодировкаТекста.UTF8);
СтрокаДанных = ТекстовыйДокумент.ПолучитьТекст();
// Восстанавливаем таблицу значений со всей структурой и типами колонок
ТаблицаДанных = ЗначениеИзСтрокиВнутр(СтрокаДанных);
КонецЕсли;
Если нет необходимости передавать данные именно строкой (например, в теле XML-сообщения), можно воспользоваться еще более простым платформенным механизмом, который записывает объект напрямую в файл и считывает его обратно.
Запись таблицы значений выполняется всего одной строкой кода:
// Сохраняем таблицу значений напрямую в файл
ЗначениеВФайл("C:\temp\table_data.usr", ТаблицаДанных);
Восстановление таблицы значений на другом компьютере выполняется аналогично:
// Восстанавливаем таблицу значений из файла
ТаблицаДанных = ЗначениеИзФайла("C:\temp\table_data.usr");
Этот метод чрезвычайно быстр, надежен и не требует написания никаких алгоритмов обхода колонок и строк.
Если вам важно, чтобы файл обмена был легкочитаемым и его можно было при необходимости открыть и отредактировать в обычном текстовом редакторе или использовать в сторонних системах (не только в 1С), лучшим выбором будет сериализация в формат JSON. Начиная с версии платформы 8.3.6, в 1С реализована нативная и очень быстрая работа с JSON.
Поскольку напрямую таблицу значений сериализовать в JSON нельзя, мы преобразуем ее в массив структур, где каждая строка таблицы — это структура вида "ИмяКолонки": "Значение".
Рассмотрим пример сериализации таблицы значений в JSON:
// Выгрузка таблицы значений в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, " ");
ЗаписьJSON.ОткрытьФайл("C:\temp\table_data.json", , , ПараметрыЗаписи);
// Преобразуем таблицу в массив структур
МассивСтрок = Новый Массив;
Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл
СтруктураСтроки = Новый Структура;
Для Каждого Колонка Из ТаблицаДанных.Колонки Цикл
СтруктураСтроки.Вставить(Колонка.Имя, СтрокаТЗ[Колонка.Имя]);
КонецЦикла;
МассивСтрок.Добавить(СтруктураСтроки);
КонецЦикла;
// Записываем массив в файл JSON
ЗаписатьJSON(ЗаписьJSON, МассивСтрок);
ЗаписьJSON.Закрыть();
Для восстановления таблицы значений из JSON-файла на целевом компьютере выполним обратную операцию:
// Чтение JSON и восстановление таблицы значений
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.ОткрытьФайл("C:\temp\table_data.json");
МассивСтрок = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
// Создаем таблицу значений и восстанавливаем колонки по первой структуре
ТаблицаДанных = Новый ТаблицаЗначений;
Если МассивСтрок.Количество() > 0 Тогда
Для Каждого КлючИЗначение Из МассивСтрок[0] Цикл
ТаблицаДанных.Колонки.Добавить(КлючИЗначение.Ключ);
КонецЦикла;
// Заполняем таблицу данными
Для Каждого СтруктураСтроки Из МассивСтрок Цикл
НоваяСтрока = ТаблицаДанных.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтруктураСтроки);
КонецЦикла;
КонецЕсли;
---
Перейдем ко второй части проблемы. Что делать, если мы хотим отказаться от использования медленного COM-объекта Excel.Application на исходном компьютере (или если там тоже планируется отказаться от MS Excel в будущем), но нам жизненно необходимо знать уровень вложенности строк (группировки/папки), который в COM-модели определялся свойством OutlineLevel?
Стандартный метод платформы ТабличныйДокумент.Прочитать() отлично открывает файлы Excel без установленного офисного пакета, но, к сожалению, объект ТабличныйДокумент в 1С не имеет простого свойства программного доступа к уровню группировки конкретной строки. Тем не менее решение есть, и оно работает в разы быстрее, чем COM-соединение.
Современный файл Excel с расширением .xlsx представляет собой стандартный ZIP-архив, содержащий XML-файлы с данными. Мы можем распаковать этот архив средствами 1С и прочитать уровень группировки напрямую из XML-структуры листа. Данные о структуре строк хранятся в тегах <row>, где уровень вложенности указан в атрибуте outlineLevel.
Проанализируем пошаговый алгоритм реализации этого метода:
ЧтениеZipФайла для распаковки файла .xlsx во временную папку.xl/worksheets/sheet1.xml).ЧтениеXML, отслеживая теги row и их атрибуты.Ниже представлен готовый пример кода, реализующий этот алгоритм:
Функция ПолучитьГруппировкиСтрокИзExcel(ПутьКФайлуXLSX) Экспорт
Результат = Новый Соответствие; // Ключ - НомерСтроки (число), Значение - УровеньГруппировки (число)
// Создаем временную папку для распаковки
ВременныйКаталог = КаталогВременныхФайлов() + "excel_unpack_" + СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", "");
СоздатьКаталог(ВременныйКаталог);
// Распаковываем XLSX как ZIP-архив
Архив = Новый ЧтениеZipФайла(ПутьКФайлуXLSX);
Архив.ИзвлечьВсе(ВременныйКаталог, РежимВосстановленияПутейФайловZIP.Восстанавливать);
Архив.Закрыть();
// Путь к XML-данным первого листа книги Excel
ПутьКЛисту = ВременныйКаталог + "/xl/worksheets/sheet1.xml";
ФайлЛиста = Новый Файл(ПутьКЛисту);
Если ФайлЛиста.Существует() Тогда
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ПутьКЛисту);
Пока ЧтениеXML.Прочитать() Цикл
// Нас интересует начало элемента row (строка листа)
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "row" Тогда
НомерСтроки = 0;
УровеньГруппировки = 0;
// Обходим атрибуты строки
Пока ЧтениеXML.ПрочитатьАтрибут() Цикл
Если ЧтениеXML.Имя = "r" Тогда // "r" - индекс строки в Excel (начиная с 1)
НомерСтроки = Число(ЧтениеXML.Значение);
ИначеЕсли ЧтениеXML.Имя = "outlineLevel" Тогда // Уровень группировки
УровеньГруппировки = Число(ЧтениеXML.Значение);
КонецЕсли;
КонецЦикла;
Если НомерСтроки > 0 Тогда
Результат.Вставить(НомерСтроки, УровеньГруппировки);
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
КонецЕсли;
// Очищаем временные файлы
УдалитьФайлы(ВременныйКаталог);
Возврат Результат;
КонецФункции
Данная функция возвращает соответствие, где для каждого номера строки сопоставлен уровень ее группировки (например, строка 10 имеет уровень группировки 2). Этот метод работает молниеносно, так как чтение XML в 1С оптимизировано на уровне платформы и не требует инициализации тяжелых внешних приложений.
Если вам не хочется работать с распаковкой ZIP-архивов, существует альтернативный способ. При чтении файла XLSX в ТабличныйДокумент платформа 1С сохраняет структуру группировок. Если этот табличный документ записать в формате XML, в полученном файле иерархия строк будет представлена специальными тегами <rowGroup>.
Рассмотрим, как можно проанализировать такую структуру:
// Читаем файл Excel в Табличный документ
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать("C:\temp\file.xlsx");
// Сохраняем табличный документ в XML-формате 1С
ПутьКXML = ПолучитьИмяВременногоФайла("xml");
ТабДок.Записать(ПутьКXML, ТипФайлаТабличногоДокумента.XML);
// При чтении полученного XML анализируем вложенность элементов <rowGroup>
// Каждому уровню вложенности тега rowGroup будет соответствовать свой уровень группировки строк
Этот способ проще в реализации, но первый вариант (Вариант А) с прямым разбором ZIP-архива является наиболее производительным и точным, так как вы получаете исходные данные Excel "как есть", без искажений при конвертации в формат табличного документа 1С.
Мы рассмотрели различные подходы к переносу данных и работе с файлами Excel без использования COM-объектов. Для быстрого и простого переноса таблицы значений между базами 1С оптимально использовать связку методов ЗначениеВФайл() / ЗначениеИзФайла(). Если требуется кроссплатформенность, лучшим выбором станет формат JSON. А для чтения иерархической структуры файлов Excel без установленного MS Office идеально подходит прямой разбор XML-файла из ZIP-архива документа.