В процессе разработки часто возникает задача импорта данных из файлов Excel, которые имеют сложную визуальную структуру. Вместо плоской таблицы мы часто видим иерархию, где разделы или "Позиции" выделены жирным шрифтом или представлены в виде объединенных ячеек, а под ними располагаются детализирующие строки (материалы, ресурсы). Разберем по шагам, как правильно прочитать такой файл и преобразовать его в дерево значений 1С.
Это наиболее быстрый и современный способ, не требующий наличия установленного Microsoft Excel на сервере или компьютере пользователя. Мы прочитаем файл напрямую в объект ТабличныйДокумент и проанализируем его области. Рассмотрим основные этапы реализации.
Во-первых, загрузим файл в память системы:
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ПутьКФайлу, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Теперь проанализируем ситуацию. Чтобы отличить "Позицию" (родительский узел) от "Ресурса" (подчиненный узел), нам нужно проверять свойства ячейки в цикле. Основные признаки заголовка в вашем случае — это жирный шрифт и объединение ячеек.
Выясним причину, по которой обычный перебор строк может не работать. При чтении объединенной области значение содержится только в первой (левой верхней) ячейке. Все остальные ячейки, входящие в объединение, будут пустыми. Проанализируем пример кода для обхода строк:
Для НомерСтроки = 1 По ТабДок.ВысотаТаблицы Цикл
// Получаем область конкретной ячейки (например, первая колонка)
ТекущаяОбласть = ТабДок.Область(НомерСтроки, 1);
// 1. Проверка на жирный шрифт
ЭтоЗаголовок = ТекущаяОбласть.Шрифт.Жирный;
// 2. Проверка на объединение ячеек
// Если область содержит объединенные ячейки, ее ширина или высота будет больше 1
Если ТекущаяОбласть.СодержитОбъединенныеЯчейки Тогда
ЭтоЗаголовок = Истина;
КонецЕсли;
// Логика заполнения дерева значений будет описана ниже
КонецЦикла;
Рассмотрим альтернативный вариант, который дает еще больше контроля над форматированием, если на компьютере установлен Excel. С помощью COM-соединения мы можем напрямую обращаться к свойствам объекта Range. Проанализируем, как определить границы объединенной ячейки:
Excel = Новый COMОбъект("Excel.Application");
Книга = Excel.Workbooks.Open(ПутьКФайлу);
Лист = Книга.Sheets(1);
Для Стр = 1 По Лист.UsedRange.Rows.Count Цикл
Ячейка = Лист.Cells(Стр, 1);
// Свойство MergeCells возвращает Истина, если ячейка часть объединения
Если Ячейка.MergeCells Тогда
// Можно получить всю область объединения через MergeArea
ОбластьОбъединения = Ячейка.MergeArea;
Сообщить("Строк в заголовке: " + ОбластьОбъединения.Rows.Count);
КонецЕсли;
// Проверка шрифта через COM
Если Ячейка.Font.Bold Тогда
// Это жирный шрифт - признак новой позиции
КонецЕсли;
КонецЦикла;
После того как мы научились определять тип строки (родитель или потомок), нам нужно правильно заполнить объект ДеревоЗначений. Основная сложность здесь заключается в хранении ссылки на текущего "родителя". Посмотрим на пример реализации алгоритма:
ТекущаяСтрокаРодитель значением Неопределено.
// Создаем дерево
ДеревоДанных = Новый ДеревоЗначений;
ДеревоДанных.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
Переменная ТекущийРодитель;
Для НомерСтроки = 1 По ТабДок.ВысотаТаблицы Цикл
ТекущаяОбласть = ТабДок.Область(НомерСтроки, 1);
ЗначениеЯчейки = ТекущаяОбласть.Текст;
Если ПустаяСтрока(ЗначениеЯчейки) Тогда
Продолжить;
КонецЕсли;
// Определяем, является ли строка новой "Позицией"
Если ТекущаяОбласть.Шрифт.Жирный Или ТекущаяОбласть.СодержитОбъединенныеЯчейки Тогда
// Создаем корневой узел
ТекущийРодитель = ДеревоДанных.Строки.Добавить();
ТекущийРодитель.Наименование = ЗначениеЯчейки;
Иначе
// Если это не заголовок, добавляем в подчинение последнему родителю
Если ТекущийРодитель <> Неопределено Тогда
НоваяСтрока = ТекущийРодитель.Строки.Добавить();
НоваяСтрока.Наименование = ЗначениеЯчейки;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Разберем несколько важных моментов, которые помогут избежать ошибок при обработке реальных документов:
Очистка данных: Данные из Excel часто приходят с "мусором". Используйте функцию СокрЛП() и обязательно удаляйте неразрывные пробелы (символ с кодом 160). Это критично для корректного поиска и сравнения строк. Пример обработки текста:
ЧистыйТекст = СтрЗаменить(СокрЛП(ТекстИзExcel), Символ(160), "");
Координаты в объединенных ячейках: Помните, что если ячейка объединена по вертикали (например, на 3 строки), то при итерации циклом Для вы встретите значение только в первой строке. Для остальных строк Текст будет пустым. Наш алгоритм выше учитывает это, пропуская пустые строки, но вы можете расширить логику, проверяя свойство Область.ГраницаСверху или используя MergeArea в COM-модели, чтобы точно знать, сколько строк занимает заголовок.
Типизация: В Excel колонки могут содержать данные разных типов (число в одной строке, текст в другой). При заполнении дерева значений всегда приводите данные к нужному типу явно с помощью функций Число(), Дата(), чтобы избежать ошибок при отображении дерева в управляемой форме.
Таким образом, комбинируя анализ свойств шрифта и проверку объединения областей, мы можем надежно восстановить иерархическую структуру документа и заполнить дерево значений для дальнейшей работы в 1С.