From a8d657414df3c26bc960a1fdf141af3519734337 Mon Sep 17 00:00:00 2001
From: Huang Xin
Date: Thu, 1 May 2025 00:01:47 +0800
Subject: [PATCH] fix: more robust txt parser (#1005)
---
apps/readest-app/src/utils/txt.ts | 33 ++++++++++++++++++++++++++++---
1 file changed, 30 insertions(+), 3 deletions(-)
diff --git a/apps/readest-app/src/utils/txt.ts b/apps/readest-app/src/utils/txt.ts
index 9b752773..1b3a0be8 100644
--- a/apps/readest-app/src/utils/txt.ts
+++ b/apps/readest-app/src/utils/txt.ts
@@ -88,8 +88,17 @@ export class TxtToEpubConverter {
const segmentRegex = new RegExp(`(?:\\r?\\n){${linesBetweenSegments},}|-{4,}\r?\n`);
let chapterRegex: RegExp;
if (language === 'zh') {
- chapterRegex =
- /(?:^|\n|\s|《[^》]+》)(第?[一二三四五六七八九十百千万0-9]+[章卷节回讲篇](?:[::、 \(\)0-9]+[^\n-]*)?(?!\S)|(?:^|\n|\s|《[^》]+》)[一二三四五六七八九十百千万]+(?:[::、 ][^\n-]+)(?!\S)|(?:楔子|前言|引言|序言|序章|总论|概论)(?:[:: ][^\n-]*)?(?!\S))/g;
+ chapterRegex = new RegExp(
+ String.raw`(?:^|\n|\s)` +
+ '(' +
+ [
+ String.raw`第[一二三四五六七八九十0-9][一二三四五六七八九十百千万0-9]*(?:[章卷节回讲篇封])(?:[::、 \(\)0-9]*[^\n-]{0,24})(?!\S)`,
+ String.raw`(?:^|\n|\s|《[^》]+》)[一二三四五六七八九十][一二三四五六七八九十百千万]*(?:[:: ][^\n-]{0,24})(?!\S)`,
+ String.raw`(?:楔子|前言|引言|序言|序章|总论|概论)(?:[:: ][^\n-]{0,24})?(?!\S)`,
+ ].join('|') +
+ ')',
+ 'gu',
+ );
} else {
chapterRegex =
/(?:^|\n|\s)(Chapter [0-9]+(?:[: ][^\n]*)?(?!\S)|Part [0-9]+(?:[: ][^\n]*)?(?!\S)|Prologue(?:[: ][^\n]*)?(?!\S)|Introduction(?:[: ][^\n]*)?(?!\S))/g;
@@ -104,6 +113,24 @@ export class TxtToEpubConverter {
.join('
');
};
+ const joinAroundUndefined = (arr: (string | undefined)[]) =>
+ arr.reduce((acc, curr, i, src) => {
+ if (
+ curr === undefined &&
+ i > 0 &&
+ i < src.length - 1 &&
+ src[i - 1] !== undefined &&
+ src[i + 1] !== undefined
+ ) {
+ acc[acc.length - 1] += src[i + 1]!;
+ return acc;
+ }
+ if (curr !== undefined && (i === 0 || src[i - 1] !== undefined)) {
+ acc.push(curr);
+ }
+ return acc;
+ }, []);
+
const chapters: Chapter[] = [];
const segments = txtContent.split(segmentRegex);
for (const segment of segments) {
@@ -111,7 +138,7 @@ export class TxtToEpubConverter {
if (!trimmedSegment) continue;
const segmentChapters = [];
- const matches = trimmedSegment.split(chapterRegex);
+ const matches = joinAroundUndefined(trimmedSegment.split(chapterRegex));
for (let j = 1; j < matches.length; j += 2) {
const title = matches[j]?.trim() || '';
const content = matches[j + 1]?.trim() || '';