Загрузка из EXCEL в 1С. Чтение файла XLSX средствами 1С. ПостроительDOM

18.04.15

Интеграция - Загрузка и выгрузка в Excel

ПРАКТИЧЕСКОЕ ПОСОБИЕ РАЗРАБОТЧИКА: Метод "NativeXLSX" (ПостроительDOM).
Обрабатываются данные EXCEL типов: "Строка", "Число", "Дата" ("Время"), "Булево", "Процент", а также изображения.

В данной статье приведен функционал, с помощью которого в обработке
"Импорт из EXCEL и др.источников (xls,xlsx,ods,sxc,dbf,mxl,csv,sql) в 1С": //infostart.ru/public/120961/
производится считывание данных из файлов табличного типа *.xlsx средствами 1С (метод "NativeXLSX").

Метод позволяет обрабатывать данные EXCEL типов: "Строка", "Число", "Дата" ("Время"), "Булево", "Процент", а также изображения.
Примечание: Изображение должны быть единственным значением в ячейке. Принадлежность изображения ячейке определяется по левому верхнему углу этого изображения.


Методы загрузки из внешнего источника:
- Метод "MS ADO" (Чтение файлов xls, xlsx средствами Microsoft ADO): //infostart.ru/public/163640/
- Метод "MS EXCEL" (Чтение файлов xls, xlsx с картинками средствами Microsoft Office): //infostart.ru/public/163641/
- Метод "LO CALC" (Чтение файлов xls, xlsx, ods, sxc с картинками средствами LibreOffice): //infostart.ru/public/163642/
- Метод "NativeXLSX" (Чтение файлов xlsx с картинками средствами 1С. ПостроительDOM): //infostart.ru/public/300092/
- Метод "NativeXLSX". Предыдущий вариант (Чтение файлов xlsx средствами 1С. ЧтениеXML):
//infostart.ru/public/225624/
- Метод "Excel1C" (Загрузка на платформе 8.3.6 с картинками. Чтение файлов xls, xlsx, ods): //infostart.ru/public/341855/
- Список листов файла: //infostart.ru/public/163724/


Функционал, представленный в статье, позволяет организовать импорт (загрузку) из файла типа XLSX в таблицу значений.
При этом учитывается, что XLSX-файл - это некий контейнер, который можно распаковать средствами 1С в набор XML-файлов.
В конечном счете задача сводится к чтению xml-файлов, содержащих различную информаци:
- строковые значения [sharedStrings.xml];
- индексы строковых значений, числовые значения, формулы [собственно страницы EXCEL-таблицы sheet...xml];
- информация об изображениях [drawing...xml];
- стили форматирования [styles.xml].
Чтение XML-файлов осуществляется с помощью связки ЧтениеXML-ПостроительDOM-ДокументDOM (встроенные объекты платформы 1С), т.е. ДокументDOM получается как результат чтения ПостроителемDOM объекта ЧтениеXML - источника данных XML.
В дальнейшем осуществляется обход дочерних узлов ДокументаDOM, чтение их значений и различных атрибутов.
Данная методика на мой взгляд проще и удобнее использования объекта ЧтениеXML для разбора структурированных XML-файлов, коими представляется XLSX-файл.


Публикаций на тему загрузки из EXCEL - множество, но
"
— Вам билетёр нужен?
— Был нужен, да уже взяли.
— Может и я на что сгожусь?
— Может и сгодишься, если скалиться не будешь ...
"
"НЕУЛОВИМЫЕ МСТИТЕЛИ" (1966).


В первом комментарии к публикации прикреплен файл, содержащий текст модуля.


&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ФайлEXCEL= "D:\Товар.xlsx";
    ИмяЛиста = Новый Структура ("ИмяЛиста, НомерЛиста","Alfa2", 2);
    СтрокаЗаголовка = 1;
    НачСтрока = 0;
    КонСтрока = 0;
    КолвоСтрокExcel = 0;
    ЗагрузитьМетодом_NativeXLSX_НаСервере(ФайлEXCEL, ИмяЛиста, СтрокаЗаголовка, НачСтрока, КонСтрока, КолвоСтрокExcel);
КонецПроцедуры

&НаСервере
Процедура ЗагрузитьМетодом_NativeXLSX_НаСервере(ФайлEXCEL, ИмяЛиста, СтрокаЗаголовка, НачСтрока, КонСтрока, КолвоСтрокExcel)
    ТаблицаРезультат = ЗагрузитьМетодом_NativeXLSX(ФайлEXCEL, ИмяЛиста, СтрокаЗаголовка, НачСтрока, КонСтрока, КолвоСтрокExcel);
КонецПроцедуры

