Текст любой ценой: Miette

    Да, вы не ошиблись, и это не дежавю. Вы наверняка когда-то (если завсегдатай) видели этот топик. С тех пор прошло много времени, а мне продолжают ходить письма с вопросами и просьбами о совете на тему чтения текстовой информации из бинарных форматов данных. А это значит, что тема до сих пор актуальна, интересна для программирующей общественности.

    За этот год (а ведь и вправду прошло больше года) я поменял место работы и занимаюсь совершенно другими вещами и давно уже не программирую (много не программирую, если быть точным) на PHP. Новый проект обязал меня совершенствоваться в python'е (и ощутить его силу), поэтому однажды воскресным вечером было решено переписать и, главное, улучшить некоторые из своих библиотек для чтения текста. Сегодня я представлю на суд публики молодой opensource-проект Miette («вкусняшка», если переводить с французского), который призван (в каком-никаком будущем) читать файлы пакета Microsoft Office.

    Основной задачей Мьетт будет в первую очередь чтение чистого текста из офисных форматов, но в этот раз мне хотелось бы пойти дальше и сотворить невозможное: заставить парсер читать форматирование (хотя бы минимальное). Задача сложная, но вполне посильная, если будет время по вечерам и интерес (а возможно посильная помощь в виде тестирование и совместной разработки) со стороны страждущего народонаселения. Но это всего лишь планы и, так сказать, хобби.

    Естественно python во многом отличается от PHP и, на мой взгляд, имеет несколько больший функционал, поэтому и принцип построения библиотек в проекте несколько другой, нежели старая «поделка» на PHP. В данном случае было решено запретить себе, как разработчику и заказчику в одном лице, загружать какие-либо большие блоки в память. Мьетт читает данные постепенно, по требованию, как это делает сам Word. Это делает его легковесным и нетребовательным к оперативной памяти. В будущем, я постараюсь пройти исходные profiler'ем и найти узкие горлышка, которые стоит оптимизировать дальше.

    Идём дальше?

    Я советую просмотреть старую статью и исходники cfb и doc на PHP перед тем, как читать дальше.

    Структура проекта


    Проект состоит (и в последствии будет состоять) из директорий, каждая из которых содержит reader того или иного типа файлов. Сейчас существует reader на Compound File Binary File Format, который является обёрткой над данными большинства офисных файлов, и для DOC (Microsoft Word). Дальше добавится поддержка XLS и PPT.

    CFB содержит два основных объекта — Reader и DirectoryEntry, над которыми построены остальные «читатели». Первый предоставляет интерфейс для работы со «вхождениями в каталог», из которых состоит CFB-хранилище. С помощью класса Reader вы сможете получить доступ к требуемому entry как по имени, так и по номеру. Для корневого вхождения («Root Entry») сделан проброс на атрибут, что, как можно заменить в классе DirectoryEntry, во многом упорядочивает и стандартизирует работу с mini FAT.

    DirectoryEntry реализует минимальный интерфейс работы с файлами: read([size]), seak(offset, [whence]) и tell(). Это опять же упрощает работу со «вхождениями» и, в целом, в духе python'а. Вы всё также можете прочесть целое вхождение с помощью read() без параметра, но при чтении нескольких байт вы получите вполне выгодное решение, которые не прочитает ни одного лишнего бита. Кроме того, вы можете обратиться к left/right sibling и child «вхождениям» через соответствующие атрибуты — это делает хождение по дереву CFB удобным и ненавязчивым.

    На примере DocTextReader вы можете увидеть пример работы с CFB. Как можно заметить, в отличие от PHP-реализации мы стараемся прочитать меньший объём данных в оперативную память, постоянно перемещаясь по doc-файлу. Нам на помощь приходят дополнительные методы DirectoryEntry get_byte, get_short и get_long, которые читают соответствующее количество байтов с определённого места. Осуществлён проброс основных «вхождений» 0/1Table и WordDocument в качестве атрибутов класса.

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

    Пример использования


    Ну и напоследок пример использования библиотеки.

    from doc.text import DocTextReader

    doc = DocTextReader('parus.doc')
    root_entry = doc.root_entry
    word_document = doc.get_entry_by_name('WordDocument')
    one_table = root_entry.child.left_sibling.left_sibling

    fc_clx = self.word_document.get_long(0x01a2)

    one_table.seek(fc_clx)
    print one_table.read(1)
    print one_table.tell() # fc_clx + 1

    print doc.read()


    P.S. Надеюсь я и Miette вас не разочаруют. Следите за обновлениями на GitHub'е :)
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 17

      +2
      Непонятно только почему импортируется одни класс, а используется другой.
        0
        Прошу меня извинить, задумался, видать, когда набирал тест примера.
        0
        Попробуем, по тестю.
          +6
          из ридми — «small sweat thing» — это маленькая потная хрень
            +3
            Пусть так и будет! Мы ж MS продукты парсим ;)
              +1
              Вот я описался-то :) Спасибо, что обратили внимание.
              +2
              Эм… а почему у вас по-французски написано «мьет», а по-русски «митти»?

              Кстати, для выдирания чистого текста из майкросвалки отлично годится Antiword.
                +1
                Простите меня за мой французский :)
                0
                А «новые» форматы — docx и компания?
                Тем более что спецификация открыта… Но, тем не менее, я так и не нашёл нормальной библиотеки для генерации, а текст приходилось вручную через lxml выдирать.
                  0
                  Посмотрите мою старую статью. А в целом парсинг зазипованного xml совсем не сложная задача, поэтому её решение если и будет на python'е, так разве что когда-нибудь потом.
                    0
                    Я знаю.) для этого в принципе достаточно базовых знаний XML'a и lxml, тем не менее, для серьёзных решений этого мало — было бы классно иметь единую библиотеку для парсинга/генерации. По всем используемым «офисным» форматам.
                  0
                  Так а на питоне разве нет ещё парсера OLE-документов, портированного с перла? На котором пхпшный Spreadsheet_Excel_Reader построен.
                    0
                    Вполне возможно, что я что-то проглядел. В целом мне требовался определённый способ чтения структуры OLE-хранилища, который я хотел бы «протащить» через все остальные надстройки, будь то doc, ppt или xls.
                      0
                      В пхп это pear.php.net/package/OLE

                      Т.к. это порт перлового первоисточника, то, в общем, вероятно, что и на питоне не стали делать то же самое заново.
                        0
                        Уважаемый, Вы видели этот порт? Он читает данные во внутренний буфер, т.е. будет загинаться на больших файлах. Мой вариант читает данные прямо из файла «на лету», у него нет проблемы нехватки памяти. Это не порт и не велосипед — это так, как надо было делать изначально.
                        0
                        Т.е. когда вы на пхп это сами писали, я удивился, но на питоне это уже дважды велосипед :)
                          0
                          На PHP был велосипед — не спорю. Да впрочем после Word'а, любая реализация формата — уже велосипед.

                    Only users with full accounts can post comments. Log in, please.