1С, Linux, Excel, Word, OpenXML і Net Core

Доброго часу доби хабратчане! Сьогодні я продовжу мучити вас великим і могутнім Руслишем. Це продовження статей:

» Розробка → Багатоплатформовий використання класів .Net з некерованого коду. Або аналог IDispatch на Linux
» Розробка → Багатоплатформовий використання класів .Net в 1С через Native ВК. Або заміна COM на Linux
» Багатоплатформовий використання класів .Net в 1С через Native ВК. Або заміна COM на Linux II
» Асинхронне програмування в 1С через .Net Native ВК

З того часу я додав використання розширень Linq. У цій статті я торкнуся практичного використання моєї компоненти. А саме кроссплатформної роботою з файлами Excel і Word c допомогою OpenXML і NetStandart.

Власне заради чого ця розробка і задумувалася. Основу були взяті звідси. На жаль без Nuget підключити бібліотеку до проекту не можна. Але через CoreClr його можна підключити. Довідкову інформацію по роботі з OpenXML можна подивитися тут.
Як мені… (Open XML SDK)

Отже почнемо з читання сторінок Excel. Завдання перетворити дані в ТаблицуЗначений.

Процедура ПрочитатьExcel(Файл)

// Завантажимо збірку 
СборкаOpenXml=ъ(Врап.Збірка("DocumentFormat.OpenXml.dll"));

// Отримаємо типи
SpreadsheetDocument=ъ(СборкаOpenXml.GetType("DocumentFormat.OpenXml.Packaging.SpreadsheetDocument"));
SharedStringTablePart=ъ(СборкаOpenXml.GetType("DocumentFormat.OpenXml.Packaging.SharedStringTablePart"));

CellValues=ъ(СборкаOpenXml.GetType("DocumentFormat.OpenXml.Spreadsheet.CellValues"));

SharedString=ъ(CellValues.SharedString);
Cell=ъ(СборкаOpenXml.GetType("DocumentFormat.OpenXml.Spreadsheet.Cell"));
Sheet=ъ(СборкаOpenXml.GetType("DocumentFormat.OpenXml.Spreadsheet.Sheet"));
Regex=ъТип("System.Text.RegularExpressions.Regex","System.Text.RegularExpressions",истина);

// Ось чого не вистачає в 1С так це Regex
ШаблонКолонки=ъ(Врап.Новий(Regex.ПолучитьСсылку(),"[A-Za-z]+"));
ШаблонСтроки=ъ(Врап.Новий(Regex.ПолучитьСсылку(),"\d+"));

// Відкриємо файл Excel
doc = ъ(SpreadsheetDocument.Open(Файл, false));
workbookPart = ъ(doc.WorkbookPart);

// Рядка зберігаються окремо
// В комірках зберігається індекс
pt=ъ(ъ(workbookPart.in(SharedStringTablePart.ПолучитьСсылку())).GetPartsOfType());
sstpart = ъ(pt.First());
sst = ъ(sstpart.SharedStringTable);
ОбщиеСтроки=ъ(sst.ChildElements);

workbook = ъ(workbookPart.Workbook);

// Отримаємо список сторінок
sheets = ъ(ъ(workbook.in(Sheet.ПолучитьСсылку())).Descendants());
sheets=ъ(Врап.ПолучитьЭнумератор(sheets.ПолучитьСсылку()));


Поки sheets.MoveNext() Цикл
sheet= ъ(sheets.Current);
id=ъ(sheet.Id).Value;
ИмяСтраницы=ъ(sheet.Name).Value;
Повідомити(ИмяСтраницы);
worksheetPart = ъ(workbookPart.GetPartById(id));

Worksheet=ъ(worksheetPart.Worksheet);
// Так як дані зберігаються тільки ті які мають значення 
// Отримаємо тз з колонками ИмяЯчейки,ИмяКолонки,НомСтроки,ЗначениеЯчейки
//Де ИмяЯчейки= A1, ИмяКолонки=A,НомСтроки=1;
Тз=ЗаписатьСтраницуВТЗ(Worksheet,ОбщиеСтроки);
// Можна цю тз проіндексувати і отримувати доступ до клітинок
// через Знайти використовуючи ИмяЯчейки або
// НайтиСтроки використовуючи ИмяКолонки і НомСтроки

// Отримаємо таблицю з колонками A,B,D..AA,AB..
Тз=ПреобразоватьВТаблицу(Тз);
Тз.ВыбратьСтроку(ИмяСтраницы);

КонецЦикла

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

Розглянемо більш докладно методи для отримання даних про комірках таблиці

Функція ИмяКолонки(ИмяЯчейки)
//Отримаємо Ім'я колнки осередку
match = ъ(ШаблонКолонки.Match(ИмяЯчейки));
return match.Value;

КонецФункции

