При разработке механизмов загрузки данных из электронных таблиц Microsoft Excel в программы 1С разработчики часто сталкиваются с неприятным поведением платформы: при попытке обратиться к ячейке табличного документа через свойство Значение возникает ошибка «Поле объекта недоступно для чтения». В то же время свойство Текст возвращает данные, но они представлены в локализованном строковом виде (например, даты с сокращениями названий месяцев или отформатированные числа с разделителями групп), что крайне неудобно для последующей программной обработки и записи в информационную базу.
Попытка принудительно установить для ячейки флаг СодержитЗначение = Истина нередко приводит к следующей ошибке: «Поле объекта недоступно для записи». Разберем по шагам, почему платформа ведет себя именно так и как правильно прочитать типизированные данные из Excel без использования медленного и платформозависимого COM-соединения.
Для понимания происходящего проанализируем модель работы объекта ТабличныйДокумент в платформе 1С:
ОбластьЯчеекТабличногоДокумента обращение к свойству Значение программно разрешено только тогда, когда булево свойство СодержитЗначение для данной ячейки установлено в Истина. По умолчанию метод ТабличныйДокумент.Прочитать() вызывается со значением второго параметра СпособЧтенияЗначенийТабличногоДокумента.Текст. При таком режиме платформа считывает все данные исключительно как строки для отображения, а признак СодержитЗначение для всех ячеек принимает значение Ложь.СодержитЗначение = Истина возникает, если табличный документ заблокирован от редактирования. Это происходит, когда у документа установлены свойства ТолькоПросмотр = Истина или Защита = Истина (например, если загружаемый лист Excel имел внутреннюю защиту или свойства были выставлены в элементе формы).СодержитЗначение = Истина постфактум, получить исходные типизированные данные уже не удастся: при текстовом чтении платформа не сохраняет в памяти типизированное значение из файла, поэтому ячейка станет пустой.Рассмотрим самое простое и правильное решение, предусмотренное разработчиками платформы. При вызове метода Прочитать() необходимо явно передать второй параметр, указав системное перечисление СпособЧтенияЗначенийТабличногоДокумента.Значение.
Посмотрим на пример кода:
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ПутьКФайлу, СпособЧтенияЗначенийТабличногоДокумента.Значение);
// Теперь свойство Значение доступно для чтения и возвращает исходный тип данных:
Ячейка = ТабДок.Область("R2C3");
Если Ячейка.СодержитЗначение Тогда
ЗначениеЯчейки = Ячейка.Значение;
ТипДанных = ТипЗнч(ЗначениеЯчейки); // Дата, Число, Строка или Булево
КонецЕсли;
После передачи этого системного перечисления платформа при чтении файла автоматически выставляет СодержитЗначение = Истина для всех заполненных ячеек и сохраняет исходные числа и даты как полноценные типы платформы 1С:Предприятие.
Построчный обход сотен и тысяч ячеек через метод Область() в цикле работает достаточно медленно. Однако после того, как документ прочитан с признаком СпособЧтенияЗначенийТабличногоДокумента.Значение, мы можем мгновенно преобразовать всю таблицу в привычную ТаблицуЗначений с сохранением типов колонок с помощью объекта ПостроительЗапроса.
Разберем реализацию этого быстрого метода:
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ПутьКФайлу, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Построитель = Новый ПостроительЗапроса;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабДок.Область());
Построитель.Выполнить();
// Получаем готовую ТаблицуЗначений с типизированными колонками
ТаблицаДанных = Построитель.Результат.Выгрузить();
Этот способ отрабатывает в разы быстрее прямого цикла по ячейкам, корректно заполняет типы колонок таблицы значений и полностью поддерживается на стороне сервера под управлением как Windows, так и Linux.
При использовании штатного метода чтения файлов Excel следует учитывать ряд важных технических особенностей:
Прочитать() всегда загружает исключительно первый лист файла. Выбрать нужный лист по имени или порядковому номеру штатными средствами табличного документа невозможно.Если необходимо обойти ограничение первого листа или требуется обрабатывать файлы нестандартной структуры, можно использовать прямое чтение формата .xlsx без внешних зависимостей. Поскольку современный файл Excel представляет собой упакованный ZIP-архив, его можно разобрать штатными средствами платформы с помощью объектов ЧтениеZipФайла и ЧтениеXML.
Рассмотрим общую схему такого подхода:
ЧтениеZipФайла.xl/sharedStrings.xml считывается общая таблица строк.xl/worksheets/ читается XML-файл нужного листа (например, sheet1.xml, sheet2.xml), что позволяет обращаться к абсолютно любому листу книги.Тем не менее, для большинства типовых задач импорта указания СпособЧтенияЗначенийТабличногоДокумента.Значение в методе Прочитать() оказывается полностью достаточно для корректного и быстрого получения данных.