本帖最后由 phan 于 2013-9-11 23:34 编辑
问:Calibre是如何分章与提取目录的?
答:Calibre首先会将各种格式的源文件(如TXT、EPUB)转化为XHTML格式,然后通过用户指定的XPath表达式将章节和目录从整篇文章中筛选出来,最后整合到目标格式(如EPUB、PDF)中。在“转换书籍”对话框中的“调试”面板里可以指定一个调试文件存放处,这样执行一次转换后即可观察由源文件生成出的XHTML格式中间文件的内容。
问:什么是XHTML?
答:XHTML意为“严格符合XML标准”的HTML。XHTML的好处之一就是结构整齐,便于后期处理(比如利用XPath)。如下代码就是典型的XHTML文档片段:
- <html>
- <head>
- <title>A very short ebook</title>
- <meta name="charset" value="utf-8" />
- </head>
- <body>
- <h1 class="bookTitle">A very short ebook</h1>
- <p style="text-align:right">Written by Kovid Goyal</p>
- <div class="introduction">
- <p>A very short ebook to demonstrate the use of XPath.</p>
- </div>
- <h2 class="chapter">Chapter One</h2>
- <p>This is a truly fascinating chapter.</p>
- <h2 class="chapter">Chapter Two</h2>
- <p>A worthy continuation of a fine tradition.</p>
- </body>
- </html>
复制代码 问:什么是XPath表达式?
答:XPath表达式用于定位XML文档中的元素。由于XHTML是XML的子集,所以XPath也可用于XHTML的定位。
问:什么是元素?
答:元素由起始标签<blabla>、对应结束标签</blabla>以及两标签中间所包括的内容组成。如<h1>Chapter 1</h1>就是一个元素,其中h1被称为元素的(标签)名称(name)。元素的起始标签中还可以含有一个或多个属性对,如<h1 class='chapter'>Chapter 1</h1>中的class='chapter'就是一个属性对。属性对又可分解为属性名(如class)和属性值(如chapter)。属性值需用单引号或双引号括起。
问:Calibre中的默认章节检测XPath表达式是什么意思?
答:默认表达式大致如下
- //*[((name()='h1' or name()='h2') and re:test(., '\s*((chapter|book|section|part)\s+)|((prolog|prologue|epilogue)(\s+|$))', 'i')) or @class = 'chapter']
复制代码 这个表达式看似复杂,但最上层结构很简单://*[(A and B) or C]。A、B、C是三个条件,整个表达式的意思是“选中任意层级的元素中同时满足条件A和条件B,或满足了条件C的元素”,Calibre会把这种元素所含的内容提取出来作为章节名。其中:
- A是 (name()='h1' or name()='h2')
- B是 re:test(., '\s*((chapter|book|section|part)\s+)|((prolog|prologue|epilogue)(\s+|$))', 'i')
- C是 @class = 'chapter'
问://*[...]是什么意思?
答:一般匹配章节的XPath表达式都如此起头,表示尝试匹配所有元素之意。具体的筛选条件写在方括号中。
问:and、or是什么意思?
答:逻辑连词,A and B意为同时满足条件A与条件B,而A or B意为条件A与条件B满足其中一项或两项都满足。括号如同四则运算中的括号表示逻辑判断优先级。
问:条件A是什么意思?
答:“若元素的标签名称是h1或h2,则元素的内容作为章节名”
对于第二问给出的XHTML样例,单独使用条件A得到的目录项为A very short ebook、Chapter One和Chapter Two。
问:条件B是什么意思?
答:“若元素的内容含有chapter、book、section、part字样并后跟若干空格,或含有prolog、prologue、epilogue字样,后面跟或不跟空格均可,则是章节名”
re.test(., X, Y)是Calibre扩展的XPath语法,表示用正则表达式X和匹配选项Y测试元素的内容。条件B中选项Y为'i',意为匹配时忽略大小写。对正则表达式介绍如下:
- \s表示空白字符(相对地,\S表示任意非空白字符)
- $表示整个内容的结束(相对地,^表示整个元素内容的开始)。
- ?表示零个或1个“前项”(比如\s?表示表示零个或1个空白字符)
- *表示零个或若干“前项”(比如\s*表示表示零个或多个空白字符)
- +表示1个或若干“前项”(比如\s+表示表示1个或多个空白字符)
- ()“括号”表示将一组符号视作一项(比如chapter?匹配chapte和chapter,而chapt(er)?匹配chapt和chapter)
- (chapter|book|section|part)和(prolog|prologue|epilogue)表示用|分割的几个单词匹配其一
- (\s+|$)表示匹配若干空白或元素内容的结尾
对于第二问给出的XHTML样例,单独使用条件B得到的目录项为Chapter One和Chapter Two。
问:条件C是什么意思?
答:“若元素的属性对中有class='chapter'一项,则元素的内容作为章节名”
注意要在属性名class前加一@符号,单双引号可以互换。
对于第二问给出的XHTML样例,单独使用条件C得到的目录项为Chapter One和Chapter Two。
问:如何编写自己所需的表达式?
答:大部分情况下将默认XPath表达式涉及语义的部分修改一下即可。通常如果源文件是TXT格式的文本文件,那么条件A和条件C都可以不要,直接把XPath表达式写成
- //*[re:test(., '^\s*(第\s*\S+\s*(章|节)(\s+|$))|((开场白|终章|尾声)(\s+|$))', 'i')]
复制代码 问:调试章节检测有什么技巧?
答:可以在调试XPath表达式时先将源文件的内容删掉大半以加快转换速度。在“转换书籍”对话框中,可以开启“内容目录”面板中的“在转换完成后手动精细调整目录”。
|