"Создание электронных книг в формате FictionBook 2.1: практическое руководство" - читать интересную книгу автора (Кондратович Михаил Иосифович)§ 3.7 Написание собственного конвертораВсе перечисленные выше способы хороши лишь для так называемых среднестатических входных файлов. Когда попадается нечто нестандартное, остается только, сцепив зубы, долго и нудно склеивать файлы, вылавливать куски текста, редактировать погрешности, и т.д. и т.п. Решить эти проблемы может собственноручное написание конвертора. Помимо возможности тонкой настройки софта под конкретную задачу, этот метод имеет еще одну положительную сторону: претензии по поводу кривизны решений предъявлять просто некому. Кроме как самому себе… Для написания конвертора наиболее подходящи скриптовые языки программирования, заточенные для работы с текстом и поддерживающие регулярные выражения. TCL, Java, Perl, Python… Лично я остановил свой выбор на самом молодом и многобещающем из них. Имеется в виду язык программирования Ruby. Ruby был создан в середине 90-х японским программистом Юкихиро Мацумото. Это многогранный, элегантный и в то же время очень простой для изучения язык. Он вобрал все лучшее, что было у его предшественников: мощь регулярных выражений Perl, объектно-ориентированное программирование Java и Python, помноженные на феноменальные изящество и функциональность. Скачать транслятор Ruby можно с официального сайта проекта www.ruby-lang.org. Вариант Ruby для Win32 представлен в двух вариантах. Обычный инсталлятор (rubyxxx-xx.exe)и zip-архив — ruby-xxx-pxxx-i386-mswin32.zip, где xxx — номера версий и обновления. На момент написания этого текста была доступна стабильная версия 1.8.6. Для наших целей вполне подойдет второй вариант — zip-архив. Только нужно будет папку, где находится запускной файл интерпретатора добавить в список папок, доступных по умолчанию. Для этого щелкаем правой кнопкой мыши по иконке «My computer», на панели «Advanced» жмем кнопку «Environment Variables». В списке «System variables» находим переменную Path и щелкаем по кнопке «Edit». В открывшемся окошке в поле «Variable Value» через «;» без пробела добавляем путь к нужной папке. Например C:\TOOLS\RUBY\bin. В принципе, для работы скрипта жизненно необходимы лишь два файла: exe-шник транслятора (ruby.exe) и служебная dll-ка (msvcrt-ruby18.dll). Поэтому, если не требуется использование библиотек, то можно просто забросить эти два файла в папку Windows, или любую другую, доступную по переменной Path. В крайнем случае, можно просто поместить их в ту же папку, где находятся скрипты. Разумеется, описывать программирование на языке Ruby прямо здесь нет ни необходимости, ни возможности. Это тема для отдельной книги, которуя, с Божьей помощью, я, возможно, напишу. А здесь ограничимся комментариями в самих скриптах. Если же вы не знаете, где взять документацию по Ruby, то вам прямая дорога на сайт www.flazx.com, где, среди множества другой ценной литературы, можно найти и исчерпывающую подборку книг по Ruby. Правда, англоязычных, но зато много и бесплатно. Из русскоязычных изданий могу посоветовать книгу М. Фицджеральда «Изучаем Ruby» (на момент написания книги была доступна только на бумаге), а также труд П. Фултона «Программирование на языке Ruby» (доступен в Сети). Данный скрипт размещается исключительно как классический пример. Так сказать для затравки. Скрипт выполняет простейшее преобразование текста, вмешательство в структуру будущей fb2-книги сведено практически к нулю. Но его ничего не стоит доработать. Например, чтобы он выделял главы, подзаголовки, при этом структурируя будущий файл. Вообще, средствами Ruby можно организовать такую интеллектуальную обработку текста, которая оставит Any2FB2 далеко позади. # Скрипт для конвертации форматированного текста DOS-866 в FB2 # (C) Юзич, апрель 2008 г. # # запускать из директории, где находится входной файл # запуск: ruby dtxt_fb2.rb lt;input.txtgt; # # массив для перекодировки текста. Псевдографика заменяется на код 135, # символы, отсутствующие в кодировке win-1251 - на код 138 doswin = [192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 168, 184, 170, 186, 175, 191, 161, 162, 176, 149, 183, 138, 185, 164, 138, 138] # считываем входной файл в одну строку wtext=ARGF.readlines.to_s ARGF.close wtext.gsub!(/\n(\S)/,' \1') # склеиваем строки абзацев wtext.squeeze!(" ") # убираем лишние пробелы wtext.gsub!(/^ /,"") # убираем пробелы в начале строки # перекодировка текста for i in 0..(wtext.length-1) case wtext[i] when 20: wtext[i] = 182 # обработать символ "Пи" when 21: wtext[i] = 167 # обработать символ "параграф" when 128..255: wtext[i] = doswin[wtext[i]-128] # обработать все остальное end end wtext.gsub!("...", (133).chr) # многоточие - в символ "многоточие" wtext.gsub!(/(\s)-\s/,'\1'+(151).chr+" ") # компьютерное тире - в типографское # порубим строку в массив wtext = wtext.split(/\n/) wtext.collect! do |line| if line =="" then line = "lt;empty-line/gt;" # пустая строка? else line="lt;pgt;"+line+"lt;/pgt;" # нет, не пустая end end # добавляем заголовок wtext.insert(0,"lt;?xml version=\"1.0\" encoding=\"windows-1251\"?gt; lt;FictionBook xmlns=\"http://www.gribuser.ru/xml/fictionbook/2.0\" xmlns:l=\"http://www.w3.org/1999/xlink\"gt; lt;descriptiongt; lt;title-infogt; lt;genregt;nonfictionlt;/genregt; lt;langgt;rult;/langgt; lt;/title-infogt; lt;document-infogt; lt;program-usedgt;Yuzich Ruby script (dostxt -gt; FB2)lt;/program-usedgt; lt;/document-infogt; lt;/descriptiongt; lt;bodygt; lt;sectiongt;") # добавляем хвост wtext lt;lt; "lt;/sectiongt;\nlt;/bodygt;\nlt;/FictionBookgt;\n" # сохраняем выходной файл wfile = File.new(ARGF.filename.dup.gsub!(/\.txt/i,".fb2"),"w") wfile.puts wtext wfile.close После того, как скрипт отработает, в директории появится файл lt;имя_исходного_файлаgt;.fb2. Небольшая правка в FB Editor и он будет полностью готов к распространению. Эта задачка гораздо интереснее первой. Как раз тот случай, когда написание специализированного конвертора полностью оправдано. На ресурсе http://www.wisesoft.ru находится множество различных журналов. Форматы в котором они представлены, различаются, Но один из них особо привлекателен для преобразования в FB2. Я говорю о журналах которые представлены в виде пачки html-файлов («Хакер», «Хакер-спец», «Мобильные компьютеры», «Хулиган» и др.). При всей простоте, конвертированию стандартными средствами этот способ хранения данных поддается с трудом. Более близкое знакомство выявило следующее. Имеется файл index.htm с оглавлением. Статьи представляют собой кучки html-файлов, разбитые по папкам. Текст представляет собой обычные абзацы, обрамленные тэгами «lt;pgt;». Оформление html-ок везде стандартное. К сожалению, такая лафа наблюдается не везде. С декабря 2006 года формат журналов начал эволюционировать. Статьи стали вмещаться в одну html-ку, появились списки, картинки, дополнительные стили. Хотя и не во всех журналах. Основная масса изданий представлена именно в таком виде, как описано выше. Поэтому сосредочимся именно на них. Наша задача заключается в том, чтобы вырезать куски текста из html-файлов, склеить их в один файл. Скрипт, представленный ниже, не только блестяще справляется с этим, но и выдает на выходе вполне приличный FB2, с заполненным заголовком, готовой аннотацией и разбитый на секции. # Скрипт для конвертации журналов с WiseSoft.ru в FB2 # (C) Юзич, апрель-май 2008 г. # # поддерживаемый формат файлов: пачка html, журналы сделанные c ноября 2003 г. по ноябрь 2006 г. включительно. # примечание: изменения в декабре 2006 и марте 2007 - некритичные # теоретически должен обрабатывать журналы сделанные по июль 2007 включительно # но уже с мая 2007 в тексте могут попадаться дополнительные тэги, мешающие правильной конвертации # хотя все это касается, в основном, журнала "Хакер" ("Хакер-спец"). # "Мобильные компьютеры", к примеру, по-прежнему, как шли, так и идут в старом формате # да и чтобы обрабатывать журналы, сделанные до ноября 2003, достаточно подправить ключевые фразы # # запускать из директории, где находится файл-оглавление журнала (index.htm) # запуск: ruby ws_j_cnv.rb # # считываем файл-оглавление в строку wfile=File.open("index.htm") ltext=wfile.readlines.to_s wfile.close # выгрызаем заголовок fbtitle=/lt;SPAN CLASS="titleSet"gt;(.+)lt;font color="#FF0000"gt;(#\d{1,3})lt;\/fontgt;(.+)lt;\/SPANgt;/.match(ltext).captures # выгрызаем ссылки на статьи и разделители filtr=/lt;SPAN CLASS="minSet"gt;(.+)lt;\/SPANgt;|lt;a href="(\d{3}\/\d.htm)"gt;(.+)lt;\/agt;/ lmas = ltext.grep(filtr) # начинаем формировать выходной текст outtext="lt;titlegt;lt;pgt;"+fbtitle[0]+fbtitle[1]+fbtitle[2]+"lt;/pgt;lt;/titlegt;\n" # начинаем формировать аннотацию annotation="lt;pgt;Содержание номера:lt;/pgt;\n" # флажок открытой секции раздела flagSect=false # прокручиваем список ссылок lmas.each do |line| if line.include? "SPAN" then # ССЫЛКА ИЛИ РАЗДЕЛИТЕЛЬ? if flagSect then outtext=outtext+"lt;/sectiongt;\n" end # РАЗДЕЛИТЕЛЬ. Секцию закрывать надо? /lt;SPAN CLASS="minSet"gt;(.+)lt;\/SPANgt;/.match(line) # выгрызаем разделитель... outtext=outtext+"lt;sectiongt;\nlt;titlegt;lt;pgt;"+$1+"lt;/pgt;lt;/titlegt;\n" # открываем секцию раздела annotation=annotation+"lt;pgt;"+$1+":lt;/pgt;\n" # и дополняем аннотацию flagSect=true # секция осталась открыта... else # НЕ-ЕТ, ВСЕ-ТАКИ ССЫЛКА... filtr.match(line) pathf = $2 # берем путь к первому файлу статьи... annotation=annotation+"lt;pgt;"+(149).chr+" "+$3+"lt;/pgt;\n" # дополняем аннотацию... puts pathf[0..2] # это чтобы не скучно было ждать... outtext=outtext+"lt;sectiongt;\n" # открыли секцию... while File::exists?(pathf) do # обрабатываем статью # считали файлик в текстовую строку... wfile=File.open(pathf) wtext=wfile.readlines.to_s wfile.close wtext[/lt;htmlgt;.+?lt;\/pgt;/m]="" # чик! головка... wtext[/lt;centergt;.+lt;\/htmlgt;/m]="" # чик! хвостик... # заголовок статьи оставлять? wtext = pathf[4,2].to_i == 1 ? wtext.sub(/.*\n(.*\n.*\n).*\n.*\n/,' \1') : wtext.sub(/.*\n.*\n.*\n.*\n.*\n/,' \1') outtext=outtext+wtext # оставшийся текст - к основному массиву # модифицируем имя файла if pathf[4,2].to_i gt;= 10 pathf[4,2] = (pathf[4,2].to_i + 1).to_s else pathf[4] = (pathf[4,2].to_i + 1).to_s end end # конец цикла outtext=outtext+"lt;/sectiongt;\n" # закрываем секцию end # усе. статью оформили... end # все статьи собрали в одну строку... # модифицируем выходной текст под FB2 outtext.gsub!("lt;pgt;lt;biggt;lt;biggt;lt;biggt;lt;stronggt;","lt;titlegt;lt;pgt;") outtext.gsub!("lt;/stronggt;lt;/biggt;lt;/biggt;lt;/biggt;lt;/pgt;","lt;/pgt;lt;/titlegt;") outtext.gsub!("lt;pgt;lt;biggt;lt;stronggt;","lt;epigraphgt;lt;pgt;") outtext.gsub!("lt;/stronggt;lt;/biggt;lt;/pgt;","lt;/pgt;lt;/epigraphgt;") outtext.gsub!("amp;nbsp;",(160).chr) outtext.gsub!(/amp;(?!lt;|gt;)/,"amp;amp;") annotation.gsub!(/amp;(?!lt;|gt;)/,"amp;amp;") outtext.gsub!("lt;brgt;","lt;/pgt;lt;pgt;") outtext.gsub!("lt;brgt;","lt;/pgt;lt;pgt;") # чистим мусор outtext.gsub!(/\x01|\x12|\x18|\x1E/, "?") # удаляем непечатные символы # корректируем неправильное использование "lt;" и "gt;" # заодно прибиваем ненужные тэги outtext.gsub!(/(lt;pgt;)(.*)(lt;\/pgt;)/) do |line| subl1,subl2,subl3 = $1,$2,$3 subl2.gsub!("lt;","\x8b") subl2.gsub!("gt;","\x9b") line=subl1+subl2+subl3 end # а линки выделим жирным outtext.gsub!(/\x8Ba href.+?\x9B(.*?)\x8B\/a\x9B/) {|line| line="lt;stronggt;"+$1+"lt;\/stronggt;"} # компьютерное тире - в типографское outtext.gsub!(/\s-\s/," \x97 ") outtext.gsub!("lt;pgt;-", "lt;pgt;\x97") annotation.gsub!(/\s-\s/," \x97 ") # добавляем заголовок outtext="lt;?xml version=\"1.0\" encoding=\"windows-1251\"?gt; lt;FictionBook xmlns=\"http://www.gribuser.ru/xml/fictionbook/2.0\" xmlns:l=\"http://www.w3.org/1999/xlink\"gt; lt;descriptiongt; lt;title-infogt; lt;genregt;nonfictionlt;/genregt; lt;authorgt; lt;nicknamegt;Редакция журналаlt;/nicknamegt; lt;/authorgt; lt;book-titlegt;"+fbtitle[0]+fbtitle[1]+fbtitle[2]+"lt;/book-titlegt; lt;annotationgt;\n"+annotation+"lt;/annotationgt; lt;dategt;"+fbtitle[2][-7,4]+"lt;/dategt; lt;langgt;rult;/langgt; lt;sequence name=\""+fbtitle[0]+"\" number=\""+fbtitle[1][1,2]+"\"/gt; lt;/title-infogt; lt;document-infogt; lt;program-usedgt;Yuzich Ruby script (WiseSoft -gt; FB2)lt;/program-usedgt; lt;src-urlgt;http://www.wisesoft.rult;/src-urlgt; lt;/document-infogt; lt;/descriptiongt; lt;bodygt;\n"+outtext if flagSect then outtext=outtext+"lt;/sectiongt;" end # если надо, закроем последнюю секцию раздела outtext=outtext+"lt;/bodygt;\nlt;/FictionBookgt;\n" # добавляем хвост # в имени выходного файла не должно быть двоеточия fbtitle[0].gsub!(":","-") # и типографские кавычки - это не есть хорошо fbtitle[0].gsub!((171).chr,"`") fbtitle[0].gsub!((187).chr,"`") # сохраняем выходной файл wfile = File.new(fbtitle[0]+fbtitle[1]+".fb2","w") wfile.puts outtext wfile.close Вам останется только загрузить готовый FB2-файл в FB Editor и доделать, то, с чем не справился скрипт: вставить обложку, разметить подзаголовки и цитаты, подправить эпиграфы. Но это не должно отнять много времени. Основную часть работы проделал скрипт. Рабочие версии этих скриптов вы можете взять с моего сайта http://yuzzich.narod.ru. |
||
|