Функція НомерСтроки(ИмяЯчейки)
// Отримаємо Номер комірки рядка
match = ъ(ШаблонСтроки.Match(ИмяЯчейки));
return число(match.Value);
КонецФункции

Процедура ЗаписатьДанныеЯчейки(value Тз,value Осередок,value ОбщиеСтроки)
Змінн ТипДанных;

// Отримаємо адреса клітинки і Тип даних
адреса=ъ(Комірка.CellReference).InnerText;
ТипДанных=Осередок.DataType;
ФлЕстьДанные=false; 
Якщо ТипДанных<> null Тоді
ТипДанных=ъ(ъ(ТипДанных).Value);
// Перевіримо тип даних Рядок
Якщо ТипДанных.Equals(SharedString.ПолучитьСсылку()) Тоді
//В CellValue зберігається индек рядки в таблиці загальних рядків
CellValue=ъ(комірка.CellValue);
Text=CellValue.Text;
ssid = Число(Text);
ChildElement=ъ(ОбщиеСтроки.get_Item(ssid));
ЗначениеЯчейки = ChildElement.InnerText;
ФлЕстьДанные=true;
КонецЕсли;
КонецЕсли;

// Якщо це не рядок, то отримаємо, то отримаємо значення з CellValue
Якщо не ФлЕстьДанные Тоді
ЗначениеЯчейки= осередок.CellValue;
Якщо (ЗначениеЯчейки<> null) Тоді
ЗначениеЯчейки=ъ(ЗначениеЯчейки);
ЗначениеЯчейки=ЗначениеЯчейки.Text;
ФлЕстьДанные=true;
КонецЕсли;

КонецЕсли;

Якщо ФлЕстьДанные Тоді
Стр=Тз.Додати();
Стр.ИмяЯчейки=адреса;
Стр.ИмяКолонки=ИмяКолонки(адресу);
Стр.НомСтроки =НомерСтроки(адресу);
Стр.ЗначениеЯчейки=ЗначениеЯчейки;
КонецЕсли;

КонецПроцедуры
Функція СоздатьТз()
// Так як дані зберігаються у вигляді таблиці
// То створимо ТЗ з даними по осередкам
ТипЧисла = Новий ОписаниеТипов("Число",Новий КвалификаторыЧисла(10,0,ДопустимыйЗнак.Неотрицательный)); 
Тз=новий ТаблицаЗначений;
Колонки=Тз.Колонки;
Колонки.Додати("ИмяЯчейки",ОписаниеСтроки());
Колонки.Додати("ИмяКолонки",ОписаниеСтроки());
Колонки.Додати("НомСтроки",ТипЧисла);
Колонки.Додати("ЗначениеЯчейки",ОписаниеСтроки());

повернення тз;
КонецФункции

Функція ЗаписатьСтраницуВТЗ(value sheet,Value ОбщиеСтроки)

// Прочитаємо всі комірки сторінки
cells = ъ(ъ(sheet.in(Cell.ПолучитьСсылку())).Descendants());
cells=ъ(Врап.ПолучитьЭнумератор(cells.ПолучитьСсылку()));

Тз= СоздатьТз();
// Запишемо дані комірки в ТЗ
Поки cells.MoveNext() Цикл
Осередок=ъ(cells.Current);
ЗаписатьДанныеЯчейки(Тз,Клітинка,ОбщиеСтроки) 
КонецЦикла;

повернення Тз;
КонецФункции

Тепер нам потрібно перетворити ТЗ з даними про клітинках в Таблицю Здначений аналогічної Сторінці Excel

Функція ЗаписатьКолонки(Колонки,НачСтр,Розряд,КоличествоРазрядов,Порівнювати,ПоследняяКолонка)
// Процедура створює колонки які менші або дорівнюють імені останньої колоки
// A,B,..,AA..ABC
Для сч=КодСимвола("A") по КодСимвола("Z") Цикл
НовСтр=НачСтр+Символ(сч);

Якщо Розряд<КоличествоРазрядов Тоді
рез= ЗаписатьКолонки(Колонки,НовСтр,Розряд+1,КоличествоРазрядов,Порівнювати,ПоследняяКолонка);
Якщо Порівнювати і Рез Тоді
повернення істина
КонецЕсли; 
Інакше
Колонки.Додати(НовСтр,ОписаниеСтроки());

Якщо Порівнювати і НовСтр=ПоследняяКолонка Тоді
повернення істина
КонецЕсли
КонецЕсли;
КонецЦикла;


повернення брехня;
КонецФункции

Процедура СоздатьКолонки(Колонки,ПоследняяКолонка)

// Створимо колонки враховуючи розряди
// Наприклад, якщо ім'я останньої колоки ABC то колонки йдуть по розрядно
//A..Z
//AA..ZZ
//AAA..ABC
КоличествоРазрядов=СтрДлина(ПоследняяКолонка);

