Skip to content

Instantly share code, notes, and snippets.

@b4tman
Created November 28, 2019 13:20
Show Gist options
  • Save b4tman/3a7236d05d241cb65cf8ca71ff174e81 to your computer and use it in GitHub Desktop.
Save b4tman/3a7236d05d241cb65cf8ca71ff174e81 to your computer and use it in GitHub Desktop.
#Область XLSX
// https://msdn.microsoft.com/ru-ru/library/office/documentformat.openxml.spreadsheet.cell.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
// https://infostart.ru/public/665512/
// https://infostart.ru/public/280340/
Процедура XLSX_Распаковать(ИмяФайла, Каталог)
Зип = Новый ЧтениеZipФайла;
Зип.Открыть(ИмяФайла);
Зип.ИзвлечьВсе(Каталог, РежимВосстановленияПутейФайловZIP.Восстанавливать);
КонецПроцедуры
Процедура XLSX_Упаковать(ИмяФайла, Каталог)
ЗаписьZIP = Новый ЗаписьZipФайла();
ЗаписьZIP.Открыть(ИмяФайла);
ЗаписьZIP.Добавить(Каталог+"*", РежимСохраненияПутейZIP.СохранятьОтносительныеПути, РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно);
ЗаписьZIP.Записать();
КонецПроцедуры
Функция XLSX_ПрочитатьДокументDOM(Каталог)
ЧтениеXML = Новый ЧтениеXML;
ПостроительDOM = Новый ПостроительDOM;
ЧтениеXML.ОткрытьФайл(Каталог+"xl\worksheets\sheet1.xml");
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
ЧтениеXML.Закрыть();
Возврат ДокументDOM;
КонецФункции
Процедура XLSX_ЗаписатьДокументDOM(Каталог, ДокументDOM)
ЗаписьXML = Новый ЗаписьXML;
ЗаписьDOM = Новый ЗаписьDOM;
Парам_XML = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Ложь, Ложь);
ЗаписьXML.ОткрытьФайл(Каталог+"xl\worksheets\sheet1.xml", Парам_XML, Ложь);
ЗаписьDOM.Записать(ДокументDOM, ЗаписьXML);
ЗаписьXML.Закрыть();
КонецПроцедуры
Процедура XLSX_УстановитьЗначениеЯчейки(ДокументDOM, Разыменователь, КодЯчейки, Знач ЗначениеЯчейки, Знач ТипЗначения="")
Если ПустаяСтрока(ТипЗначения) Тогда
Если Тип("Число") = ТипЗнч(ЗначениеЯчейки) Тогда
ТипЗначения = "Число";
ИначеЕсли Тип("Строка") = ТипЗнч(ЗначениеЯчейки) Тогда
ТипЗначения = "Строка";
Иначе
ТипЗначения = "Строка";
ЗначениеЯчейки = Строка(ЗначениеЯчейки);
КонецЕсли;
КонецЕсли;
ПоискЯчейки = ДокументDOM.ВычислитьВыражениеXPath(СтрШаблон("//*[@r='%1']", КодЯчейки), ДокументDOM.ЭлементДокумента, Разыменователь);
УзелЯчейки = ПоискЯчейки.ПолучитьСледующий();
Если Неопределено = УзелЯчейки Тогда
УзелЯчейки = XLSX_ДобавитьЯчейку(ДокументDOM, Разыменователь, КодЯчейки);
КонецЕсли;
Если Неопределено = УзелЯчейки Тогда
Лог(СтрШаблон("Ошибка заполнения ячейки: %1", КодЯчейки));
Иначе
Для Каждого ДочернийУзел Из УзелЯчейки.ДочерниеУзлы Цикл
УзелЯчейки.УдалитьДочерний(ДочернийУзел);
КонецЦикла;
Если "Строка" = ТипЗначения Тогда
УзелЯчейки.УстановитьАтрибут("t", "inlineStr");
Узел_is = ДокументDOM.СоздатьЭлемент("is");
Узел_t = ДокументDOM.СоздатьЭлемент("t");
ЗначениеУзла = ДокументDOM.СоздатьТекстовыйУзел(ЗначениеЯчейки);
Узел_t.ДобавитьДочерний(ЗначениеУзла);
Узел_is.ДобавитьДочерний(Узел_t);
УзелЯчейки.ДобавитьДочерний(Узел_is);
ИначеЕсли "Число" = ТипЗначения Тогда
УзелЯчейки.УстановитьАтрибут("t", "n");
Узел_v = ДокументDOM.СоздатьЭлемент("v");
ЗначениеУзла = ДокументDOM.СоздатьТекстовыйУзел(Формат(ЗначениеЯчейки, "ЧРД=.; ЧГ=0"));
Узел_v.ДобавитьДочерний(ЗначениеУзла);
УзелЯчейки.ДобавитьДочерний(Узел_v);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Функция XLSX_МестоДляВставкиЯчейки(ДокументDOM, Разыменователь, НомерСтолбца, НомерСтроки, УзелСтроки)
// поиск узла перед которым нужно вставить узел ячейки (например L3 нужно вставить перед М3 если он есть, или перед N3,... или в конец)
// FIXME: возможно, не самое оптимальное решение (поиск по перебору колонок перед нужной)
СледЯчейка = Неопределено;
Если УзелСтроки.ЕстьДочерниеУзлы() Тогда
ТекНомерК = НомерСтолбца - 1;
Пока ТекНомерК > 0 Цикл
ТекКод = XLSX_КодСтолбцаExcel(ТекНомерК) + НомерСтроки;
ПоискУзла = ДокументDOM.ВычислитьВыражениеXPath(СтрШаблон("//*[@r='%1']", ТекКод), УзелСтроки, Разыменователь);
ПредЯчейка = ПоискУзла.ПолучитьСледующий();
Если НЕ Неопределено = ПредЯчейка Тогда
СледЯчейка = ПредЯчейка.СледующийСоседний;
Прервать;
КонецЕсли;
ТекНомерК = ТекНомерК - 1;
КонецЦикла;
// если не нашли след. ячейку по предыдущим, то проверим
// возможно у первого узла больший номер колонки чем у новой ячейки
// например если первый элемент D1 а новая ячейка B1, то след.ячейка = первая
Если Неопределено = СледЯчейка Тогда
ПервыйУзел = УзелСтроки.ПервыйДочерний();
ТекКод = ПервыйУзел.ПолучитьАтрибут("r");
ТекАдр = XLSX_ПолучитьАдресЯчейки(ТекКод);
ТекНомерК = XLSX_НомерСтолбцаExcel(ТекАдр.КодСтолбца);
Если ТекНомерК > НомерСтолбца Тогда
СледЯчейка = ПервыйУзел;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Возврат СледЯчейка;
КонецФункции
Функция XLSX_ДобавитьЯчейку(ДокументDOM, Разыменователь, КодЯчейки)
Результат = Неопределено;
// разбиение кода ячейки на столбец и строку
АдресЯчейки = XLSX_ПолучитьАдресЯчейки(КодЯчейки);
НомерСтолбца = XLSX_НомерСтолбцаExcel(АдресЯчейки.КодСтолбца);
// поиск строки / <row ...
ПоискСтроки = ДокументDOM.ВычислитьВыражениеXPath(СтрШаблон("//*[@r='%1']", АдресЯчейки.НомерСтроки), ДокументDOM.ЭлементДокумента, Разыменователь);
УзелСтроки = ПоискСтроки.ПолучитьСледующий();
// нет такой строки
// TODO: добавить строку? (надо также менять <dimension ref="A1:R4"/>)
Если Неопределено = УзелСтроки Тогда
Лог(СтрШаблон("Ошибка: не найдена строка файла - %1", АдресЯчейки.НомерСтроки));
Возврат Результат;
КонецЕсли;
// TODO: добавить проверку наличия колонки и добавление её (надо также менять <dimension ref="A1:R4"/> и <cols>)
// создание узла / <c r="G1" s="2" t="s">
УзелЯчейки = ДокументDOM.СоздатьЭлемент("c");
УзелЯчейки.УстановитьАтрибут("r", КодЯчейки);
СледЯчейка = XLSX_МестоДляВставкиЯчейки(ДокументDOM, Разыменователь, НомерСтолбца, АдресЯчейки.НомерСтроки, УзелСтроки);
УзелСтроки.ВставитьПеред(УзелЯчейки, СледЯчейка);
Результат = УзелЯчейки;
Возврат Результат;
КонецФункции
Функция XLSX_ПолучитьАдресЯчейки(КодЯчейки)
Результат = Новый Структура("КодСтолбца,НомерСтроки", "", 0);
ПозицияНомераСтроки = 0;
Если 2 > СтрДлина(КодЯчейки) Тогда Возврат Результат; КонецЕсли;
Для ТекПозиция=1 По СтрДлина(КодЯчейки) Цикл
ТекСимвол = Сред(КодЯчейки, ТекПозиция, 1);
Если НЕ 0 = СтрНайти("0123456789", ТекСимвол) Тогда
ПозицияНомераСтроки = ТекПозиция;
Прервать;
КонецЕсли;
КонецЦикла;
Если НЕ 0 = ПозицияНомераСтроки Тогда
Результат.КодСтолбца = Лев(КодЯчейки, ПозицияНомераСтроки - 1);
Результат.НомерСтроки = Прав(КодЯчейки, 1 + СтрДлина(КодЯчейки) - ПозицияНомераСтроки);
Результат.НомерСтроки = Число(Результат.НомерСтроки);
КонецЕсли;
Возврат Результат;
КонецФункции
Функция XLSX_КодСтолбцаExcel(Знач Номер)
// формирует буквенный код столбца Excel, например: 1 - A, 2 - B, 27 - AA, 703 - AAA, ...
Результат = "";
КолвоАлфавит = 1 + КодСимвола("Z") - КодСимвола("A"); // 26
СмещениеСимвола = КодСимвола("A") - 1; // 64
Пока Номер > 0 Цикл
Если Номер <= КолвоАлфавит Тогда
Результат = Символ(СмещениеСимвола + Номер) + Результат;
Номер = 0;
Иначе
ТекСимвол = ((Номер - 1) % КолвоАлфавит) + 1;
Номер = Цел((Номер - 1) / КолвоАлфавит);
Результат = Символ(СмещениеСимвола + ТекСимвол) + Результат;
КонецЕсли;
КонецЦикла;
Возврат Результат;
Конецфункции
Функция XLSX_НомерСтолбцаExcel(Знач БуквКод)
// формирует номер столбца Excel по буквенному коду, например: 1 - A, 2 - B, 27 - AA, 703 - AAA, ...
Результат = 0;
КолвоАлфавит = 1 + КодСимвола("Z") - КодСимвола("A"); // 26
СмещениеСимвола = КодСимвола("A") - 1; // 64
Множитель = 1;
ДлиннаКода = СтрДлина(БуквКод);
Пока ДлиннаКода > 0 Цикл
ТекСимвол = Прав(БуквКод, 1);
Результат = Результат + Множитель * (КодСимвола(ТекСимвол) - СмещениеСимвола);
ДлиннаКода = ДлиннаКода - 1;
Если 0 = ДлиннаКода Тогда
БуквКод = "";
Иначе
БуквКод = Лев(БуквКод, ДлиннаКода);
КонецЕсли;
Множитель = Множитель * КолвоАлфавит;
КонецЦикла;
Возврат Результат;
Конецфункции
//XLSX
#КонецОбласти
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment