Как прочитать Excel-файл и загрузить данные в ТаблицуЗначений 1С без явного перебора строк?

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

В мире разработки на 1С часто возникает задача импорта данных из файлов Excel. Традиционные методы часто подразумевают трудоемкий и не всегда оптимальный перебор строк и колонок, что может быть утомительным и медленным для больших объемов данных. Однако существуют более изящные и эффективные подходы. Сегодня мы рассмотрим один из таких «потрясающих» методов, который позволяет загрузить содержимое Excel-файла в ТаблицуЗначений 1С, избегая явного циклического перебора, а также разберем его преимущества, недостатки и альтернативные решения.

Основной метод: загрузка Excel через ТабличныйДокумент и ПостроительОтчета

Представленный метод базируется на использовании встроенных в 1С объектов ТабличныйДокумент и ПостроительОтчета. Он позволяет нам сначала прочитать Excel-файл как табличный документ 1С, а затем использовать его как источник данных для построителя отчета, который уже выгрузит данные в удобную для нас ТаблицуЗначений.

Разберем процесс по шагам.

Шаг 1: Выбор файла на клиенте

Первым делом нам необходимо дать пользователю возможность выбрать нужный Excel-файл. Это действие выполняется на клиенте, поскольку именно там у пользователя есть доступ к локальной файловой системе. Для этого мы используем объект ДиалогВыбораФайла.

В следующем примере мы видим, как инициализируется диалог выбора файла, устанавливается фильтр для файлов Excel и предлагается пользователю выбрать файл. Выбранный путь к файлу сохраняется в переменной Файлик.


&НаКлиенте
Процедура ФайликНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    Режим = РежимДиалогаВыбораФайла.Открытие;
    ДиалогОткрытияФайла = Новый ДиалогВыбораФайла(Режим);
    ДиалогОткрытияФайла.ПолноеИмяФайла = "";
    Фильтр = "Excel файлы(*.xls;*.xlsx)|*.xls;*.xlsx";
    ДиалогОткрытияФайла.Фильтр = Фильтр;
    ДиалогОткрытияФайла.Заголовок = "Выберите файл товарной накладной!";
    
    Если ДиалогОткрытияФайла.Выбрать() Тогда
        Файлик = ДиалогОткрытияФайла.ПолноеИмяФайла;
    Иначе
        Файлик = "";
    КонецЕсли;
КонецПроцедуры

Шаг 2: Передача файла с клиента на сервер

Поскольку основные операции по чтению и обработке данных, как правило, выполняются на сервере (что является хорошей практикой и обязательным требованием для работы с файлами на сервере), выбранный на клиенте файл необходимо передать на сервер. Оригинальный код из сообщения 0 не демонстрирует эту часть, но она критически важна для корректной работы в клиент-серверном режиме. Мы можем использовать для этого функцию ПоместитьФайлНаСерверАсинх() или НачатьПомещениеФайловНаСервер(), если нужна асинхронная передача.

Вот как это может выглядеть концептуально:


// Предполагаем, что ПолноеИмяФайлаНаКлиенте - это переменная 'Файлик' из предыдущего шага.
// ИмяФайлаДляСервера - будет уникальным именем файла во временном хранилище.

// Пример использования ПоместитьФайлНаСерверАсинх
&НаКлиенте
Процедура ЗагрузитьНаСервер(ПолноеИмяФайлаНаКлиенте)
    Если ПустаяСтрока(ПолноеИмяФайлаНаКлиенте) Тогда
        Возврат;
    КонецЕсли;
    
    ИдентификаторФайлаНаСервере = ПоместитьФайлНаСервер(ПолноеИмяФайлаНаКлиенте, УникальныйИдентификатор); // УникальныйИдентификатор - уникальная строка
    
    // Вызываем серверную процедуру, передавая идентификатор временного хранилища
    ЗагрузитьИзФайлаНаСервере2022(ИдентификаторФайлаНаСервере);
КонецПроцедуры

&НаСервере
Процедура ЗагрузитьИзФайлаНаСервере2022(АдресФайлаВоВременномХранилище)
    // Извлекаем файл из временного хранилища во временный файл на сервере
    ПолноеИмяВременногоФайлаНаСервере = ПолучитьИмяВременногоФайла(".xls"); // Или ".xlsx"
    ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресФайлаВоВременномХранилище);
    ДвоичныеДанныеФайла.Записать(ПолноеИмяВременногоФайлаНаСервере);
    
    // Дальнейший код для чтения Excel будет использовать ПолноеИмяВременногоФайлаНаСервере
    // ...
КонецПроцедуры

Или, как часто делают для простоты, передают путь к файлу из временного хранилища сразу:


// В процедуре на клиенте после выбора файла:
Если ДиалогОткрытияФайла.Выбрать() Тогда
    Файлик = ДиалогОткрытияФайла.ПолноеИмяФайла;
    // Помещаем файл во временное хранилище и получаем его адрес
    АдресФайлаВоВременномХранилище = ПоместитьФайлВоВременноеХранилище(Новый ДвоичныеДанные(Файлик));
    
    // Вызываем серверную процедуру
    ЗагрузитьИзФайлаНаСервере2022(АдресФайлаВоВременномХранилище);
Иначе
    Файлик = "";
КонецЕсли;

// ...

&НаСервере
Процедура ЗагрузитьИзФайлаНаСервере2022(АдресФайлаВоВременномХранилище)
    // Здесь мы уже работаем с адресом в хранилище, а не с путем на клиенте.
    // Если ТабДок.Прочитать() требует путь к файлу, то его нужно будет получить из хранилища.
    // Часто удобнее сначала сохранить во временный файл на сервере.
    ИмяВременногоФайлаНаСервере = ПолучитьИмяВременногоФайла(); // Получаем путь к временному файлу на сервере
    ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресФайлаВоВременномХранилище);
    ДвоичныеДанные.Записать(ИмяВременногоФайлаНаСервере);
    
    // Далее используем ИмяВременногоФайлаНаСервере для ТабДок.Прочитать()
    ТабДок = Новый ТабличныйДокумент;
    ТабДок.Прочитать(ИмяВременногоФайлаНаСервере, СпособЧтенияЗначенийТабличногоДокумента.Значение);
    // ...
КонецПроцедуры

Шаг 3: Чтение и загрузка данных в ТаблицуЗначений на сервере

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

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


&НаСервере
Процедура ЗагрузитьИзФайлаНаСервере2022(ИмяФайлаНаСервере) // Предполагаем, что сюда приходит имя файла на сервере
Табл.Очистить(); // Очистка существующей таблицы, если есть
ТаблДок.Очистить(); // Очистка существующего табличного документа, если есть
 
ТабДок = Новый ТабличныйДокумент;
// Читаем Excel-файл как ТабличныйДокумент 1С
ТабДок.Прочитать(СокрЛП(ИмяФайлаНаСервере), СпособЧтенияЗначенийТабличногоДокумента.Значение);

// Определяем область для чтения. Можно указать конкретную область, например,
// ТабДок.Область(НачальнаяСтрока, НачальнаяКолонка, КонечнаяСтрока, КонечнаяКолонка)
// В данном случае читаем всю таблицу
ОбластьЯчеек = ТабДок.Область(1, 1, ТабДок.ВысотаТаблицы, ТабДок.ШиринаТаблицы);

// Создаем ОписаниеИсточникаДанных из области ячеек
ИсточникДанных = Новый ОписаниеИсточникаДанных(ОбластьЯчеек);

// Инициализируем ПостроительОтчета
ПостроительОтчета = Новый ПостроительОтчета; 
ПостроительОтчета.ИсточникДанных = ИсточникДанных;

// Выполняем построитель отчета
ПостроительОтчета.Выполнить();

// Выгружаем результат в ТаблицуЗначений
ТабЗначений = ПостроительОтчета.Результат.Выгрузить();
КонецПроцедуры

Преимущества метода ТабличныйДокумент.Прочитать() и ПостроительОтчета

