两本整本书要先并成一份,才谈得上下一站
古籍文本往下走,第一步往往不是算法而是拼接:一部书占一个 TXT,切分和检索要做两遍。这次直接上整本——模板 txt-merge.html 现场 10126 字节、234 行,脚本段 6844 字节 169 行原样进 node:vm 跑,源码一行没改,DOM 按 9 个 id 建桩;本轮纯 Node、没开浏览器。
两部公版古籍:古文观止那份 445453 字节、LF 2089 处、切出 2090 段、JS 串长 150341;浮生六记那份 110280 字节、LF 244、245 段、串长 36958。一次投进文本合并,下拉停在默认档。
面板、落盘、逐本切段,三套尺各读一个数
统计条给 文件数 2、总行数 2334、总字符数 187299;同一份产物落盘 555733 字节,按行数出来只有 2333。行数不是加出来的:两书段数之和 2335,合并后少 1;两书 LF 之和 2333,合并后多 1。九个档位里面板行永远比落盘行大 1——那一格走 result.split('\n').length,末尾换行之后的空段也占一格。字节与字符两层是纯加,两本各自之和与合并产物都差 0,倍数 2.9671。
图 1:段数、LF、面板行、落盘行这四把尺都挤在 2333 到 2335 三格之内。
四个分隔档位,逐档加几行、几字、几字节
四个 option 都没有 selected,浏览器默认落第一项「无分隔」,getSeparator(L168–L175)因此返回空串。行账只由分隔串含几个换行决定:面板行 = ΣLF + (n−1)×LF(分隔串) + 1,勾标记再叠 n;13 个档位组合的预测与读数逐字相等。
| 档位 | 面板总行数 | 总字符数 | 落盘字节 |
|---|---|---|---|
| 无分隔 | 2334 | 187299 | 555733 |
| 换行 | 2335 | 187300 | 555734 |
| 空行 | 2336 | 187301 | 555735 |
| 自定义三横杠 | 2334 | 187302 | 555736 |
| 空行加标记 | 2338 | 187343 | 555793 |
自定义框留空不是回落,那一支原样取空串,所以与无分隔逐字节同账。串里不含换行就不加行:三横杠 +3 字 0 行、缝隙段成 12 码元,四个等号 +4 字 0 行,带换行才加行。
差的那一格不在尾换行
两书末字节都是 0x0A、回车字节 0 处,缝隙段因此逐字等于浮生六记首行,9 码元独立成段。砍掉末字节造出 A2(445452 字节、LF 2088、2089 段)与 B2(110279 字节、LF 243、244 段)跑 2×2:无分隔四组 2334 / 2333 / 2333 / 2332,对两书段之和的差全是 -1;换行档差全 0;空行档差全 +1。末字节改的是缝隙内容——缺尾换行的那本排前面时缝隙成 55 码元,即前书末行 46 码元焊上 9 码元,配三横杠成 58。
文件名标记:每块恒为 13 加名长
标记串 '===== ' + name + ' =====\n'(L186)自带 1 个换行,每块恒为 13 加名长;复选框没有 checked,默认不勾。四种命名实测:
- 中文书名各 8 码元:每块 21 码元,两块 +2 行 +42 字,落盘多 58 字节——其中 8 枚汉字各占 3 字节
- 拼音名各 17 码元:两块 60 码元
- 8 码元配 34 码元:68 码元
- 带目录前缀各 20 码元:66 码元
行数增量恒为 2,字节增量却各档不同。
批量、重复投递,以及它其实不实时
单书四档同账——单元素数组 join 根本不插分隔串,2090 行 / 150341 字 / 445453 字节。六部(两部各三遍,ΣLF 6999)无分隔 7000 / 561897 / 1667199,空行加标记 7016 / 562033 / 1667383;无分隔档「面板减各文件之和」恒为 -(n−1)。同一文件再拖一次照样入列,push 不判重;列表每批只刷新 1 次,连点 3 次合并只是覆写统计条 3 次。
改下拉、填自定义框、勾复选框之后结果区不动,两控件的监听器各 0 个,得再点一次合并才跳到 2338 行 / 187343 字;合并完补一个文件,队列已 3 项,面板仍写 文件数 2。
编码与几条失败路径
两书首 3 字节都是 e3 80 90,即「【」的编码,无字节顺序标记,严格解一次通过、宽松解下替换字符各 0,这两类账在真书上无从发生。空列表点合并只弹「请先添加文件」;0 字节文件单独合并给出 文件数 1 / 总行数 0 / 总字符数 0 这组自相矛盾的读数;一成一败的混合批列表只 1 行,提示却写已添加 2 个。脚本段里字节门槛、1024、max=、截断切片全 0 处命中,1667199 字节的产物整串赋给结果框。
书本身数得出来的结构
古文观止独立成行的方括号标记 238 行(左右两枚码位各 238 次互证)=卷头 12 + 书首 4 + 篇题 222;卷头落在行序 44、248、438、590、713、806、904、1020、1136、1292、1510、1832,按这 12 行切区间逐卷数篇题:
图 2:十二卷篇题数逐卷排开,三卷与九卷两根 22 最高。
浮生六记的标记只有 4 行,落在行 1、54、91、148,各 9 码元,是文本里四卷的分界。同族两页各有一把尺:读取那页的字符栏与这里的字符格同属 JS 串长口径。
对照列表文案,13 条判定里 1 条站反
config/tools.php 第 1608 行起逐条判:[一致] 7、[部分一致] 5、[冲突] 1。部分一致三条与账有关——标题只举 TXT,accept 却放了 11 项;分隔符那句漏掉默认档「无分隔」,自定义留空又不回落;「批量」只到前端拼接这一层。站反的那条正是「实时预览」。浏览器解码本体的字面、磁盘编码下文件名的实际长度、187299 码元写进结果框的耗时与结果框可视高度,一律标未实测。
常见问题
Q:面板总行数为什么总比落盘行数多 1?
那一格走 split 换行取段数,末尾换行之后的空段也算一格。
Q:分隔符选了自定义却没填,会退回无分隔吗?
结果一样但不是退回:那一支原样取空串、没有兜底,产物与无分隔逐字节相同,555733 字节、2334 行、187299 字三数全等。
Q:改档位或勾上标记,结果区会跟着变吗?
不会,控件上没有监听器,改完得再点一次合并才从 2334 行跳到 2338 行、187343 字。