Как правильно загрузить файл XLSX в таблицу значений, если названия колонок — даты?

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

При автоматизации обмена данными часто возникает задача загрузки внешних файлов формата Excel (XLSX) в 1С. Типичный сценарий: файл выгрузки из сторонней системы (например, СБИС) содержит отчет, где заголовками колонок выступают даты (например, "01.09.2023", "02.09.2023" и так далее). При попытке загрузить такой файл через ПостроительЗапроса разработчики часто сталкиваются с неприятным эффектом: заголовки колонок в итоговой ТаблицеЗначений начинают дублироваться, заменяться или вовсе превращаться в нечитаемый набор символов. В этой статье мы разберем, почему это происходит и как правильно обработать данные, чтобы загрузка прошла корректно.

Почему возникает проблема с заголовками-датами

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

  1. Имя не может начинаться с цифры.
  2. Имя не может содержать точки, пробелы и другие спецсимволы (допустимо только нижнее подчеркивание).
  3. Имя должно быть уникальным в рамках таблицы.

Когда ПостроительЗапроса анализирует строку заголовков Excel, содержащую даты (например, "11.09.2023"), он пытается автоматически "нормализовать" их. Точки заменяются на подчеркивания, а если в начале стоит цифра, платформа пытается добавить префикс. Однако при использовании метода Прочитать с параметром СпособЧтенияЗначенийТабличногоДокумента.Значение, 1С воспринимает ячейку не как текст, а как тип Дата. Внутренние алгоритмы нормализации при превращении даты в идентификатор могут давать сбой, что приводит к "зацикливанию" имен (например, когда колонки с 11 по 20 число начинают именоваться так же, как с 01 по 10).

Разбор стандартного (проблемного) кода

Давайте проанализируем исходный код, который вызывает ошибку:


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

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

Решение №1: Предварительная обработка заголовков (самый быстрый способ)

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

Рассмотрим исправленный алгоритм:


// ... код загрузки файла в ТабличныйДокумент ...

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

// Теперь ПостроительЗапроса увидит корректные строковые идентификаторы
ПЗ = Новый ПостроительЗапроса;
ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабличныйДокумент.Область());
// ... дальнейшая работа с ПЗ ...

Этот способ позволяет сохранить "магию" построителя запросов (быструю типизацию строк), но исключает конфликты именования.

Решение №2: Использование ЧтениеДанныхXLSX (современный подход)

Если ваша платформа версии 8.3.10 и выше, рекомендуем отказаться от ТабличногоДокумента в пользу специализированного объекта ЧтениеДанныхXLSX. Это работает быстрее и дает полный контроль над процессом.

Проанализируем пример реализации:


ЧтениеXLSX = Новый ЧтениеДанныхXLSX;
ЧтениеXLSX.Открыть(ИмяВременногоФайла);

// Читаем первый лист
Страницы = ЧтениеXLSX.ПолучитьСтраницы();
Если Страницы.Количество() > 0 Тогда
    ЧтениеXLSX.ВыбратьСтраницу(Страницы[0]);
КонецЕсли;

ТЗ = Новый ТаблицаЗначений;
// Читаем первую строку для создания колонок
Если ЧтениеXLSX.Прочитать() Тогда
    Для НомерКолонки = 0 По ЧтениеXLSX.КоличествоКолонок() - 1 Цикл
        ИмяКолонки = "Кол_" + НомерКолонки; // Или обработка текста из ЧтениеXLSX.Значение()
        ТЗ.Колонки.Добавить(ИмяКолонки, , ЧтениеXLSX.Значение(НомерКолонки));
    КонецЦикла;
КонецЕсли;

// Читаем данные построчно
Пока ЧтениеXLSX.Прочитать() Цикл
    НоваяСтрока = ТЗ.Добавить();
    Для НомерКолонки = 0 По ЧтениеXLSX.КоличествоКолонок() - 1 Цикл
        НоваяСтрока[НомерКолонки] = ЧтениеXLSX.Значение(НомерКолонки);
    КонецЦикла;
КонецЦикла;

ЧтениеXLSX.Закрыть();

Рекомендации по выбору метода

Выясним, какой метод лучше подходит для вашей ситуации:

Важный совет: При чтении через ТабличныйДокумент всегда проверяйте параметр СпособЧтенияЗначенийТабличногоДокумента. Если вам важны именно строковые заголовки, используйте значение Текст, чтобы 1С не пыталась преобразовать "01.01.2023" в объект даты прямо в ячейке документа, что часто и становится причиной сбоев в работе ПостроителяЗапроса.

← На главную