Этот метод действительно заслуживает внимания благодаря нескольким ключевым достоинствам:

  1. Простота и лаконичность кода: Мы минимизируем объем кода для чтения Excel-файла. Вместо циклов и явного обращения к ячейкам, мы используем высокоуровневые методы.
  2. Отсутствие явного перебора строк: Основная "фишка" метода, которая так порадовала автора темы. Весь сложный механизм чтения и преобразования данных берет на себя платформа 1С.
  3. Автоматическое определение структуры: ПостроительОтчета способен автоматически определить имена и количество колонок, основываясь на первой строке выбранной области табличного документа. Это удобно, когда формат файла стандартизирован.

Недостатки и ограничения метода

Несмотря на свою привлекательность, данный метод имеет ряд существенных ограничений и может быть неэффективен в более сложных сценариях:

  1. Проблемы с заголовками колонок:
    • Если заголовки колонок находятся не в первой строке Excel-файла (например, в десятой), ПостроительОтчета может некорректно их определить или пропустить данные.
    • Символы в названиях колонок Excel (например, пробелы, точки, специальные символы) могут быть недопустимы для имен колонок в 1С, что требует их преобразования.
    • Если в Excel есть две колонки с одинаковыми названиями, это может привести к непредсказуемому поведению при выгрузке.
  2. Работа с несколькими листами: Метод по умолчанию читает только первый лист Excel-файла. Если необходимо прочитать данные с 56-го или любого другого листа, этот подход потребует доработки или использования альтернативных методов.
  3. Обработка больших файлов и производительность: При работе с очень большими Excel-файлами (десятки и сотни тысяч строк) метод ТабличныйДокумент.Прочитать() и ПостроительОтчета может работать медленно или вызывать зависания, что было отмечено в обсуждении. Определение "последней строки" как 1 048 576 даже при наличии нескольких строк данных - это частая проблема, которая может сильно замедлить процесс.
  4. Защищенные паролем файлы и листы: Объект ТабличныйДокумент.Прочитать() не может работать с файлами или листами, защищенными паролем, что приводит к ошибкам.
  5. Изменение порядка колонок и пропущенные колонки: Как было замечено в обсуждении, при выгрузке из построителя в таблицу значений порядок колонок может измениться или некоторые колонки могут быть пропущены, что требует дополнительной обработки или ручного сопоставления.
  6. Совместимость с Microsoft Office (32/64-бит): Если сервер 1С 64-битный, а установленный Microsoft Office 32-битный (или наоборот), могут возникнуть проблемы с совместимостью, если в системе задействуются компоненты Office.
  7. Нестандартная структура файла: Для Excel-файлов со сложной, неоднородной структурой, объединенными ячейками или несколькими "шапками" метод может дать непредсказуемый результат.
  8. Необходимость передачи файла на сервер: Как мы уже рассмотрели, для клиент-серверного взаимодействия требуется дополнительный код для передачи файла с клиента на сервер, что усложняет общую картину.

Альтернативные методы работы с Excel-файлами в 1С

Учитывая ограничения основного метода, давайте рассмотрим другие подходы к работе с Excel в 1С, которые предлагают большую гибкость и контроль, хотя и могут быть сложнее в реализации.

1. Чтение через COM-объект (OLE-автоматизация)

Этот метод позволяет 1С напрямую взаимодействовать с установленным на компьютере приложением Microsoft Excel. Вы можете создать объект COMОбъект("Excel.Application") и управлять им, как если бы вы работали с Excel вручную.

Концептуальный пример:


&НаСервере
Процедура ЗагрузитьЧерезCOM(ИмяФайлаНаСервере)
    Попытка
        Excel = Новый COMОбъект("Excel.Application");
        Excel.Visible = Ложь; // Скрываем Excel
        Excel.DisplayAlerts = Ложь; // Отключаем предупреждения
        
        Книга = Excel.Workbooks.Open(ИмяФайлаНаСервере);
        Лист = Книга.Sheets(1); // Доступ к первому листу
        
        // Чтение данных из ячеек или диапазона
        // Например, чтение значения ячейки A1:
        ЗначениеЯчейки = Лист.Cells(1, 1).Value;
        
        // Или чтение диапазона в массив или ТаблицуЗначений
        // Для этого обычно требуется явный перебор или создание ТаблицыЗначений 1С
        // и заполнение ее по строкам/колонкам из Excel
        
        Книга.Close(0); // Закрываем книгу без сохранения изменений
        Excel.Quit();  // Закрываем Excel
    Исключение
        Сообщить("Ошибка при работе с Excel через COM: " + ОписаниеОшибки());
        Если Excel <> Неопределено Тогда
            Excel.Quit();
        КонецЕсли;
    КонецПопытки;
