EPUB 转 TXT 看着是个”去掉标签”的活,实际结果差异很大:同一批书里有的转出来章节整整齐齐,有的顺序错乱、标题全丢、打开还是乱码。
差异不在工具的取舍,而在每本 EPUB 内部结构的规范程度。这篇把 EPUB 转 TXT 的解析链路拆开讲,你就能预判某本书会转成什么样,出问题时也知道该动哪个开关。
EPUB 就是一个 ZIP
把 .epub 改名成 .zip 解开,典型结构是这样:
book.epub
├── mimetype ← 固定内容 application/epub+zip
├── META-INF/
│ └── container.xml ← 指向 OPF 的入口
└── OEBPS/
├── content.opf ← 清单:metadata + manifest + spine
├── toc.ncx ← EPUB2 目录
├── nav.xhtml ← EPUB3 目录
├── text/
│ ├── part0001.xhtml ← 正文(一章或多章)
│ └── part0002.xhtml
└── images/cover.jpg
解析顺序是固定的四步:
- 读
META-INF/container.xml,拿到 OPF 的路径 - 解析 OPF 的三段:
metadata(书名 / 作者 / 语言)、manifest(资源清单)、spine(阅读顺序) - 定位目录文件:manifest 里
properties="nav"的是 EPUB3 目录,media-type="application/x-dtbncx+xml"的是 EPUB2 的 ncx - 按 spine 顺序逐个读 XHTML,抽出文字
书名、作者来自 OPF 的 dc:title / dc:creator——这也是为什么有些书转出来的文件名和你在阅读器里看到的书名一致,而有些书是”未命名”:那本书的 OPF 里就没写。想修这个问题用 EPUB 元数据编辑 补上再转。
章节顺序:只认 spine
<spine> 是 OPF 里明确列出的翻页次序:
<spine toc="ncx">
<itemref idref="cover"/>
<itemref idref="chapter1"/>
<itemref idref="chapter2"/>
...
</spine>
阅读器按它翻页,工具也按它遍历。按文件名排序会在三种常见情况下翻车:
| 文件名形态 | 字典序结果 | 问题 |
|---|---|---|
part0012.html、text00008.xhtml | 流水号与章节号无关 | 顺序随机 |
ch1、ch2 … ch10、ch11 | ch1 → ch10 → ch11 → ch2 | 第 10 章插到第 2 章前面 |
preface、ch1、appendix | 按字母排 | 附录跑到正文前面 |
所以”章节顺序会不会乱”这个问题的答案是:只要这本 EPUB 的 spine 是对的,导出顺序就和阅读器完全一致;如果连阅读器里翻页顺序都是乱的,那是书本身坏了,转换救不回来。
章节标题:三级回退
标题的取值优先级:
① nav.xhtml(EPUB3)里指向该文件的链接文字
↓ 取不到
② toc.ncx(EPUB2)的 navPoint / text
↓ 取不到
③ 该章正文里第一个 <h1>~<h6> 的文字
↓ 取不到
④ 留空(该章无标题)
匹配方式是把目录里的 href 规范化成包内绝对路径后与 spine 文档比对,锚点(#part2)会被去掉、URL 编码会被解码——所以 ../text/ch1.xhtml#top 和 text/ch1.xhtml 能对上。
这里有一个必须知道的结构性限制:
一个 XHTML 文件里塞了多章、目录靠
#anchor分节的书,同一个文件只会取到第一个目录标题,几章正文也会被合并成一章。
这种书在预览里的表现是”章节数明显少于阅读器目录条数”——比如阅读器显示 120 章,工具只识别出 8 章。它不是 bug 而是 TXT 的表达能力问题:spine 里就只有 8 个文档,工具没有更可靠的依据在文件内部切章。真的需要按章切分时,可以先导出 TXT,再用 TXT 转 EPUB 按章节正则重新切一遍目录。
哪些页会被跳过
导出的 TXT 里不会出现这些内容:
| 被跳过的 | 原因 |
|---|---|
nav.xhtml 目录页本身 | 否则 TXT 开头会多出一份章节名清单 |
| 正文抽取后为空的文档 | 纯封面页、纯图片页、占位页 |
| 非 XHTML 的 spine 项 | 图片、SVG 等无文字内容 |
<script>、<style>、<head> | 代码和样式不是正文 |
<rt>、<rp> | 日文假名注音,否则会混进汉字之间 |
最后一条对日文书特别重要——不跳过 ruby 注音的话,“漢字”会被抽成”漢かん字じ”这种夹字结果。
正文换行怎么产生
抽取文字时,块级元素和 <br> 各产生一次换行:
块级:p / div / h1~h6 / li / blockquote / section / article /
tr / pre / hr / ul / ol / table / figure / figcaption /
header / footer / aside / dd / dt / caption / main
然后做两步清理:
- 每行 trim,把不换行空格(
)换成普通空格 - 去掉全部空行,再按”段落间空行”开关用
\n\n或\n重新连接
所以你在导出选项里控制的是段落密度,而不是原书的空行——原书里连续三个空行不会被照搬过来。表格会被拉平:每个 <tr> 一行、单元格文字挨在一起,结构信息丢失。
编码:EPUB 内部不一定是 UTF-8
规范要求 EPUB 用 UTF-8 或 UTF-16,但实际流通的书(尤其早期国产转换工具产出的)会出现 GBK 声明。工具的解码策略:
- 有 UTF-8 BOM → 去掉 BOM 按 UTF-8 解
- 否则扫描文件前 1KB,找
charset=/encoding=声明 - 声明是
gbk/gb2312→ 用 GB18030 解(GB18030 是它们的超集,能覆盖更多生僻字) - 声明的编码浏览器不支持 → 回退 UTF-8
所以输入侧的乱码基本能自愈。如果转出来的中文还是乱码,通常是这本 EPUB 的编码声明本身写错了(声明 UTF-8 实际是 GBK),这种只能用 Calibre 之类的工具先修再转。
输出侧:BOM 和 CRLF 分别治什么病
这两个开关经常被搞混:
| 症状 | 病因 | 开关 |
|---|---|---|
| 记事本里全文挤成一行 | 换行符是 LF,老记事本只认 CRLF | 换行符 → CRLF |
| 记事本里中文是乱码 / 锟斤拷 | 无 BOM,被当成 ANSI(GBK) 打开 | 勾 UTF-8 BOM |
| 脚本读文件首行匹配失败 | 多了 BOM 那三个字节 | 不要勾 BOM |
| 手机阅读器显示正常但电脑异常 | 手机端两种都认,问题只在电脑 | 按电脑软件调 |
按目标设备的推荐配置:
| 使用场景 | BOM | 换行符 |
|---|---|---|
| Windows 记事本、老 MP3 / 电子词典 | 勾 | CRLF |
| 手机阅读器(静读天下、多看等) | 不勾 | LF |
| macOS / VS Code / Sublime | 不勾 | LF |
| TTS 朗读软件 | 视软件而定,先试不勾 | LF |
| 喂脚本 / 入库 / Git 管理 | 不勾 | LF |
简繁转换与其它输出控制
- 顶部书名 / 作者:在文首加
《书名》和作者:XXX两行,方便在一堆 TXT 里认书 - 章节分隔:补齐标题(每章开头插入目录标题,正文首行已是该标题时不重复插入)/ 仅空行 / 不分隔
- 段落间空行:段落之间留一行还是紧凑单行
- 简繁转换:OpenCC 字典逐字转换,正文和标题同时生效
导出规模上的限制:单文件 100 MB,多本可一次拖入、打包 ZIP 下载;文件名取自书名并过滤掉 \ / : * ? " < > | 这些非法字符。
全程本地,与 TXT 转 EPUB 互逆
解压、解析、抽取、简繁转换、打包全部在浏览器里用 JavaScript 完成,断网也能用,文件不出本机。
反方向的整理需求看这两篇:
- 把 TXT 重新做成带目录的 EPUB:TXT 转 EPUB 与 网文 TXT 转 EPUB:章节识别为什么会翻车
- 书名作者乱、封面丢、系列排不到一起:EPUB 元数据编辑 与 改完元数据阅读器不认怎么办