// Метод "NativeXLSX" (ПостроительDOM).
// Преобразует текст формата XML (файл типа XLSX) в таблицу значений,
// при этом колонки таблицы формируются на основе описания в XML.
//
// Параметры:
//         ФайлEXCEL - Полное имя файла (путь к файлу с именем файла и расширением)
//         ИмяНомерЛиста - Структура Имя и Номер выбранного листа файла EXCEL.
//        СтрокаЗаголовка (по умолчанию = 1) - Номер строки EXCEL, в которой расположены заголовки колонок.
//            Не используется.
//      В обработке 1-я строка анализируется для сопоставления колонок EXCEL с реквизитами 1С (справочники, докуметны, регистры).
//        НачСтрока (по-умолчанию = 0) - Номер начальной строки, начиная с которой считываются данные из EXCEL.
//        КонСтрока (по-умолчанию = 0) - Номер конечной строки, которой заканчиваются считываемые данные из EXCEL.
//            Если НачСтрока=0 и КонСтрока=0, то считывается вся таблица, находящаяся на листе EXCEL.
//        КолвоСтрокEXCEL - Количество строк на листе "ИмяЛиста" EXCEL. Возвращается в вызываемую процедуру.
//
// Возвращаемые значения:
//         ТаблицаРезультат - Результат считывания с листа "ИмяЛиста" EXCEL.
//
&НаСервере
Функция ЗагрузитьМетодом_NativeXLSX(Знач ФайлEXCEL, Знач ИмяНомерЛиста, Знач СтрокаЗаголовка = 1, Знач НачСтрока = 0, Знач КонСтрока = 0, КолвоСтрокEXCEL = 0)
    Перем ZIPКаталог, ФайлИмяЛиста, ФайлНомерЛиста, КолвоКолонокEXCEL, НомерСтроки, НомерКолонки, ИмяКолонки, ИндексКолонки, ШиринаКолонки, ДлинаСтроки;
    Перем МассивИменКолонокXLSX, МассивSharedStrings, МассивNumFmtId, СоответствиеNumFmtIdFormatCode, ТаблицаIMAGE, СтрокаIMAGE, КаталогMEDIA;
    Перем ДокументDOM, ЭлементыROW, ЭлементСтрока, ЭлементыCOL, ЭлементКолонка;
    Перем ЗначениеЯчейки, ТипЗначения, ФорматЯчейки, ФорматСтиля;
    Перем ТаблицаРезультат, НоваяСтрока;
   
    ФайлИмяЛиста    = ИмяНомерЛиста.ИмяЛиста;
    ФайлНомерЛиста    = ИмяНомерЛиста.НомерЛиста;
   
    ФайлНомерЛиста = ?(ФайлНомерЛиста = 0, 1, ФайлНомерЛиста);
   
    ZIPКаталог = ПолучитьИмяКаталогаВременныхФайловДляРаспаковкиXLSX();
   
    Если НЕ ПредварительнаяОбработкаФайлаXLSX(ФайлEXCEL, ZIPКаталог) Тогда
        Возврат Новый ТаблицаЗначений;
    КонецЕсли;
   
    СоответствиеNumFmtIdFormatCode = Новый Соответствие;
    МассивNumFmtId = ИзвлечьДанныеИзФайла_StylesXML(ZIPКаталог, СоответствиеNumFmtIdFormatCode);
   
    МассивSharedStrings = ИзвлечьДанныеИзФайла_SharedStringsXML(ZIPКаталог);
   
    КаталогMEDIA = "media";
    ТаблицаIMAGE = ИзвлечьДанныеИзФайла_DrawingXML(ZIPКаталог, ФайлНомерЛиста, КаталогMEDIA);
   
    ДокументDOM = ПолучитьДокументDOM_NativeXLSX(ZIPКаталог + "XL\WorkSheets\Sheet" + ФайлНомерЛиста + ".xml");
    ЭлементыROW = ПолучитьЭлементыROW_NativeXLSX(ДокументDOM);
    Если ЭлементыROW = Неопределено ИЛИ ЭлементыROW.Количество() = 0 Тогда
        // Завершение работы.
        // Закрытие Объектов.
        ДокументDOM = Неопределено;
        Возврат Новый ТаблицаЗначений;
    КонецЕсли;
   
    МассивИменКолонокXLSX = ПолучитьМассивИменКолонокНаЛистеXLSX(ДокументDOM, ЭлементыROW);
    КолвоКолонокEXCEL = МассивИменКолонокXLSX.Количество();
   
    // Создание результирующей таблицы, в которую будут записываться считанные из EXCEL данные.
    ТаблицаРезультат = Новый ТаблицаЗначений;
   
    // Формирование колонок результирующей таблицы.
   
    // "НомерСтроки" - для наглядности и удобства.
    // В зависимости от разрабатываемой обработки.
    // "Сопоставлено" - может быть другим.
    // Здесь же могут быть добавлены другие колонки, не формируемые из содержимого файла EXCEL.
    ТаблицаРезультат.Колонки.Добавить("НомерСтроки", Новый ОписаниеТипов("Число"), "№", 4);
    ТаблицаРезультат.Колонки.Добавить("Сопоставлено", Новый ОписаниеТипов("Булево"), "Сопоставлено", 1);
    ТаблицаРезультат.Колонки.Добавить("УровеньГруппировки", Новый ОписаниеТипов("Число"), "Гр", 2);    // Группировка строк в файле EXCEL.
    Для ит = 1 ПО КолвоКолонокEXCEL Цикл
        ИмяКолонки = "N" + ит;
        Колонка = ТаблицаРезультат.Колонки.Добавить(ИмяКолонки);
    КонецЦикла;
   
    // Строки XLSX.
    НомерСтроки = 0;
    Для Каждого ЭлементСтрока ИЗ ЭлементыROW Цикл
        НомерСтроки = НомерСтроки + 1;
       
        // Начало диапазона считываемых данных.
        Если НЕ НачСтрока = 0 И НЕ НомерСтроки = 1 И НомерСтроки < НачСтрока Тогда
            Продолжить;
        КонецЕсли;
       
        // Окончание диапазона считываемых данных.
        Если КонСтрока > 0 И НомерСтроки > КонСтрока Тогда
            Прервать;
        КонецЕсли;
       
        // Окончание данных.
        Если КолвоСтрокEXCEL > 0 И НомерСтроки > КолвоСтрокEXCEL Тогда
            Прервать;
        КонецЕсли;
       
        НоваяСтрока = ТаблицаРезультат.Добавить();
        НоваяСтрока.НомерСтроки = НомерСтроки;
        НоваяСтрока.УровеньГруппировки = ЗначениеАтрибутаУзлаDOM(ЭлементСтрока, "outlineLevel");
       
        // Колонки XLSX.
        НомерКолонки = 0;
        ЭлементыCOL = ЭлементСтрока.ДочерниеУзлы;
        Для ит = 0 ПО КолвоКолонокEXCEL-1 Цикл
            НомерКолонки = НомерКолонки + 1;
           
            ЭлементКолонка = Неопределено;
            Попытка
                ЭлементКолонка = ЭлементыCOL[ит];
                ТипЗначения = ЗначениеАтрибутаУзлаDOM(ЭлементКолонка, "t");
                ФорматЯчейки = ЗначениеАтрибутаУзлаDOM(ЭлементКолонка, "s");
                ИмяКолонки = ЗначениеАтрибутаУзлаDOM(ЭлементКолонка, "r");
                ИмяКолонкиБезЦифр = ЗаменитьОдниСимволыДругими("0123456789", ИмяКолонки, "");
                ИндексКолонки = МассивИменКолонокXLSX.Найти(ИмяКолонкиБезЦифр) + 1;
            Исключение
                ЭлементКолонка = Неопределено;
                ИндексКолонки = НомерКолонки;
            КонецПопытки;
           
            ИмяКолонки = "N" + ИндексКолонки;
           
            ЗначениеЯчейки = Неопределено;
            Если НЕ ЭлементКолонка = Неопределено И НЕ ЭлементКолонка.ПервыйДочерний = Неопределено И (ВРег(ЭлементКолонка.ПервыйДочерний.ИмяУзла) = "V"  ИЛИ ВРег(ЭлементКолонка.ПервыйДочерний.ИмяУзла) = "F") Тогда    // "V" - Значение, "F" - Формула.
               
                Если ВРег(ЭлементКолонка.ПервыйДочерний.ИмяУзла) = "F" Тогда
                    Если НЕ ЭлементКолонка.ПоследнийДочерний = Неопределено И ВРег(ЭлементКолонка.ПоследнийДочерний.ИмяУзла) = "V" Тогда
                        ЗначениеЯчейки = ЭлементКолонка.ПоследнийДочерний.ТекстовоеСодержимое;
                    Иначе
                        Сообщить("Строка " + НомерСтроки + " Колонка " + ИндексКолонки + " значение формулы не определено.");
                    КонецЕсли;
                Иначе
                    ЗначениеЯчейки = ЭлементКолонка.ТекстовоеСодержимое;
                КонецЕсли;
               
                ФорматСтиля = "";
                Если (НЕ ФорматЯчейки = "" И НЕ ФорматЯчейки = Неопределено) Тогда
                    Попытка
                        ФорматСтиля = СоответствиеNumFmtIdFormatCode.Получить(МассивNumFmtId[Число(ФорматЯчейки)]);
                    Исключение
                        ФорматСтиля = "";
                    КонецПопытки;
                КонецЕсли;
               
                Если ЗначениеЗаполнено(ЗначениеЯчейки) Тогда
                   
                    Если ТипЗначения = Неопределено ИЛИ ВРег(ТипЗначения) = "N" ИЛИ ВРег(ТипЗначения) = "B" Тогда    // Тип "Число", "Булево".
                        Попытка
                            Значение1 = Число(ЗначениеЯчейки);
                        Исключение
                            Значение1 = ЗначениеЯчейки;
                        КонецПопытки;
                       
                        ЗначениеЯчейки = Значение1;
                       
                        Если (ФорматСтиля = "" ИЛИ ФорматСтиля = Неопределено) Тогда
                            // ФорматСтиля = Неопределено - Атрибут "s" отсутствует.
                            // MS Office может не формировать в xml-файле описание стиля форматирования для ячейки.
                            // LibreOffice формирует в xml-файле необходимые описания стиля форматирования ячейки.
                            // Сообщить("Не определен стиль форматирования для кода " + ФорматЯчейки + " значения " + ЗначениеЯчейки);
                        КонецЕсли;
                       
                        Если ТипЗнч(ЗначениеЯчейки) = Тип("Строка")
                            И (Найти(ЗначениеЯчейки, "E-") > 0 ИЛИ Найти(ЗначениеЯчейки, "E+") > 0) Тогда
                            ЗначениеЯчейки = ПолучитьЧислоВСтепениИзСтроковогоЗначения(ЗначениеЯчейки);
                        КонецЕсли;
                       
                        Если ТипЗнч(ЗначениеЯчейки) = Тип("Число") Тогда
                            // ПРОЦЕНТ.
                            Если ЭтоПроцентXLSX(ЗначениеЯчейки, ФорматСтиля) Тогда
                                ЗначениеЯчейки = Окр(ЗначениеЯчейки * 100, 3);
                            // БУЛЕВО.
                            ИначеЕсли ЭтоБулевоXLSX(ЗначениеЯчейки, ФорматСтиля) Тогда
                                ЗначениеЯчейки = Булево(ЗначениеЯчейки);
                            // ВРЕМЯ.
                            ИначеЕсли ЭтоВремяXLSX(ЗначениеЯчейки, ФорматСтиля) Тогда
                                ЗначениеЯчейки = КонвертироватьЧислоXLSXвДатуВремя(ЗначениеЯчейки);
                            // ДАТА.
                            ИначеЕсли ЭтоДатаXLSX(ЗначениеЯчейки, ФорматСтиля) Тогда
                                ЗначениеЯчейки = КонвертироватьЧислоXLSXвДату(ЗначениеЯчейки);
                            // ЧИСЛО.
                            ИначеЕсли ТипЗначения = Неопределено
                                ИЛИ ( ЭтоЧислоXLSX(ЗначениеЯчейки, ФорматСтиля)
                                И НЕ ЭтоБулевоXLSX(ЗначениеЯчейки, ФорматСтиля)
                                И НЕ ЭтоВремяXLSX(ЗначениеЯчейки, ФорматСтиля)
                                И НЕ ЭтоДатаXLSX(ЗначениеЯчейки, ФорматСтиля) )
                                Тогда
                                // Без преобразования.
                            Иначе
                                // Прочие форматы.
                            КонецЕсли;
                           
                        Иначе
                           
                            Сообщить("Не удалось преобразовать значение к ""числовому""(число, процент, время, дата, булево) типу.");
                           
                        КонецЕсли;
                       
                    ИначеЕсли ВРег(ТипЗначения) = "S" Тогда
                       
                        // МассивSharedStrings может быть пустым.
                        Попытка
                            ЗначениеЯчейки = СокрЛП(МассивSharedStrings[Число(ЗначениеЯчейки)]);
                        Исключение
                            ЗначениеЯчейки = "";
                        КонецПопытки;
                       
                    ИначеЕсли ВРег(ТипЗначения) = "STR" Тогда
                       
                        Если ТипЗнч(ЗначениеЯчейки) = Тип("Строка") Тогда
                            ЗначениеЯчейки = СокрЛП(ЗначениеЯчейки);
                        КонецЕсли;
                       
                    КонецЕсли;
                   
                КонецЕсли;
               
                НоваяСтрока[ИмяКолонки] = ЗначениеЯчейки;
               
                // Используется при формировании таблицы на форме обработки.
                ШиринаКолонки = ТаблицаРезультат.Колонки[ИмяКолонки].Ширина;
                ДлинаСтроки    = СтрДлина(СокрЛП(НоваяСтрока[ИмяКолонки]));
                ТаблицаРезультат.Колонки[ИмяКолонки].Ширина = ?(ШиринаКолонки < ДлинаСтроки, ДлинаСтроки, ШиринаКолонки);
               
            ИначеЕсли ЭлементКолонка = Неопределено ИЛИ (ЭлементКолонка.ПервыйДочерний = Неопределено И ТаблицаIMAGE.Количество() > 0) Тогда
               
                Для Каждого СтрокаIMAGE ИЗ ТаблицаIMAGE Цикл
                    Если СтрокаIMAGE.ROW = НомерСтроки И СтрокаIMAGE.COL = НомерКолонки Тогда
                        НоваяСтрока[ИмяКолонки] = ZIPКаталог + "XL\" + КаталогMEDIA + "\" + СтрокаIMAGE.FILE;
                        Прервать;
                    КонецЕсли;
                КонецЦикла;
               
                // Используется при формировании таблицы на форме обработки.
                ШиринаКолонки = ТаблицаРезультат.Колонки[ИмяКолонки].Ширина;
                ДлинаСтроки    = СтрДлина(СокрЛП(НоваяСтрока[ИмяКолонки]));
                ТаблицаРезультат.Колонки[ИмяКолонки].Ширина = ?(ШиринаКолонки < ДлинаСтроки, ДлинаСтроки, ШиринаКолонки);
               
            КонецЕсли;
           
        КонецЦикла;
       
    КонецЦикла;
   
    // Завершение работы.
    // Закрытие Объектов.
    ДокументDOM = Неопределено;
   
    УдалитьКолонкиСНулевойШириной(ТаблицаРезультат);
   
    Возврат ТаблицаРезультат;
   
