Чтение файлов *.xlsx средствами 1С 8.2


Формат файлов *.xlsx представляет собой фактически ZIP-архив, внутри которого находятся xml-файлы, что позволяет считывать значения ячеек исключительно средствами 1С 8.2

Внутри архива *.xlsx практический интерес для получения значений ячеек представляют три вида файлов:

1. workbook.xml — содержит описание рабочей книги (количество листов)

2. sharedStrings.xml — содержит строковые значения ячеек всех листов

3. Файлы sheet1.xml…sheetN.xml — значения ячеек конкретного (по номеру, независимо от названия) листа.

Почти все значения внутри файлов sheet1.xml…sheetN.xml представляют собой числа, причем для строчных значений это индекс строки из файла sharedStrings.xml, а для даты — число дней с начала XX века.

Теги   f содержат написание формулы, теги v  — значение ячейки, независимо от того было ли оно «вбито» напрямую, или это результат формулы.

Данная обработка исключительно средствами 1С 8.2 производит распаковку архива *.xlsx и парсинг вышеуказанных файлов.

 

14.06.12

Заодно уж добавил и docxreader

43 Comments

  1. anton.fly7

    а формулы считает?

    Reply ↓
  2. Abadonna

    (1) anton.fly7,

    формулы без проблем, только я не посчитал нужным их сами выводить, всё равно ж, в основном, значения нужны.

    <c r=»B4″>

    <f>SUM(B1:B3)</f>

    <v>10920</v>

    </c>

    Тег f — формула, тег v — значение. Лично меня сама формула не интересует

    Reply ↓
  3. andrewks

    ага, сам примерно такое делал, только для ods (OpenOffice) — там тоже зип-архив, а внутриях хмл, который также можно распарсить

    Reply ↓
  4. serega3333

    зачетно, для формул нужно очень

    Reply ↓
  5. CheBurator

    Автор, пиши ещё!

    Reply ↓
  6. CaSH_2004

    Любопытно а какая скорость то чтения, есть какое-то сравнение? Например чтением через тот-же КОМ-Эксель?

    Reply ↓
  7. andrewks

    (6) по сравнению с КОМ-Эксель, думаю, на порядок

    Reply ↓
  8. cool.vlad4

    А чем отличается от http://infostart.ru/public/19139/ ?

    Reply ↓
  9. Abadonna

    (8) кхм… наверное тем, что я не мониторю все обработки :)))

    В смысле, мы с Василием всегда плюсы друг-другу ставили, но никогда даже и не смотрели, что там за разработки 😉

    Эту либо вообще не видел, либо в упор не помню

    Reply ↓
  10. Abadonna

    (6) CaSH_2004,

    Проверял на матрице 10 000 строк х 5 колонок, ровно 7 секунд на моем рабочем

    (Intel® Core™2 Duo CPU E7500 2.93GHz, ОЗУ 2 ГБ)

    Обработку, скорее всего, можно и оптимизировать, эта скорее как пример на данный момент.

    Может, и XML будет быстрее парсить как текстовик…

    Reply ↓
  11. Abadonna

    (8) cool.vlad4,

    >А чем отличается от http://infostart.ru/public/19139/ ?

    Посмотрел ради интереса, да абсолютно всем отличается! Код даже близко рядом не стоял, а что касается структуры архива — дык это мелкосфот придумал, а не мы с Василием.

    И уж никогда бы мне в голову не пришло

    МассивБукв = Новый Массив;
    МассивБукв.Добавить(«A»);
    МассивБукв.Добавить(«B»);
    МассивБукв.Добавить(«C»);
    МассивБукв.Добавить(«D»);
    ……….
    

    Испокон веков я делаю такие массивы так:

    Стр=»ABCDEFGHIJKLMNOPQRSTUVWXYZ»;

    Reply ↓
  12. Поручик

    (11) А я бывало и так

    МассивБукв = Новый Массив;
    Для Код = КодСимвола(«A») По КодСимвола(«Z») Цикл МассивБукв.Добавить(Символ(Код)); КонецЦикла;
    
    Reply ↓
  13. Jonny_wk

    Интересно

    Reply ↓
  14. Den_D

    Скоро мы все перейдем на XML общение ))

    И даже между собой исключительно по родительским узлам будем общаться)))

    Reply ↓
  15. Bassgood

    (0) Заметил, что не всегда экселевский файл читается корректно при помощи этого метода (колонки читаются не в том порядке, в котором они расположены в экселе, и некоторые значения ячеек читаются неправильно), в чем причина я понять так и не смог (заметил лишь только то, что если его содержимое скопировать в новый файл — то все читается нормально), скорее всего имеется какая то заморочка с преобразованием эксель файла в XML формат, потому что при помощи COM файл читается нормально (если интересно, то приложил этот эксель файл к сообщению). Вот такая вот загагулина.

    Reply ↓
  16. Abadonna

    (15) Zigfridish,

    я это тоже заметил, но пока особо не разбирался. Обработка абсолютно прозрачная, с комментами, может кто и найдет, где собака порылась;) Будет время — сам еще внимательно попроверяю

    Reply ↓
  17. SergDi

    (10) 7 секунд это круто

    Reply ↓
  18. fillipok

    Спасибо за обработку))в добавку:

    1. файлы OpenOffice таблицы формата .ods открываются таким же способом только естественно файл с данными xml другой))(вроде и .odt так же, но не проверял).

    Reply ↓
  19. Konder.Djironimo

    Спасибо! Все было в свое время заточено под *.xls но потом с переходом на *.xlsx пришлось всех оставлять на 2003, а тут всех обрадуем 😉 еще раз спасибо!

    Reply ↓
  20. Dethmond

    Хорошо что стал поддерживать *.xlsx. Спасибо

    Reply ↓
  21. Abadonna

    (20)Честное слово, это не я! :))))) Это мелкософт поддерживает

    Reply ↓
  22. Dethmond

    (21) ладно, Microsoft`у тоже спасибо))

    Reply ↓
  23. khaoos

    Классно придумано. Да и на другие статьи автора обращу внимание, интересные вещи пишет. Молодец!

    Reply ↓
  24. 1cmax

    Кстати, если в файле нет ни одной ячейки со строками

    файл sharedStrings.xml не создается

    и обработка валится с ошибкой.

    стоит добавить проверку

    Процедура ПарсингSharedStrings()

    XML=Новый ЧтениеXML;

    //Добавлено: maxval 03.07.2012

    Имя = Объект.ВременнаяПапка+»sharedStrings.xml»;

    Ф = новый Файл(Имя);

    Если Не ф.Существует() Тогда

    Возврат;

    КонецЕсли;

    XML.ОткрытьФайл(Имя);

    /// maxval 03.07.2012

    Reply ↓
  25. 1cmax

    реализовал все те же принципы под Управляемые формы, в.т.ч. Веб-клиент:

    http://infostart.ru/public/142187/

    Reply ↓
  26. Abadonna

    (24) 1cmax,

    Ага, я как-то поленился проверить…

    Reply ↓
  27. 1cmax

    (15) Zigfridish, Аналогичную багу нашел.. эх.. сначала думал что для любого сервака универсальное решение. а то серваки на линуксе. там никаких ком объектов…

    Reply ↓
  28. cerg110

    Как раз что скинули файл для загрузки в xsls. Как будет возможность сразу скачаю. Спасибо за полезную обработку.

    Reply ↓
  29. yuraos

    (21)


    Честное слово, это не я! :))))) Это мелкософт поддерживает

    говорят он уже пожалел об этом

    —

    в следующем офисе вроде грозятся зделать бинарный закрытый формат

    🙂

    Reply ↓
  30. Spartan

    (0) небольшой косячок… в блоке

     //— находим строчные значения из файла  sharedStrings.xml
    МассивСтрок=Новый СписокЗначений;
    ПарсингSharedStrings();
    //— находим индексы строк и прочие значения из файла xlworksheetssheetN.xml

    МассивСтрок д.б. именно массивом, а не списком значений, иначе при получении значения из ячейки со строкой имеем тип «ЭлементСпискаЗначений», а не «Строка».

     Если ЭтоСтрока Тогда
    Попытка
    Значение=МассивСтрок.Получить(Число(XML.Значение));
    Исключение
    КонецПопытки;

    Визуально в табличном поле этого не видно, но при работе с получившейся таблицей могут быть неожиданности.

    Reply ↓
  31. Spartan

    (15) А где в Вашем файле получаются неправильные значения при чтении обработкой? А то файл немаленький — визуально очень сложно заметить. Мне нужно понять, насколько это критично для моей задачи.

    Reply ↓
  32. Abadonna

    (31)Блин, ну код же открытый. Мне она без надобности, к старому я не возвращаюсь;)

    Reply ↓
  33. Spartan

    (32) Да не, вопросов нет… За код спасибо! Это я написал для тех, кто будет использовать. 😉

    Reply ↓
  34. Abadonna

    (33) Spartan, я просто не заметил сгоряча, что (31) не мне :)))

    Reply ↓
  35. Spartan

    (15),(31) Все, увидел… посмотрел сегодня на свежую голову.

    P.S. Нашел в чем косяк — сейчас разбираюсь как победить. Получается неверный массив строк из файла sharedStrings.xml, если в нем присутствуют пустые строки.

    Reply ↓
  36. Spartan

    (0),(15),(27) Как-то так, видимо:

    Функция ПарсингSharedStrings()
    XML=Новый ЧтениеXML;
    //XML.ОткрытьФайл(ВременнаяПапка+»sharedStrings.xml»);
    //Добавлено: maxval 03.07.2012
    Имя = ВременнаяПапка+»sharedStrings.xml»;
    Ф = новый Файл(Имя);
    Если Не Ф.Существует() Тогда
    Возврат Ложь;
    КонецЕсли;
    XML.ОткрытьФайл(Имя);
    /// maxval 03.07.2012
    Пока XML.Прочитать() Цикл
    //Если XML.Имя=»#text» Тогда
    // МассивСтрок.Добавить(XML.Значение);
    //КонецЕсли;
    // Spartan — 29.03.2013 — начало
    Если XML.Имя = «t» И XML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
    Если XML.Прочитать() И XML.Имя = «#text» Тогда
    МассивСтрок.Добавить(XML.Значение);
    Иначе
    МассивСтрок.Добавить(«»);
    КонецЕсли;
    КонецЕсли;
    // Spartan — 29.03.2013 — конец
    КонецЦикла;
    XML.Закрыть();
    
    Возврат Истина;
    КонецФункции
    

    Показать

    Reply ↓
  37. Lapitskiy

    (36) Spartan, не недостаточно, когда несколько листов, неверно определяет.

    Reply ↓
  38. freep777

    Обработка замечательная!!! Спасибо огромнющее за нее!

    Только вот не пойму, на одном файле нормально работает, а на другом выдает ошибку

    {Форма.Форма.Форма(80)}: Индекс находится за границами массива

    ТЗ[НомерСтроки-1][НомерКолонки-1]=Значение;

    Помогите, пожалуйста, разобраться.

    (к сообщению прикреплены файлы — на «Книга2» — работает, а на «Книга1» — выдает ошибку)… Вроде ничем таким особым файлы не различаются??? Не могу понять…

    Reply ↓
  39. ksnik

    (38) ответ найден, смотрите приложенные мной скриншоты — временные файлы,

    вот этот лист загружается успешно



    а на следующем скриншоте нумерация строк начинается не с единицы, что ведет к рассинхронизации номеров строк файла и таблизы значений 1с,



    и он не загружается потому что в таблице значений количество строк соответствует количеству строк файла, но обращение к строке таблицы значений 1с идет по номеру строки файла который не соответствует номеру строки заведенному в 1с. Надо вводить уточнять нумерацию для соответствия между файлом и таблицей значений.

    Reply ↓
  40. ksnik

    (38) вот такая доработка (исправление) парсит файл правильно

    Функция ПарсингSheet(Лист=1)
    ТЗ=Новый ТаблицаЗначений;
    XML=Новый ЧтениеXML;
    ИмяФайла=»sheet»+Строка(Лист)+».xml»;
    XML.ОткрытьФайл(ВременнаяПапка+ИмяФайла);
    
    Пока XML.Прочитать() Цикл
    Если (XML.Имя=»row») И (XML.КоличествоАтрибутов()>0) Тогда
    ТекущаяСтрока=XML.ЗначениеАтрибута(0);
    //Космачев+
    //Стр=ТЗ.Добавить();
    РазницаСтрок = ТекущаяСтрока — ТЗ.Количество();
    Для СчРазницаСтрок = 1 По РазницаСтрок Цикл
    Стр=ТЗ.Добавить();
    КонецЦикла;
    //Космачев-
    НомерСтроки=Число(ТекущаяСтрока);

    Показать

    Reply ↓
  41. ksnik

    Спасибо автору за легкий код! Очень хорошая обработка! Я еще добавил в нее номера строк и колонок вот так, процедуру ТЗИПриПолученииДанных надо подключить через обработчик события табличного поля:

    // Добавим в начало модуля переменную необходимую для автоматической нумерации строк
    Перем ТЗАнализа;
    …
    //считывание ячеек заданного листа
    Процедура ОсновныеДействияФормыПарсинг(Кнопка)
    Если Не ФайлСчитан Тогда
    Предупреждение(«Не открыт файл xlsx!»,5);
    Возврат;
    КонецЕсли;
    ТЗИ.Очистить();
    //— находим строчные значения из файла  sharedStrings.xml
    МассивСтрок=Новый СписокЗначений;
    ПарсингSharedStrings();
    //— находим индексы строк и прочие значения из файла xlworksheetssheetN.xml
    Если НомерЛиста>КоличествоЛистов Тогда
    Сообщить(«Указан номер листа, превышающий общее количество листов. Установлен Лист 1″,СтатусСообщения.Информация);
    НомерЛиста=1;
    КонецЕсли;
    //Космачев 13.04.2019 +добавил номера строк и колонок и уменьшил ширину пустых колонок+
    ТЗАнализа = ПарсингSheet(НомерЛиста);
    ЭлементыФормы.ТЗИ.Значение=ТЗАнализа;
    ЭлементыФормы.ТЗИ.СоздатьКолонки();
    СписокНомеров = Новый СписокЗначений;
    Для Каждого СтрТЗ Из ТЗАнализа Цикл
    Для Каждого КолТЗ Из ТЗАнализа.Колонки Цикл
    Если СокрЛП(ТЗАнализа[ТЗАнализа.Индекс(СтрТЗ)][ТЗАнализа.Колонки.Индекс(КолТЗ)])>»» Тогда
    Если СписокНомеров.НайтиПоЗначению(ТЗАнализа.Колонки.Индекс(КолТЗ))=Неопределено Тогда
    СписокНомеров.Добавить(ТЗАнализа.Колонки.Индекс(КолТЗ));
    КонецЕсли;
    КонецЕсли;
    КонецЦикла;
    КонецЦикла;
    н = 0;
    Для Каждого КолонкаТЗИ Из ЭлементыФормы.ТЗИ.Колонки Цикл
    КолонкаТЗИ.ТекстШапки = СокрЛП(н);
    Если СписокНомеров.НайтиПоЗначению(н)=Неопределено Тогда
    КолонкаТЗИ.Ширина = 1;
    КонецЕсли;
    н = н + 1;
    КонецЦикла;
    ЭлементыФормы.ТЗИ.Колонки.Вставить(0,»НПП»);
    КонецПроцедуры
    
    Процедура ТЗИПриПолученииДанных(Элемент, ОформленияСтрок)
    Для Каждого ОфСтроки Из ОформленияСтрок Цикл
    //ОфСтроки.Ячейки.Колонка1.УстановитьТекст(СокрЛП(ТЗАнализа.Индекс(Элемент.ТекущаяСтрока)));//+1
    ОфСтроки.Ячейки.Колонка1.УстановитьТекст(СокрЛП(ТЗАнализа.Индекс(ОфСтроки.ДанныеСтроки)));//+1
    КонецЦикла;
    КонецПроцедуры
    //Космачев-
    Процедура ПриОткрытии()

    Показать

    и уменьшил ширину колонок в которых ни в одной строке нет данных

    Reply ↓
  42. ksnik

    (16) колонки перепутаны потому что колонки создаются не единовременно в порядке определеном в файе но иначе, по мере обнаружения заполненных значений ячеек при сканировании листа процедурой ПарсингSheet.

      ИначеЕсли XML.Имя=»#text» Тогда
    Разница=НомерКолонки-ТЗ.Колонки.Количество();
    Для А=1 По Разница Цикл
    ТЗ.Колонки.Добавить();
    КонецЦикла;
    Значение=XML.Значение;

    В данном случае помогло бы предварительое скаирование для определения кол-ва строк и колонок в файле (или м.б. считывание этих значений из полей файла), затем создание колонок и второй проход по файлу парсером уже с заполнением ТЗ для сохранения правилього порядка. Если много пустых их можно удалить позже.

    Простейшим способом упорядочения колонок будут заголовки колонок на исходном импортируемом листе.

    Reply ↓
  43. ksnik

    (42) Вот программная реализация удаления путаницы и упорядочения колонок

    //Космачев 13.04.2019 +Фиксируем колонки чтоб не переставлялись+
    
    Функция ПарсингSheet1проход(Лист=1)
    
    XML=Новый ЧтениеXML;
    ИмяФайла=»sheet»+Строка(Лист)+».xml»;
    XML.ОткрытьФайл(ВременнаяПапка+ИмяФайла);
    КолвоКолонок = 0;
    Пока XML.Прочитать() Цикл
    Если (XML.Имя=»row») И (XML.КоличествоАтрибутов()>0) Тогда
    ТекущаяСтрока=XML.ЗначениеАтрибута(0);
    НомерСтроки=Число(ТекущаяСтрока);
    ИначеЕсли XML.Имя=»c» Тогда
    ИмяЯчейки=XML.ЗначениеАтрибута(0);
    НомерКолонки=СтрЗаменить(ИмяЯчейки,ТекущаяСтрока,»»);
    НомерКолонки=КолонкаЧислом(НомерКолонки);
    ИначеЕсли XML.Имя=»#text» Тогда
    Если НомерКолонки>КолвоКолонок Тогда
    КолвоКолонок = НомерКолонки;
    КонецЕсли;
    КонецЕсли;
    КонецЦикла;
    Возврат КолвоКолонок;
    КонецФункции
    
    
    Функция ПарсингSheet(Лист=1)
    //Космачев 13.04.2019 +Фиксируем колонки чтоб не переставлялись+
    КолвоКолонок = ПарсингSheet1проход(НомерЛиста);
    Сообщить(КолвоКолонок);
    ТЗ=Новый ТаблицаЗначений;
    Для Сч = 1 По КолвоКолонок Цикл
    ТЗ.Колонки.Добавить();
    КонецЦикла;
    
    //Космачев 13.04.2019 —Фиксируем колонки чтоб не переставлялись—
    
    XML=Новый ЧтениеXML;
    
    …
    
    //Космачев 13.04.2019 +убираем динамическое создание колонок — фиксируем колонки чтоб не переставлялись+
    //Разница=НомерКолонки-ТЗ.Колонки.Количество();
    //Для А=1 По Разница Цикл
    // ТЗ.Колонки.Добавить();
    //КонецЦикла;

    Показать

    Reply ↓

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *