txt文本文件解析bookParser.js
js
/**
* 解析TXT书籍
* @param {string} text - TXT文件内容
* @param {string} fileName - 文件名(用于默认标题)
* @param {function} progressCallback - 进度回调函数 (progress: number, message: string)
* @returns {Promise<object>} 解析后的书籍对象 { title, info, author, chapters }
*/
export async function parseTxtBook(text, fileName, progressCallback) {
try {
// 初始进度更新
progressCallback(0.1, '正在分析文本内容...');
// 1. 去除 BOM
if (text.charCodeAt(0) === 0xFEFF) {
text = text.slice(1)
}
// 2. 按行分割
const lines = text.split(/\r?\n/).map(line => line.trim())
progressCallback(0.2, '正在识别章节结构...');
// 3. 🔍 阶段1:使用你原来的“标准正则”(精准)
const standardRegex = /^第[零一二三四五六七八九十百千0-9]+[章节回集卷]/i
let chapterStartIndex = lines.findIndex(line => standardRegex.test(line))
let chapterRegex = standardRegex
// 4. 如果没找到标准章节,进入“宽松模式”
if (chapterStartIndex === -1) {
progressCallback(0.25, '章节结构识别失败,进入宽松模式... 后续还可手动调整正则识别...');
// 宽松正则:只找常见关键词
const looseRegex = /(?:第[零一二三四五六七八九十百千0-9]+[章节回集卷]|序章|终章|外传|番外|楔子|第[0-9]+章|Chapter|Vol\.)/i
// 找第一个像章节的行
chapterStartIndex = lines.findIndex(line => looseRegex.test(line))
// 如果还是没找到,从第一个非空行开始
if (chapterStartIndex === -1) {
chapterStartIndex = lines.findIndex(line => line.length > 0)
}
// 使用宽松正则进行后续解析
chapterRegex = looseRegex
}
// 5. 如果还是没找到,从第0行开始
if (chapterStartIndex === -1) {
chapterStartIndex = 0
}
// 6. 提取 info:第一章前的内容,最多 500 字
const rawInfo = lines.slice(0, chapterStartIndex).join('\n')
const info = rawInfo.length > 500
? rawInfo.substring(0, 500) + '...'
: rawInfo
// 7. 提取作者
let author = '佚名'
const authorMatch = info.match(/(?:作者|著者)[::]\s*([^\n]+)/i)
if (authorMatch) {
author = authorMatch[1].trim()
}
// 8. 文件名作为标题
const title = fileName.replace(/\.\w+$/, '').trim() || '未知小说'
// 9. 解析章节
const chapters = []
let currentChapter = null
const contentBuffer = []
progressCallback(0.3, '正在分割章节...');
// ✅ 计算有效行数
const totalContentLines = Math.max(1, lines.length - chapterStartIndex); // 防止除0
for (let i = chapterStartIndex; i < lines.length; i++) {
const line = lines[i]
if (chapterRegex.test(line)) {
// 保存上一章
if (currentChapter) {
currentChapter.content = contentBuffer.join('\n').trim()
chapters.push(currentChapter)
// ✅ 基于有效行数计算进度
const processedLines = i - chapterStartIndex;
const progress = 0.3 + 0.6 * (processedLines / totalContentLines);
progressCallback(Math.min(progress, 0.95), `已识别 ${chapters.length} 个章节`);
}
// 新章节
currentChapter = { title: line, content: '' }
contentBuffer.length = 0
} else if (currentChapter) {
contentBuffer.push(line)
}
}
// 保存最后一章
if (currentChapter) {
currentChapter.content = contentBuffer.join('\n').trim()
chapters.push(currentChapter)
}
// 如果没有识别到任何章节,将整个内容作为一个章节
if (chapters.length === 0) {
progressCallback(0.9, '未识别到章节结构,创建“全文”章节...');
chapters.push({
title: '全文', // 或 '正文' / '单章内容' / '原始文本'
content: text.trim() // 使用原始文本(已去BOM)
})
}
// 清理内容
chapters.forEach(ch => {
ch.content = ch.content.replace(/\n{3,}/g, '\n\n').trim()
})
progressCallback(1.0, '解析完成');
return {
title,
info,
author,
chapters
}
} catch (error) {
console.error('TXT解析错误:', error);
throw new Error('解析TXT文件失败: ' + error.message);
}
}