Для сч=1 За Цикл КоличествоРазрядов
Порівнювати=сч=КоличествоРазрядов;
рез= ЗаписатьКолонки(Колонки,"",1,сч,Порівнювати,ПоследняяКолонка);
Якщо Порівнювати і рез Тоді
повернення;
КонецЕсли; 
КонецЦикла; 
КонецПроцедуры

Функція НайтиИмяПоследнейКолонки(Тз)
рез="";
ДлинаРез=0;
Для кожного стрТз з Тз Цикл 
Стр=стрТз.ИмяКолонки;
СтрДл=СтрДлина©;

Якщо СтрДл>ДлинаРез Тоді
ДлинаРез=СтрДл;
рез=Р;
ИначеЕсли СтрДл=ДлинаРез Стор>рез Тоді
рез=Р;
КонецЕсли; 
КонецЦикла;
повернення рез;
КонецФункции

Функція ПреобразоватьВТаблицу(Тз)
рез=новий ТаблицаЗначений;
ПоследняяКолонка=НайтиИмяПоследнейКолонки(Тз);
СоздатьКолонки(рез.Колонки,ПоследняяКолонка);
Колонки=рез.Колонки;
// Часто исползую цю функцію
// Код можна подивитись тут http://infostart.ru/public/371762/
// Згрупуємо дані ТЗ за номером рядка
Тз=глСгруппироватьТзПоПолю(тз,"НомСтроки");
сч=1;

Для кожного стрТз з Тз Цикл 
НомСтроки=стрТз.НомСтроки;
// Рядка додамо номера яких менше НомСтроки
Поки сч<НомСтроки Цикл
сч=сч+1;
рез.Додати();
КонецЦикла;
сч=сч+1;
стр=рез.Додати();
ТзГр=стрТз.ТзПоГруппе;


Для кожного стрТзГр з ТзГр Цикл
// Звичайно, можна отримати індекс знаючи зміщення мисвола 65 і 26 розрядну систему
// але знайдемо колонку по імені та її індекс
Колонка=Колонки.Знайти(стрТзГр.ИмяКолонки);
стор. Встановити(Колонки.Індекс(Колонка),стрТзГр.ЗначениеЯчейки); 
КонецЦикла; 
КонецЦикла;
повернення рез;
КонецФункции

Тепер перейдемо до читання даних файлу Word.

Функція GetPlainText(value Елемент)
ЗаписьXML = Новий ЗаписьXML;
ЗаписьXML.УстановитьСтроку();

// Отримаємо секції і рекурсивно пройдемося по їх значень
Секції=ъ Елемент.Elements()); 

Секції=ъ(Врап.ПолучитьЭнумератор(Секції.ПолучитьСсылку()));

Поки Секції.MoveNext() Цикл
Секція= ъ(Секції.Current);
ИмяСекции=Секція.LocalName;
Якщо ИмяСекции= "t" Тоді
Стр=Секція.InnerText;
ЗаписьXML.ЗаписатьБезОбработки©;

ИначеЕсли ИмяСекции= "cr" або ИмяСекции= "br" Тоді
ЗаписьXML.ЗаписатьБезОбработки(NewLine); 
ИначеЕсли ИмяСекции= "tab" Тоді
ЗаписьXML.ЗаписатьБезОбработки(Таб); 
// Paragraph
ИначеЕсли ИмяСекции= "p" Тоді
ЗаписьXML.ЗаписатьБезОбработки(GetPlainText(Секція));
ЗаписьXML.ЗаписатьБезОбработки(NewLine+NewLine);
Інакше
ЗаписьXML.ЗаписатьБезОбработки(GetPlainText(Секція));
КонецЕсли; 
КонецЦикла;

повернення ЗаписьXML.Закрити();
КонецФункции

Функція ПрочитатьWord(Файл)
СборкаOpenXml=ъ(Врап.Збірка("DocumentFormat.OpenXml.dll"));
WordprocessingDocument=ъ(СборкаOpenXml.GetType("DocumentFormat.OpenXml.Packaging.WordprocessingDocument"));
Пакет = ъ(WordprocessingDocument.Open(Файл, брехня));
Елемент = ъ(ъ(ъ(Пакет.MainDocumentPart).Document).Body);
if (Елемент = null) Тоді
повернення ""
КонецЕсли;

повернення GetPlainText(Елемент);
КонецФункции // 

У своїх статтях я хочу донести насамперед до 1С, що є платформна заміна COM з допомогою NetStadart. Але на мій превеликий жаль, поки даний підхід нікого не цікавить. Привертає увагу лише Руслиш. Якщо у когось будуть ідеї чим можна привернути увагу до заміни COM пишіть. Буду тільки радий.

Приклади і вихідні коди можна скачати тут.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.