КонецПроцедуры

2. Чтение через ADO (ActiveX Data Objects)

Метод ADO позволяет подключаться к файлу Excel как к полноценной базе данных с помощью OLE DB драйверов и выполнять SQL-запросы для извлечения данных.

3. Использование внешних компонент

Существуют сторонние внешние компоненты, разработанные специально для 1С, которые позволяют читать файлы Excel (как .xls, так и .xlsx) напрямую, без необходимости установки Microsoft Office.

4. Прямое чтение OpenXML (для .xlsx)

Файлы формата .xlsx на самом деле представляют собой ZIP-архивы, внутри которых находятся XML-файлы с данными, структурой и форматированием. Можно программно распаковать такой архив и парсить XML-файлы, чтобы получить данные.

5. Работа с CSV-файлами как альтернатива Excel

Когда это возможно, преобразование Excel-файла в формат CSV (Comma Separated Values) и последующая работа с ним как с текстовым файлом может быть гораздо более эффективным и простым решением.

Обработка типичных проблем и частные случаи

При работе с внешними файлами данных мы неизбежно сталкиваемся с различными сложностями. Давайте разберем, как можно решать наиболее частые из них.

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

Описание проблемы: Метод с ПостроителемОтчета предполагает, что заголовки колонок находятся в первой строке. Если это не так, или если заголовки содержат недопустимые символы для имен колонок 1С (например, пробелы, точки, знаки препинания), или если заголовки дублируются, это может привести к некорректной выгрузке данных или ошибкам.

Решение:

  1. Программное изменение области чтения: Если заголовки начинаются, например, с 10-й строки, мы можем указать ПостроителюОтчета, что данные начинаются с этой строки:
    
    // ОбластьЯчеек = ТабДок.Область(1, 1, ТабДок.ВысотаТаблицы, ТабДок.ШиринаТаблицы); // Исходная строка
    ОбластьЯчеек = ТабДок.Область(10, 1, ТабДок.ВысотаТаблицы, ТабДок.ШиринаТаблицы); // Если заголовки с 10-й строки
    

    Однако, в этом случае заголовки будут определены по 10-й строке. Если нам нужны данные, а заголовки не важны или нужно их переименовать, можно использовать подход, предложенный в сообщении 45.

  2. Явное указание колонок или переименование: После выгрузки в ТаблицуЗначений мы можем программно переименовать колонки или добавить нужные.
  3. Пример альтернативной функции для переименования колонок:

    В следующем примере мы видим функцию, которая читает табличный документ, а затем принудительно создает колонки в результирующей таблице значений с именами "Номер_1", "Номер_2" и так далее. Это полезно, когда названия колонок в Excel нестабильны, или они не важны, и нам просто нужны данные.

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

    Эта функция сначала создает временный ТабличныйДокумент, в который "вставляет" нумерованные заголовки, а затем выводит в него содержимое исходного табличного документа. Таким образом, ПостроительЗапроса (аналогично ПостроителюОтчета) гарантированно получает понятные имена колонок.

2. Изменение порядка колонок и пропущенные колонки

Описание проблемы: Иногда после автоматической загрузки порядок колонок в результирующей ТаблицеЗначений не соответствует ожидаемому или некоторые колонки из Excel вовсе не попали в ТЗ. В обсуждении это было отмечено как проблема.

Решение:

  1. Использование соответствия: После получения ТаблицыЗначений, мы можем создать новую ТЗ с правильной структурой и затем перебирать строки исходной ТЗ, заполняя новую с учетом соответствия колонок.
    
    НоваяТЗ = Новый ТаблицаЗначений;
    НоваяТЗ.Колонки.Добавить("ПравильноеИмя1");
    НоваяТЗ.Колонки.Добавить("ПравильноеИмя2");
            
    Для Каждого СтрокаТЗ Из ТабЗначений Цикл
        НоваяСтрока = НоваяТЗ.Добавить();
        НоваяСтрока.ПравильноеИмя1 = СтрокаТЗ["ИсходноеИмяКолонкиИзExcel"];
        // ...
    КонецЦикла;
    
  2. Проверка наличия колонок: Перед обращением к колонке, всегда проверяйте ее наличие: Если ТабЗначений.Колонки.Найти("ИмяКолонки") <> Неопределено Тогда.