КонецФункции

&НаСервере
Функция ПолучитьИмяКаталогаВременныхФайловДляРаспаковкиXLSX()
    Возврат КаталогВременныхФайлов() + "XLSX\";
КонецФункции

&НаСервере
Функция ПредварительнаяОбработкаФайлаXLSX(ФайлEXCEL, ZIPКаталог)
    Перем Файл;
   
    Файл = ПолучитьОбъектФайл(ФайлEXCEL);
    Если Файл = Неопределено Тогда
        Сообщить("Невозможно загрузить данные, т.к. невозможно открыть для чтения файл:
        |" + ФайлEXCEL);
        Возврат Ложь;
    КонецЕсли;
   
    Если НЕ ВРег(Файл.Расширение) = ".XLSX" Тогда
        Сообщить("Файл с расширением " + Файл.Расширение + " не поддерживается методом NativeXLSX:
        |" + ФайлEXCEL);
        Возврат Ложь;
    КонецЕсли;
   
    Если НЕ РаспаковатьXLSXвКаталогВременныхФайлов(ФайлEXCEL, ZIPКаталог) Тогда
        Сообщить("Ошибка распаковки файла.
        |Возможно каталог временных файлов занят.
        |" + ФайлEXCEL);
        Возврат Ложь;
    КонецЕсли;
   
    Возврат Истина;
   
КонецФункции

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

&НаСервере
Функция ФайлСуществуетИДоступен(ПолноеИмяФайла)
    Перем Файл;
   
    Файл = Новый Файл(ПолноеИмяФайла);
    Если НЕ Файл.Существует() Тогда
        Возврат Ложь;
    КонецЕсли;
   
    // Проверка: Занят ли файл другим процессом?
    Попытка
        ПереместитьФайл(Файл.ПолноеИмя, Файл.ПолноеИмя);
    Исключение
        Сообщить("Файл не доступен:
        |" + Файл.ПолноеИмя);
        Возврат Ложь;
    КонецПопытки;
   
    Возврат Истина;
   
КонецФункции

&НаСервере
Функция РаспаковатьXLSXвКаталогВременныхФайлов(ФайлEXCEL, ZIPКаталог)
    Перем ZIPФайл;
   
    Попытка
        УдалитьФайлы(ZIPКаталог);
       
        ZIPФайл = Новый ЧтениеZipФайла;
        ZIPФайл.Открыть(ФайлEXCEL);
        ZIPФайл.ИзвлечьВсе(ZIPКаталог, РежимВосстановленияПутейФайловZIP.Восстанавливать);
        Возврат Истина;
    Исключение
        Возврат Ложь;
    КонецПопытки;
   
    Возврат Истина;
   
КонецФункции

&НаСервере
Функция ПолучитьДокументDOM_NativeXLSX(ФайлSheetXML)
    Перем ЧтениеXML, ПостроительDOM, ДокументDOM;
   
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.ОткрытьФайл(ФайлSheetXML);
   
    ПостроительDOM = Новый ПостроительDOM;
    Попытка
        ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
        //Сообщить("Версия XML: " + ДокументDOM.ВерсияXML);
    Исключение
        Сообщить(ОписаниеОшибки());
        ДокументDOM = Неопределено;
    КонецПопытки;
   
    // Завершение работы.
    // Закрытие Объектов.
    ЧтениеXML.Закрыть();
   
    Возврат ДокументDOM;
   
КонецФункции

&НаСервере
Функция ПолучитьЭлементыROW_NativeXLSX(Знач ДокументDOM)
    Перем ЭлементыROW;
   
    Попытка
        ЭлементыROW = ДокументDOM.ПолучитьЭлементыПоИмени("row");
    Исключение
        Сообщить(ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;
   
    // Если строки пусты, то они не включаются в список элементов.
    // Если строки пусты и отдельные ячейки содержат оформление (например задан тип "Текст"),
    // то эти строки включаются в список (содержание: пустые).
    Возврат ЭлементыROW;
   
КонецФункции

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

&НаСервере
Функция ПолучитьМассивИменКолонокНаЛистеXLSX(ДокументDOM, ЭлементыROW)
    Перем DIMENSION, ДиапазонДанных, ДиапазонКолонок, ПерваяКолонка, ПоследняяКолонка, НомерПервойК, НомерПоследнейК;
    Перем ЭлементыCOL, Колонка;
    Перем МассивИменКолонокXLSX, КолвоКолонокEXCEL;
   
    // DIMENSION.
    Попытка
        DIMENSION = ДокументDOM.ПолучитьЭлементыПоИмени("DIMENSION");
        ДиапазонДанных     = ЗначениеАтрибутаУзлаDOM(DIMENSION[0], "ref");
        ДиапазонКолонок = ЗаменитьОдниСимволыДругими("0123456789", ДиапазонДанных, "");
        ПерваяКолонка     = Лев(ДиапазонКолонок, Найти(ДиапазонКолонок ,":") - 1);
        ПоследняяКолонка= Сред(ДиапазонКолонок, Найти(ДиапазонКолонок ,":") + 1);
        НомерПервойК    = НомерКолонкиДесятичный(ПерваяКолонка);
        НомерПоследнейК    = НомерКолонкиДесятичный(ПоследняяКолонка);
    Исключение
        НомерПоследнейК = Неопределено;
        Сообщить("Не удалось определить конечную колонку диапазона файла XLSX");
    КонецПопытки;
   
    МассивИменКолонокXLSX = Новый Массив;
   
    ЭлементыCOL = ЭлементыROW[0].ДочерниеУзлы;
    Для Каждого Колонка ИЗ ЭлементыCOL Цикл
        ИмяКолонки = ЗначениеАтрибутаУзлаDOM(Колонка, "r");
        ИмяКолонки = ЗаменитьОдниСимволыДругими("0123456789", ИмяКолонки, "");
        МассивИменКолонокXLSX.Добавить(ИмяКолонки);
    КонецЦикла;
   
    КолвоКолонокEXCEL = МассивИменКолонокXLSX.Количество();
    Если НЕ НомерПоследнейК = Неопределено Тогда
        Если КолвоКолонокEXCEL < НомерПоследнейК Тогда
            Сообщить("Строка заголовков колонок (1-я строка) заполнена не полностью:
            |К-во колонок строки меньше объявленной: " + КолвоКолонокEXCEL + "<" + НомерПоследнейК);
            ДополнитьМассивИменКолонокXLSX(НомерПоследнейК, МассивИменКолонокXLSX);
        ИначеЕсли МассивИменКолонокXLSX.Количество() > НомерПоследнейК Тогда
            Сообщить("Строка заголовков колонок (1-я строка) содержит больше колонок, чем объявлено:
            |К-во колонок строки больше объявленной:" + КолвоКолонокEXCEL + ">" + НомерПоследнейК);
        КонецЕсли;
    КонецЕсли;
   
    Возврат МассивИменКолонокXLSX;
   
КонецФункции

&НаСервере
Функция НомерКолонкиДесятичный(ИмяКолонкиXLSX)
    Перем Латиница, ДлинаНомера, Поз, ит;
    Перем НомерКолонки;
   
    Латиница = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";    // 26.
    ДлинаНомера = СтрДлина(ИмяКолонкиXLSX);
    НомерКолонки = 0;
    Для ит = 1 ПО ДлинаНомера Цикл
        Поз = Найти(Латиница, Сред(ИмяКолонкиXLSX, (ДлинаНомера + 1 - ит), 1));
        НомерКолонки = НомерКолонки + Поз * Pow(26, ит - 1);
    КонецЦикла;
   
    Возврат НомерКолонки;
   
КонецФункции

&НаСервере
Процедура ДополнитьМассивИменКолонокXLSX(Знач КолвоКолонокExcel, МассивИменКолонокXLSX, Индекс = - 1)
    Перем Алфавит, ит, Буква;
   
    Алфавит = РазложитьСтрокуВМассивПодстрок("A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z", ",");
   
    Если МассивИменКолонокXLSX.Количество() >= КолвоКолонокExcel Тогда
        Возврат;
    КонецЕсли;
   
    Если Индекс > Алфавит.Количество() - 1 Тогда
        Возврат;
    КонецЕсли;
   
    Буква = ?(Индекс = -1, "", Алфавит[Индекс]);
    Для ит = 0 ПО Алфавит.Количество() - 1 Цикл
        Если МассивИменКолонокXLSX.Найти(Буква + Алфавит[ит]) = Неопределено Тогда
            МассивИменКолонокXLSX.Добавить(Буква + Алфавит[ит]);
        КонецЕсли;
        Если МассивИменКолонокXLSX.Количество() >= КолвоКолонокExcel Тогда
            Возврат;
        КонецЕсли;
    КонецЦикла;
   
    Если МассивИменКолонокXLSX.Количество() > КолвоКолонокExcel Тогда
        Возврат;
    Иначе
        Индекс = Индекс + 1;
        ДополнитьМассивИменКолонокXLSX(КолвоКолонокExcel, МассивИменКолонокXLSX, Индекс);
    КонецЕсли;
   
    Возврат;
   
КонецПроцедуры

&НаСервере
Функция ИзвлечьДанныеИзФайла_StylesXML(ZIPКаталог, СоответствиеNumFmtIdFormatCode)
    Перем Файл, ДокументDOM, ЭлементыNnumFmts, ЭлементыCellXfs, ЭлементыКоллекции, Элемент;
    Перем МассивNumFmtId, ит;
   
    МассивNumFmtId = Новый Массив;
    СоответствиеNumFmtIdFormatCode = Новый Соответствие;
   
    Файл = Новый Файл(ZIPКаталог + "XL\Styles.xml");
    Если НЕ Файл.Существует() Тогда
        Возврат Новый Массив;
    КонецЕсли;
   
    ДокументDOM = ПолучитьДокументDOM_NativeXLSX(Файл.ПолноеИмя);
   
    Попытка
        ЭлементыNnumFmts = ДокументDOM.ПолучитьЭлементыПоИмени("numFmts");
    Исключение
        Сообщить(ОписаниеОшибки());
        Возврат Новый Массив;
    КонецПопытки;
   
    Если ЭлементыNnumFmts.Количество() > 0 Тогда
        ЭлементыКоллекции = ЭлементыNnumFmts[0].ДочерниеУзлы;
        Для Каждого Элемент ИЗ ЭлементыКоллекции Цикл
            СоответствиеNumFmtIdFormatCode.Вставить(ЗначениеАтрибутаУзлаDOM(Элемент, "numFmtId"), ВРег(ЗначениеАтрибутаУзлаDOM(Элемент, "formatCode")));
        КонецЦикла;
    КонецЕсли;
   
    Попытка
        ЭлементыCellXfs = ДокументDOM.ПолучитьЭлементыПоИмени("cellXfs");
    Исключение
        Сообщить(ОписаниеОшибки());
        Возврат Новый Массив;
    КонецПопытки;
   
    Если ЭлементыCellXfs.Количество() > 0 Тогда
        ЭлементыКоллекции = ЭлементыCellXfs[0].ДочерниеУзлы;
        Для Каждого Элемент ИЗ ЭлементыКоллекции Цикл
            МассивNumFmtId.Добавить(ЗначениеАтрибутаУзлаDOM(Элемент, "numFmtId"));
        КонецЦикла;
    КонецЕсли;
 
    // Проверка сопоставления кодов массива и соответствия.
    // MS Office может не формировать в xml-файле описание стиля форматирования для ячейки.
    // LibreOffice формирует в xml-файле необходимые описания стиля форматирования ячейки.
    Для Каждого ит ИЗ МассивNumFmtId Цикл
        Если СоответствиеNumFmtIdFormatCode.Получить(ит) = Неопределено Тогда
            Если ит = "0" Тогда    // Стандарт для числа (Целое число).
                СоответствиеNumFmtIdFormatCode.Вставить(ит, "GENERAL");
            ИначеЕсли ит = "9" ИЛИ ит = "10" Тогда    // Форматы для % ("0%", "0.00%").
                СоответствиеNumFmtIdFormatCode.Вставить(ит, "0%");
            ИначеЕсли ит = "14" ИЛИ ит = "16" Тогда    // Форматы для даты.
                СоответствиеNumFmtIdFormatCode.Вставить(ит, "DD.MM.YYYY");
            ИначеЕсли ит = "45" ИЛИ ит = "46" ИЛИ ит = "47" Тогда    // Форматы для времени.
                СоответствиеNumFmtIdFormatCode.Вставить(ит, "HH:MM:SS");
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
   
    Возврат МассивNumFmtId;
   
КонецФункции

&НаСервере
Функция ИзвлечьДанныеИзФайла_SharedStringsXML(ZIPКаталог)
    Перем Файл, ДокументDOM, ЭлементыSharedStrings, ЭлементыКоллекции, Элемент;
    Перем МассивSharedStrings;
   
    // Если в файле EXCEL не содержится значений, имеющих тип "СТРОКА", то файл "SharedStrings.xml" не формируется.
   
    МассивSharedStrings = Новый Массив;
   
    Файл = Новый Файл(ZIPКаталог + "XL\SharedStrings.xml");
    Если НЕ Файл.Существует() Тогда
        Возврат Новый Массив;
    КонецЕсли;
   
    ДокументDOM = ПолучитьДокументDOM_NativeXLSX(Файл.ПолноеИмя);
   
    Попытка
        ЭлементыSharedStrings = ДокументDOM.ПолучитьЭлементыПоИмени("sst");
    Исключение
        Сообщить(ОписаниеОшибки());
        Возврат Новый Массив;
    КонецПопытки;
   
    Если ЭлементыSharedStrings.Количество() > 0 Тогда
        ЭлементыКоллекции = ЭлементыSharedStrings[0].ДочерниеУзлы;
        Для Каждого Элемент ИЗ ЭлементыКоллекции Цикл
            МассивSharedStrings.Добавить(Элемент.ТекстовоеСодержимое);
        КонецЦикла;
    КонецЕсли;
   
    Возврат МассивSharedStrings;
   
КонецФункции

&НаСервере
Функция ИзвлечьДанныеИзФайла_DrawingXML(Знач ZIPКаталог, Знач ФайлНомерЛиста, Знач КаталогMEDIA)
    Перем Файл, ДокументRELS, ЭлементыRELS, НомерDRAWING, ДокументDRAWING, ЭлементыDRAWING, Атрибут, ФайлDRAWING;
    Перем ЭлементыКоллекции1, Элемент1, ЭлементыКоллекции2, Элемент2, ЭлементыКоллекции3, Элемент3;
    Перем ТаблицаIMAGE, НоваяСтрока, СтруктураПараметров, НайденныеСтрокиТЗ, СтрокаТЗ;
   
    // Если в файле EXCEL не содержится значений, имеющих тип "ИЗОБРАЖЕНИЕ", то файл "drawing...xml" не формируется.
   
    ТаблицаIMAGE = Новый ТаблицаЗначений;
    ТаблицаIMAGE.Колонки.Добавить("ID");
    ТаблицаIMAGE.Колонки.Добавить("rID");
    ТаблицаIMAGE.Колонки.Добавить("NAME");
    ТаблицаIMAGE.Колонки.Добавить("ROW");
    ТаблицаIMAGE.Колонки.Добавить("COL");
    ТаблицаIMAGE.Колонки.Добавить("FILE");
   
    Файл = Новый Файл(ZIPКаталог + "XL\WorkSheets\_rels\sheet" + ФайлНомерЛиста + ".xml.rels");
    Если НЕ Файл.Существует() Тогда
        Возврат Новый ТаблицаЗначений;
    КонецЕсли;
   
    ДокументRELS = ПолучитьДокументDOM_NativeXLSX(Файл.ПолноеИмя);
   
    Попытка
        ЭлементыRELS = ДокументRELS.ПолучитьЭлементыПоИмени("Relationships");
    Исключение
        Сообщить(ОписаниеОшибки());
        Возврат Новый ТаблицаЗначений;
    КонецПопытки;
   
    НомерDRAWING = "/Неопределено/";
    Если ЭлементыRELS.Количество() > 0 Тогда
        Элемент1 = ЭлементыRELS[0].ПервыйДочерний;
        НомерDRAWING = ЗначениеАтрибутаУзлаDOM(Элемент1, "Target");    // ="../drawings/drawing2.xml".
        Если Найти(ВРег(НомерDRAWING), ".XML") > 0 Тогда
            НомерDRAWING = СтрЗаменить(НомерDRAWING, "../drawings/drawing", "");
            НомерDRAWING = СтрЗаменить(НомерDRAWING, ".xml", "");
            Попытка
                НомерDRAWING = Число(НомерDRAWING);
            Исключение
                Сообщить("Не удалось получить номер файла Drawing.xml");
            КонецПопытки;
        КонецЕсли;
    КонецЕсли;
   
    Файл = Новый Файл(ZIPКаталог + "XL\Drawings\Drawing" + НомерDRAWING + ".xml");
    Если НЕ Файл.Существует() Тогда
        Возврат Новый ТаблицаЗначений;
    КонецЕсли;
   
    ДокументDRAWING = ПолучитьДокументDOM_NativeXLSX(Файл.ПолноеИмя);
   
    Попытка
        ЭлементыDRAWING = ДокументDRAWING.ПолучитьЭлементыПоИмени("wsDr");
    Исключение
        Сообщить(ОписаниеОшибки());
        Возврат Новый ТаблицаЗначений;
    КонецПопытки;
   
    Если ЭлементыDRAWING.Количество() > 0 Тогда
        ЭлементыКоллекции1 = ЭлементыDRAWING[0].ДочерниеУзлы;
        Для Каждого Элемент1 ИЗ ЭлементыКоллекции1 Цикл
            НоваяСтрока = ТаблицаIMAGE.Добавить();
            ЭлементыКоллекции2 = Элемент1.ДочерниеУзлы;
            Для Каждого Элемент2 ИЗ ЭлементыКоллекции2 Цикл
                ЭлементыКоллекции3 = Элемент2.ДочерниеУзлы;
                Для Каждого Элемент3 ИЗ ЭлементыКоллекции3 Цикл
                    Если Элемент2.ИмяУзла = "xdr:from" Тогда
                        Если Элемент3.ИмяУзла = "xdr:row" Тогда
                            НоваяСтрока.ROW = Число(Элемент3.ТекстовоеСодержимое)+1;
                        КонецЕсли;
                        Если Элемент3.ИмяУзла = "xdr:col" Тогда
                            НоваяСтрока.COL = Число(Элемент3.ТекстовоеСодержимое)+1;
                        КонецЕсли;
                    КонецЕсли;
                    Если Элемент2.ИмяУзла = "xdr:pic" Тогда
                        Если Элемент3.ИмяУзла = "xdr:nvPicPr" И Элемент3.ПервыйДочерний.ИмяУзла = "xdr:cNvPr" Тогда
                            НоваяСтрока.ID = ЗначениеАтрибутаУзлаDOM(Элемент3.ПервыйДочерний, "id");
                            НоваяСтрока.NAME = ЗначениеАтрибутаУзлаDOM(Элемент3.ПервыйДочерний, "name");
                        КонецЕсли;
                        Если Элемент3.ИмяУзла = "xdr:blipFill" И Элемент3.ПервыйДочерний.ИмяУзла = "a:blip" Тогда
                            НоваяСтрока.rID = ЗначениеАтрибутаУзлаDOM(Элемент3.ПервыйДочерний, "r:embed");
                        КонецЕсли;
                    КонецЕсли;
                КонецЦикла;
            КонецЦикла;
        КонецЦикла;
    КонецЕсли;
   
    Файл = Новый Файл(ZIPКаталог + "XL\Drawings\_rels\Drawing" + НомерDRAWING + ".xml.rels");
    Если НЕ Файл.Существует() Тогда
        Возврат Новый ТаблицаЗначений;
    КонецЕсли;
   
    ДокументRELS = ПолучитьДокументDOM_NativeXLSX(Файл.ПолноеИмя);
   
    Попытка
        ЭлементыRELS = ДокументRELS.ПолучитьЭлементыПоИмени("Relationships");
    Исключение
        Сообщить(ОписаниеОшибки());
        Возврат Новый ТаблицаЗначений;
    КонецПопытки;
   
    Если ЭлементыRELS.Количество() > 0 Тогда
        ЭлементыКоллекции1 = ЭлементыRELS[0].ДочерниеУзлы;
        Для Каждого Элемент1 ИЗ ЭлементыКоллекции1 Цикл
            Атрибут = ЗначениеАтрибутаУзлаDOM(Элемент1, "Id");
            ФайлDRAWING = ЗначениеАтрибутаУзлаDOM(Элемент1, "Target");
            СтруктураПараметров = Новый Структура("rID", Атрибут);
            НайденныеСтрокиТЗ = ТаблицаIMAGE.НайтиСтроки(СтруктураПараметров);
            Если Найти(ФайлDRAWING, "../"+КаталогMEDIA+"/") > 0 Тогда
                Для Каждого СтрокаТЗ ИЗ НайденныеСтрокиТЗ Цикл
                    СтрокаТЗ.File = СтрЗаменить(ФайлDRAWING, "../"+КаталогMEDIA+"/", "");
                КонецЦикла;
            Иначе
                Сообщить("Преобразование имени файла не произведено: " + ФайлDRAWING);
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;

    Возврат ТаблицаIMAGE;
   
КонецФункции

&НаСервере
Функция ЭтоЧислоXLSX(Знач ЗначениеЯчейки, Знач ФорматСтиля)
   
    Если ( ВРег(ФорматСтиля) = "GENERAL"
        ИЛИ ВРег(ФорматСтиля) = "STANDARD"
        ИЛИ Найти(ФорматСтиля, "0") > 0 )
        Тогда
        Возврат Истина;
    КонецЕсли;
   
    Возврат Ложь;
   
КонецФункции

&НаСервере
Функция ЭтоПроцентXLSX(Знач ЗначениеЯчейки, Знач ФорматСтиля)
   
    Если ( Найти(ФорматСтиля, "%") > 0 )
        Тогда
        Возврат Истина;
    КонецЕсли;
   
    Возврат Ложь;
   
КонецФункции

&НаСервере
Функция ЭтоБулевоXLSX(ЗначениеЯчейки, ФорматСтиля)
   
    Если (ЗначениеЯчейки = 0 ИЛИ ЗначениеЯчейки = 1)
        И Найти(ВРег(ФорматСтиля), "ИСТИНА") > 0 ИЛИ Найти(ВРег(ФорматСтиля), "ЛОЖЬ") > 0
        ИЛИ Найти(ВРег(ФорматСтиля), "TRUE") > 0 ИЛИ Найти(ВРег(ФорматСтиля), "FALSE") > 0
        Тогда
        Возврат Истина;
    КонецЕсли;
   
    Возврат Ложь;
   
КонецФункции

&НаСервере
Функция ЭтоДатаXLSX(Знач ЗначениеЯчейки, Знач ФорматСтиля)
   
    Если ЗначениеЯчейки = Цел(ЗначениеЯчейки) И
        ( Найти(ФорматСтиля, "DD") > 0
        ИЛИ Найти(ФорматСтиля, "MM") > 0
        ИЛИ Найти(ФорматСтиля, "YY") > 0
        ИЛИ Найти(ФорматСтиля, "QQ") > 0
        ИЛИ Найти(ФорматСтиля, "WW") > 0 )
        Тогда
        Возврат Истина;
    КонецЕсли;
   
    Возврат Ложь;
   
КонецФункции

&НаСервере
Функция ЭтоВремяXLSX(Знач ЗначениеЯчейки, Знач ФорматСтиля)
   
    Если ЗначениеЯчейки < 1 И
        ( Найти(ФорматСтиля, "HH:") > 0
        ИЛИ Найти(ФорматСтиля, "[HH]:") > 0
        ИЛИ Найти(ФорматСтиля, "[H]:") > 0
        ИЛИ Найти(ФорматСтиля, "MM:") > 0
        ИЛИ Найти(ФорматСтиля, ":SS") > 0 )
        Тогда
        Возврат Истина;
    КонецЕсли;
   
    Возврат Ложь;
   
КонецФункции

&НаСервере
Функция КонвертироватьЧислоXLSXвДату(Знач Число)
    Перем Дата1900, Разница, ДатаРезультат;
   
    Дата1900 = Дата("19000101");
   
    Разница = Число - 2;    // EXCEL ошибочно считает 1900-й год високосным.
    Разница = ?(Разница < 0, 0, Разница);
   
    ДатаРезультат = Дата1900 + Разница * 24 * 60 * 60;
   
    Возврат ДатаРезультат;
   
КонецФункции

&НаСервере
Функция КонвертироватьЧислоXLSXвДатуВремя(Знач Число)
    Перем КВоСекунд;
    Перем ВремяРезультат;
   
    // 0,0000115740740740741 = 1 сек.
    // 1                     = 24 часа 00 мин 00 сек.
   
    Если ТипЗнч(Число) = Тип("Число") Тогда
        КВоСекунд = Число * 100000 / 1.15740740740741;
        КВоСекунд = Окр(КВоСекунд);
    Иначе
        Сообщить("Ошибка определения значения типа ""Время"".");
 
   КонецЕсли;
   
    ВремяРезультат = Дата("19000101000000") + КВоСекунд;
   
    Возврат ВремяРезультат;
   
КонецФункции

&НаСервере
Функция ПолучитьЧислоВСтепениИзСтроковогоЗначения(Знач ЗначениеЯчейки)
    Перем Поз1Е, Степень;
    Перем Значение;
   
    Если Найти(ЗначениеЯчейки, "E-") > 0 Тогда
        Поз1Е = Найти(ЗначениеЯчейки, "E-");
        Степень = Сред(ЗначениеЯчейки, Поз1Е+2);
        Значение = Лев(ЗначениеЯчейки, Поз1Е-1);
        Попытка
            Степень = Число(Степень);
            Значение = Число(Значение) / Pow(10, Степень);
        Исключение
            Значение = ЗначениеЯчейки;
        КонецПопытки;
    ИначеЕсли Найти(ЗначениеЯчейки, "E+") > 0 Тогда
        Поз1Е = Найти(ЗначениеЯчейки, "E+");
        Степень = Сред(ЗначениеЯчейки, Поз1Е+2);
        Значение = Лев(ЗначениеЯчейки, Поз1Е-1);
        Попытка
            Степень = Число(Степень);
            Значение = Число(Значение) * Pow(10, Степень);
        Исключение
            Значение = ЗначениеЯчейки;
        КонецПопытки;
    Иначе
        Значение = ЗначениеЯчейки;
    КонецЕсли;
   
    Возврат Значение;
   
КонецФункции

// Выполняет замену символов в строке.
//
// Параметры:
//  ЗаменяемыеСимволы - Строка - строка символов, каждый из которых требует замены;
//  Строка            - Строка - исходная строка, в которой требуется замена символов;
//  СимволыЗамены     - Строка - строка символов, на каждый из которых нужно заменить символы параметра ЗаменяемыеСимволы.
//
//  Возвращаемое значение:
//   Строка - строка после замены символов.
//
//  Примечание: функция предназначена для простых случаев, например, для замены латиницы на похожие кириллические символы.
//              Функция не анализирует повторную замену символов, поэтому такой вызов:
//               ЗаменитьОдниСимволыДругими("кр", "карета", "гз") вернёт слово "газета", а
//               ЗаменитьОдниСимволыДругими("кр", "карета", "рк") не вернёт слово "ракета".
//
&НаСервере
Функция ЗаменитьОдниСимволыДругими(ЗаменяемыеСимволы, Строка, СимволыЗамены)
    Перем Результат, НомерСимвола;
   
    Результат = Строка;
   
    Для НомерСимвола = 1 ПО СтрДлина(ЗаменяемыеСимволы) Цикл
        Результат = СтрЗаменить(Результат, Сред(ЗаменяемыеСимволы, НомерСимвола, 1), Сред(СимволыЗамены, НомерСимвола, 1));
    КонецЦикла;
   
    Возврат Результат;
   
КонецФункции

// Функция "расщепляет" строку на подстроки, используя заданный
//      разделитель. Разделитель может иметь любую длину.
//      Если в качестве разделителя задан пробел, рядом стоящие пробелы
//      считаются одним разделителем, а ведущие и хвостовые пробелы параметра Стр
//      игнорируются.
//      Например,
//      РазложитьСтрокуВМассивПодстрок(",один,,,два", ",") возвратит массив значений из пяти элементов,
//      три из которых - пустые строки, а
//      РазложитьСтрокуВМассивПодстрок(" один   два", " ") возвратит массив значений из двух элементов
//
//  Параметры:
//      Стр -           строка, которую необходимо разложить на подстроки.
//                      Параметр передается по значению.
//      Разделитель -   строка-разделитель, по умолчанию - запятая.
//
//  Возвращаемое значение:
//      массив значений, элементы которого - подстроки
//
&НаСервере
Функция РазложитьСтрокуВМассивПодстрок(Знач Стр, Разделитель = ",")
   
    МассивСтрок = Новый Массив();
    Если Разделитель = " " Тогда
        Стр = СокрЛП(Стр);
        Пока 1 = 1 Цикл
            Поз = Найти(Стр, Разделитель);
            Если Поз = 0 Тогда
                МассивСтрок.Добавить(СокрЛП(Стр));
                Возврат МассивСтрок;
            КонецЕсли;
            МассивСтрок.Добавить(СокрЛП(Лев(Стр, Поз - 1)));
            Стр = СокрЛ(Сред(Стр, Поз));
        КонецЦикла;
    Иначе
        ДлинаРазделителя = СтрДлина(Разделитель);
        Пока 1 = 1 Цикл
            Поз = Найти(Стр, Разделитель);
            Если Поз = 0 Тогда
                Если (СокрЛП(Стр) <> "") Тогда
                    МассивСтрок.Добавить(СокрЛП(Стр));
                КонецЕсли;
                Возврат МассивСтрок;
            КонецЕсли;
            МассивСтрок.Добавить(СокрЛП(Лев(Стр,Поз - 1)));
            Стр = Сред(Стр, Поз + ДлинаРазделителя);
        КонецЦикла;
    КонецЕсли;
   
КонецФункции

&НаСервере
Процедура УдалитьКолонкиСНулевойШириной(ТаблицаРезультат)
    Перем МассивПустыхКолонок;
   
    // Найдем пустые колонки.
    МассивПустыхКолонок = Новый Массив;
    Для Каждого Колонка ИЗ ТаблицаРезультат.Колонки Цикл
        Если Колонка.Ширина = 0 Тогда
            МассивПустыхКолонок.Добавить(Колонка.Имя);
        КонецЕсли;
    КонецЦикла;
   
    // Удалим пустые колонки.
    Для Каждого ПустаяКолонка ИЗ МассивПустыхКолонок Цикл
        ТаблицаРезультат.Колонки.Удалить(ПустаяКолонка);
    КонецЦикла;

КонецПроцедуры


МА! С уважением к сообществу МА!

EXCEL XML Загрузка Импорт ЧтениеXML ПостоительDOM

См. также

Загрузка номенклатуры из Excel в УТ11, КА 2, ERP 2, Розница 2. Дополнительные реквизиты и сведения, характеристики, картинки, цены, остатки

Загрузка и выгрузка в Excel Розничная торговля Логистика, склад и ТМЦ Ценообразование, анализ цен Прайсы Платформа 1С v8.3 1С:Комплексная автоматизация 1.х 1С:Розница 2 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Управленческий учет Платные (руб)

Загрузка из файлов xls, xlsx, ods, csv, mxl в УТ11, КА 2, ERP 2, Розница 2. Задействованы все возможности конфигурации - заполнение реквизитов номенклатуры, дополнительных реквизитов и сведений, характеристики, доп.реквизиты и сведения характеристик. Дополнительные обработки для расширения возможностей.

10560 руб.

29.10.2014    209813    620    524    

438

Загрузка номенклатуры c картинками (несколько потоков одновременно) и сопутствующими данными в базу и любые документы из yml, xls, xlsx, xlsm, ods, ots, csv для УТ 10.3, УТ 11 (все), БП 3, КА 2, ERP 2, УНФ 1.6/3.0, Розница 2

Загрузка и выгрузка в Excel Логистика, склад и ТМЦ Ценообразование, анализ цен Файловый обмен (TXT, XML, DBF), FTP Платформа 1С v8.3 1С:Бухгалтерия 2.0 1С:Управление торговлей 10 1С:Розница 2 1С:Управление нашей фирмой 1.6 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Платные (руб)

Эволюция не стоит на месте - новая удобная версия функциональной обработки для Вашего бизнеса! Что же Вы получаете? Удобный и интуитивно понятный интерфейс с 3-мя этапами работы. 2 режима - автоматический и ручной. Чтение XLSX, XLSM, CSV, XML/YML форматов без офиса, на любом сервере! Визуальное связывание колонок файла и реквизитов простым перетаскиванием колонок. Создание или обновление номенклатуры с иерархией, характеристик, доп. реквизитов, упаковок, загрузка практически неограниченного количества картинок на одну номенклатуру (с возможностью загрузки в несколько потоков одновременно), с хранением в томах или в базе. Загрузка номенклатуры поставщиков или поиск по их данным номенклатуры. Загрузка доп. реквизитов в характеристики. Загрузка штрихкодов с генерацией новых. Создание элементов справочников и ПВХ "на лету" для выбранных реквизитов. (Обновление от 11.12.2023, версия 9.5 - 9.9)

13200 руб.

20.11.2015    150379    365    375    

499

Маркетплейсный загрузчик для 12-ти маркетплейсов в "БП 3", "УТ 11", "КА 2", ERP, УНФ

Загрузка и выгрузка в Excel Маркетплейсы Платформа 1С v8.3 Бухгалтерский учет 1С:Розница 2 1С:Управление нашей фирмой 1.6 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Бухгалтерский учет Управленческий учет Платные (руб)

Реальный помощник, с помощью которого Вы сделаете необходимые документы для Wildberries, OZON, ЯндексМаркет, Мегамаркет, Aliexpress, "Детский мир", Казань-Экспресс, "Леруа Мерлен", ЭНФАНТА (Акушерство), ЛаМода, Летуаль, "Твой дом" в документы "Отчет комиссионера (агента) о продажах" и другие, может работать в "Бухгалтерия 3", "Бухгалтерия 3 КОРП", УТ 11, УНФ, КА 2, ERP. Возможность подключить любые маркетплейсы. Анализ продаж ОЗОН. 30 дней БЕСПЛАТНОГО пользования!

1800 руб.

12.08.2021    31793    234    63    

117

Распознавание и загрузка сканов в 1С "одним нажатием": УПД, ТОРГ-12, накладные, счета, номенклатура, заказы и т.д.

Загрузка и выгрузка в Excel Документооборот и делопроизводство (СЭД) Учет документов Распознавание документов и образов Управляемые формы 1С:Комплексная автоматизация 1.х 1С:Бухгалтерия 2.0 1С:Управление торговлей 10 1С:Розница 2 1С:Управление производственным предприятием 1С:Управление нашей фирмой 1.6 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Управление холдингом 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Бухгалтерский учет Управленческий учет Платные (руб)

Универсальная программа для распознавания ЛЮБЫХ СКАНОВ ИЛИ ФОТО ТОВАРНЫХ ДОКУМЕНТОВ в 1С. Не требует указания параметров и предварительной настройки. Просто выбираете файл (PDF, JPG, DOC, XLS, HTML и т.д.) выбираете документ 1С и нажимаете кнопку "Распознать и загрузить".

5400 руб.

04.06.2019    101126    296    173    

312

Загрузка данных отчета о реализации товаров из Excel файла СберМегаМаркет

Загрузка и выгрузка в Excel Маркетплейсы Платформа 1С v8.3 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Управление нашей фирмой 3.0 Бухгалтерский учет Управленческий учет Платные (руб)

Загрузка данных отчета о реализации товаров из сервиса СберМегаМаркет для конфигурации: Бухгалтерия предприятия, редакция 3.0; Управление торговлей, редакция 11 и Управление нашей фирмой, редакция 3.0 в документ "Отчет комиссионера (агента) о продажах".

3600 руб.

07.06.2022    14947    78    0    

59

Загрузка документов и номенклатуры из Excel в 1С "одним нажатием": УПД, ТОРГ-12, отчеты маркетплейсов, заказы, счета, прайсы

Загрузка и выгрузка в Excel Оптовая торговля Платформа 1С v8.3 Управляемые формы Платформа 1C v8.2 Конфигурации 1cv8 1С:Комплексная автоматизация 1.х 1С:Бухгалтерия 2.0 1С:Управление торговлей 10 1С:Розница 2 1С:Управление производственным предприятием 1С:Управление нашей фирмой 1.6 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Бухгалтерский учет Управленческий учет Платные (руб)

Универсальная обработка для загрузки документов из Excel в 1С. Не требует указания параметров (номера колонок, номер первой строки таблицы и т.д.) и предварительной настройки. Просто выбираете файл Excel, документ 1С и нажимаете кнопку "Загрузить". Обработка сама находит таблицу в файле Excel, необходимые для загрузки данные в ней (номенклатура, количество, НДС, цена, сумма) и загружает ее в 1С. Вместе с номенклатурой может найти контрагента, номер и дату документа, штрих-коды, серии ГТД, страну и т.д. Распознает документы ЛЮБОЙ ФОРМЫ (УПД, ТОРГ-12, заказ, отчет комиссионера и т.д.). Не требует MS Office. Для поиска таблиц используются методы эвристического поиска. Загружает только то, что нужно, т.е. пропускает повторы шапки таблицы, заголовки, промежуточные итоги, подписи и т.д. Содержит модуль работы с электронной почтой и api-загрузчик отчетов о продажах маркетплейсов.

5000 руб.

09.11.2016    214602    921    886    

938
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. StepByStep 3666 02.09.14 14:53 Сейчас в теме
Чтение файла XLSX средствами 1С. ПостоительDOM
Прикрепленные файлы:
ЗагрузитьМетодом_NativeXLSX_ПостроительDOM.txt
user645801_yyyuuu123q; Alister; RibD; proonec; bendarik; chemezov; Pavel_nv; jaroslav.h; kuzyara; +9 Ответить
2. monkbest 115 17.09.14 14:54 Сейчас в теме
Я извиняюсь, а можно в статью написать текст с описанием. А то кроме цитаты из мстителей слов-то и нет:)

По листнингу понять, что рассказывается в статье - сложно. Скомкали все. Дали ссылки на пять статей, в которых та же каша, процитировали. Кроме заголовка ни одного информативного слова нет.
3. StepByStep 3666 17.09.14 17:34 Сейчас в теме
(2) monkbest,
Добавил небольшое описание.
СПАСИБО.
4. arasn 17.09.14 17:12 Сейчас в теме
5. DragEugen 24.09.14 13:54 Сейчас в теме
если бы было с описанием всех функций то вообще бы цены небыло
6. monkbest 115 24.09.14 14:18 Сейчас в теме
так это же получается клевая штука. Читаем ексель без екселя. очень удобно будет для случаев, когда нет возможности создать ОЛЕ оъект екселя в силу его отсутствия или же недостаточности прав

11. Ivon 673 12.11.14 16:44 Сейчас в теме
(6) monkbest, Читать эксель без экселя можно и с помощью ADO. И быстрее, и код меньше, и читать можно файлы любых версий.
Shmell; ARkiller; +2 Ответить
12. StepByStep 3666 25.11.14 22:18 Сейчас в теме
7. Drizer2000 14 25.09.14 13:29 Сейчас в теме
А этот метод обрабатывает только файлы с расширением XLSX, а обычный xls умеет?
8. awk 741 07.10.14 18:18 Сейчас в теме
(7) Drizer2000, Докумены Microsoft Office с некоторых пор хранятся в формате xml. И сжаты zip.
StepByStep; +1 Ответить
9. awk 741 07.10.14 18:19 Сейчас в теме
(7) Drizer2000, xls хранятся не в zip/xml.
10. hazd 12.10.14 01:07 Сейчас в теме
интересный подход, но как-то все сложно описано, сохраню в блокнот, время будет, разберу.
13. Soikalv 16.12.14 15:02 Сейчас в теме
Интересно. на досуге разберусь.
Как много нам открытий чудных готовит внедрение новых платформ 1с. Вот перехожу на тройку и радуюсь ища новые обработки для тройки.а так хорошо все было настроено в 8.1. и 8.2. Так и не поняла из статьи, эта обработка - то,что я ищу или нет. В 8.2. была типовая внешняя обработка с диска ИТС для загрузки данных из таблиц икселя в любой табличный документ. Целые базы грузили, очень удобно было..
14. StepByStep 3666 17.12.14 15:48 Сейчас в теме
(13) Soikalv,
Обработка, в которой применяется функционал этой статьи: http://infostart.ru/public/120961/
Обработка построена по-другому, если сравнивать ее с обработкой с ИТС.
15. kuzyara 1896 26.12.14 05:21 Сейчас в теме
При применении на практике мне нужна была просто ТаблицаЗначений, идентичная натуральной, доработал несколько моментов:
Во-первых, убрал разделитель разрядов во всех числах которые приводятся к строке, по-моему здесь типизация - лишнее.
Во-вторых, убрал первые три колонки: "НомерСтроки", "Сопоставлено", "УровеньГруппировки".
В-третьих, МассивИменКолонокXLSX определяется по первой строке, пришлось написать свою функцию ДесятичныйВНомерКолонки(х):
Функция ДесятичныйВНомерКолонки(Знач НомерКолонки)
	Перем Латиница, ДлинаНомера, Поз, ит;
	Латиница = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";	// 26.
             // 1   5    0    5    0    5
	
	целое = НомерКолонки;
	слово="";
	
	Пока Целое>0 Цикл
		НовЦелое=Цел(Целое/26);
		
		остаток = Целое%26;
		
		Буква = Сред(Латиница, остаток, 1);
		слово = Буква+слово;
		Целое=НовЦелое;
	КонецЦикла;	
	
	Возврат слово;
	
КонецФункции
Показать


В целом метод рабочий, автору большая благодарность.
18. StepByStep 3666 14.01.15 10:25 Сейчас в теме
(15) kuzyara,
Удачного применения.
16. Cyberhawk 135 28.12.14 12:17 Сейчас в теме
Подскажите, пожалуйста, можно ли в вашей "подсистеме" (назвал так прилагаемый к публикации набор процедур и функций) узнавать цвет фона ячейки xlsx-файла? Если да, то в какой процедуре / функции из прилагаемых это можно сделать?
17. kuzyara 1896 29.12.14 09:16 Сейчас в теме
(16) Cyberhawk, принципиальной сложности в разборе https://en.wikipedia.org/wiki/Microsoft_Office_XML_formats нет.
19. StepByStep 3666 14.01.15 10:28 Сейчас в теме
(16) Cyberhawk,
При распаковке должен сформироваться файл styles.xml, в котором описаны используемые форматы.
20. pedchenko32 14.01.15 15:41 Сейчас в теме
Добрый день! Почему, если в файле xlsx 1 лист, возникает ошибка файл не обнаружен ...sheet2.xml? Если создать 2й лист, с таким же содержимым, считывание происходит...
23. StepByStep 3666 25.02.15 13:58 Сейчас в теме
(20) pedchenko32, (21) pedchenko32, (22) pedchenko32,
1. "sheet2.xml" - не сталкивался.
2. "ячейка пустая" - ЗначениеЗаполнено(ЗначениеЯчейки).
3. "строки с оформлением" - Конкретно это не интересовало. Если строки пусты - то написать доп процедуру.
21. pedchenko32 14.01.15 17:04 Сейчас в теме
и еще вопрос, как определить в этом случае, что ячейка пустая?
22. pedchenko32 14.01.15 17:22 Сейчас в теме
или как сделать, чтобы строки с оформлением тоже не включались в таблицу значений?
24. Жолтокнижниг 258 17.03.15 12:27 Сейчас в теме
Необходимо поправить загрузку SharedStrings
иногда встречаются подобные конструкции:
<si><r><t>mail@mail.ru</t></r><r><rPr><sz val="10"/><color rgb="FF000000"/><rFont val="Arial"/><family val="2"/><charset val="204"/></rPr><t xml:space="preserve">  </t></r><r><rPr><sz val="10"/><color rgb="FF909090"/><rFont val="Arial"/><family val="2"/><charset val="204"/></rPr><t> </t></r></si>

За счет этого сбивается нумерация.
Если SharedStrings.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда  
МассивSharedStrings.Добавить(Строка);
КонецЕсли;

мое исправление ситуации
25. tyfmyl 21.03.16 14:40 Сейчас в теме
Добавте в параметры функции разрядность округления - Окр(). Excel при чтении данных при выгрузке полей не форматированных представляет некоторые числа в виде бесконечной десятичной дроби с "9" ой на конце.
Из справки известно, что excel хранит числа в 8 байтах. Для округления достаточно 14 цифр разрядов.
26. wtlz 272 09.01.20 21:41 Сейчас в теме
Добрый день! Спасибо большое за качественные методы чтения EXCEL - использовал уже не раз.
Столкнулся с файлом на десятки тысяч строк, проблема на цикле:
Для Каждого Элемент ИЗ ЭлементыКоллекции Цикл
МассивSharedStrings.
Добавить(Элемент.ТекстовоеСодержимое);
КонецЦикла;

может есть идеи, как ускорить чтение значений?
27. SergeyN 1071 26.07.20 11:09 Сейчас в теме
За статью однозначно плюс. Автор, а есть сравнение по времени с другими вариантами чтения в разных видах файлов (много/мало колонок, больше текста или больше чисел и т.д) ?
28. VZhulanov 4 12.12.23 12:50 Сейчас в теме
А чем так интересен именно метод разборки xml через DOM ?
Я попробовал один и тот-же файлик xlsx прочитать и этим алгоритмом и алгоритмом простого разбора XML (по ссылке из этой-же статьи)
Там 12 колонок, 54 тыщи строк, в данных только текст или числа
через DOM ушло времени 40минут, а вторым методом всего 1.5 минуты

А полез в эти статьи только потом что через ТабличныйДокумент этот файлик чаще не читается, просто 1Ска зависает или вылетает, хотя иногда читается
Оставьте свое сообщение