3. Большие файлы и производительность

Описание проблемы: Как упоминалось, обработка очень больших Excel-файлов (сотни тысяч строк) методом ТабличныйДокумент.Прочитать() и ПостроительОтчета может быть очень медленной или привести к зависанию. Точное определение последней строки Excel как 1 048 576, даже если данных меньше, усугубляет проблему.

Решение:

  1. Ограничение области чтения: Если вы знаете точный диапазон данных, используйте его при вызове ТабДок.Область(), чтобы не читать пустые строки.
  2. Поэтапное чтение: Для очень больших файлов можно использовать COM-объект и читать данные блоками (по 1000-5000 строк), что снижает потребление памяти.
  3. Оптимизация COM: При работе с COM-объектом всегда скрывайте окно Excel (Excel.Visible = Ложь) и отключайте предупреждения (Excel.DisplayAlerts = Ложь), чтобы повысить производительность.
  4. CSV-файлы: Если возможно, предложите пользователю сохранять данные в формат CSV. Это самый быстрый способ импорта больших объемов табличных данных.

4. Защищенные паролем файлы и листы

Описание проблемы: Методы 1С напрямую не могут прочитать защищенный паролем Excel-файл или лист.

Решение: Единственным надежным способом является использование COM-объекта. При открытии книги через Excel.Workbooks.Open() можно указать пароль в параметрах, если он известен.


// Пример открытия защищенной книги через COM
Книга = Excel.Workbooks.Open(ИмяФайла, , , , "ПарольКниги");
// Если защищен лист, можно снять защиту:
Лист.Unprotect("ПарольЛиста");

5. Ошибки чтения и преобразования типов

Описание проблемы: При чтении данных из Excel могут возникать ошибки преобразования типов (например, Excel хранит число как текст) или общие ошибки чтения, если файл поврежден или имеет нестандартную структуру.

Решение:

  1. Обработка исключений: Всегда оборачивайте вызов ТабДок.Прочитать() (и другие операции с файлами) в конструкцию Попытка...Исключение для корректной обработки возможных ошибок.
  2. Явное преобразование типов: После получения ТаблицыЗначений, при работе со значениями из колонок, явно преобразуйте их к нужному типу 1С (Число(), Строка(), Дата(), Булево()), используя проверки ТипЗнч() или Попытка...Исключение для каждого преобразования.

6. Совместимость 32/64-битных версий Microsoft Office и сервера 1С

Описание проблемы: В 64-битной среде 1С могут возникнуть проблемы при попытке использовать 32-битные COM-объекты (например, 32-битный Microsoft Office) и наоборот. Это является частой причиной ошибок "класс не зарегистрирован" или других проблем совместимости.

Решение:

  1. Установка соответствующей версии Office: В идеале, версия Office (32-бит или 64-бит) должна соответствовать архитектуре сервера 1С (если COM-объект используется на сервере) или клиента (если COM-объект используется на клиенте).
  2. Настройка DCOM: В некоторых случаях можно попытаться настроить DCOM для взаимодействия 64-битного приложения с 32-битным COM-сервером, но это достаточно сложный процесс.
  3. Использование внешних компонент: Внешние компоненты часто предоставляют версии, совместимые как с 32-битными, так и с 64-битными средами, что позволяет обойти эту проблему.

Таким образом, хотя метод с ПостроителемОтчета и ТабличныйДокумент.Прочитать() удобен своей лаконичностью и идеально подходит для простых и стандартизированных Excel-файлов, для более сложных сценариев, требующих гибкости, полного контроля над структурой Excel, обработки ошибок или высокой производительности, нам придется обратиться к другим подходам, таким как OLE-автоматизация, ADO, внешние компоненты или даже прямое парсинг OpenXML. Выбор метода всегда зависит от конкретных требований задачи, сложности структуры Excel-файла и имеющихся ресурсов.

← На главную