
LunaTranslator 文字处理全解析16 种文本清洗/去重方法的原理、配置与实战指南【免费下载链接】LunaTranslator视觉小说翻译器 / Visual Novel Translator项目地址: https://gitcode.com/GitHub_Trending/lu/LunaTranslator导读在 Visual Novel TranslatorLunaTranslator的 HOOK 模式下从游戏内存中截取的文本经常带有乱码、重复字符、HTML 标签、读音注音等“脏数据”。本文以官方文档 docs/cht/textprocess.md 为骨架逐条讲解 16 种内置文字处理方法的作用、触发场景、参数含义与源码级实现原理并给出内嵌翻译限制、执行顺序配置、自定义 Python 处理等实战要点帮助读者针对不同引擎的游戏快速组合出最合适的清洗方案。一、文字处理机制概述1.1 为什么需要文字处理HOOK 模式通过注入游戏进程、挂钩文本绘制函数来截取屏幕上显示的文字。这种方式的优点是速度快、无 OCR 误差但也带来了特有的“脏数据”问题重复字符游戏为了绘制阴影、描边、发光等效果同一字符会被反复绘制多次HOOK 会多次截取乱码字符日文游戏使用了 Shift_JIS 编码某些特殊字符无法用该字符集表示截取后呈现为乱码脚本残留游戏脚本中的 HTML 标签、{汉字/读音}注音标记、控制符等也会被一并截获整段重复部分引擎每帧刷新整行文字导致同一句话被截取多次。此时就需要对截取到的原始文本进行文字处理text processing。正如官方文档开篇所述一般在 HOOK 模式下有時會讀取到錯誤的文字例如有重複的文字或者其他亂七八糟的文字這時需要使用文字處理來解決。1.2 处理的执行顺序从源码结构看文字处理并不只有一个函数而是由多条链组成HOOK 截取后的立即处理核心入口是 myutils/post.py 中的POSTSOLVE()函数它以postprocess_rank处理顺序列表为顺序逐个执行启用的处理方法翻译前后的二次处理项目还提供了基于类的处理链如 transoptimi/myprocess.py在 LunaTranslator.py 中通过solvebeforetrans()在翻译前调用各处理类的process_before()、翻译后调用process_after()对结果进行再加工。本文聚焦于前者——即文档核心描述的POSTSOLVE处理链中的 16 种方法。1.3 配置存储与默认状态所有处理方法的开关与参数统一存储在 defaultconfig/postprocessconfig.json 中。每个方法是一个配置项典型结构如下_2: { use: true, name: HOOK_去除重复字符_AAAABBBBCCCC-ABC, args: { 重复次数(若为1则自动分析去重): 1, 保持非重复字符: true }, argstype: { 重复次数(若为1则自动分析去重): { type: intspin, min: 1, max: 10000 }, 保持非重复字符: { type: switch } }, isHookOnly: true }其中use是否启用默认启用三个最常用方法Unicode正规化、HOOK_去除重复字符、HOOK_去除重复行_ABCDABCDABCD-ABCDargs方法的参数及默认值argstype参数的类型定义intspin表示整数微调框combo表示下拉选择switch表示开关isHookOnly: true仅对 HOOK 模式生效对 OCR 等其他文本源无效isExUse: true属于“内嵌翻译安全集”在内嵌翻译模式下仍可生效。在POSTSOLVE()的执行逻辑中post.py每个方法都会被检查必须存在于配置、use为真、满足isHookOnly/isExUse约束后才会根据其函数签名1 个参数或 2 个参数被调用异常会被捕获而不影响整条链继续执行。二、基础过滤类方法适用于所有文本源2.1 过滤文字中的非日文字符集字符_remove_non_shiftjis_char作用过滤掉无法使用 Shift_JIS 字符集编码的字符主要针对日文游戏的乱码问题。由于乱码多出现在日文游戏该方法在配置中默认存在。官方示例エマさんԟのイԠラストは全部大好き → エマさんのイラストは全部大好き源码实现post.pydef _remove_non_shiftjis_char(line: str) - str: return line.encode(shift-jis, ignore).decode(shift-jis)原理很直观将文本用shift-jis编码ignore参数会丢弃无法编码的字符再解码还原从而剔除非法字符。注意该方法主要面向日文游戏。若游戏使用其他编码如中文游戏使用 GBK此方法可能不适用可考虑使用下面的 Unicode 正规化或字符串替换。2.2 过滤控制字符_remove_control作用过滤掉 ASCII 控制符即\x00–\x1F以及\x7F范围内的字符例如文档中提到的 这类不可见字符。这些字符通常来自游戏内部格式标记。源码实现post.pydef _remove_control(line: str) - str: return .join(r for r in line if not is_ascii_control(r))is_ascii_control定义在 myutils/utils.py用于判断字符是否为 ASCII 控制字符。2.3 过滤英文标点_remove_symbo作用过滤掉 ASCII 英文字符集中的标点符号!#$%()*,-./:;?[\]^_{|}~源码实现post.pydef _remove_symbol(line: str) - str: return .join(r for r in line if not is_ascii_symbol(r))2.4 过滤「」以外的字符_remove_not_in_ja_bracket作用仅保留日文角括号「」内的内容。适用于 HOOK 截取到大量行外文本、而有效对话都在「」内的情况。官方示例こなみ「ひとめぼれってやつだよね……」 → 「ひとめぼれってやつだよね……」源码实现post.pydef _remove_not_in_ja_bracket(line: str) - str: sections re.findall(r「[^」]*」, line) return .join(sections) if sections else line注意如果文本中没有任何「」括号该函数会原样返回文本避免误伤无括号对话。2.5 去除花括号 {}_1作用许多日文游戏脚本使用花括号给汉字注音常见格式为{漢字/讀音}与{漢字:讀音}。该方法先按这些模式去除读音标注再去除所有剩余花括号及其内容。官方示例「{恵麻/えま}さん、まだ{起き/おき}てる」 或 「{恵麻:えま}さん、まだ{起き:おき}てる」 → 「恵麻さん、まだ起きてる」源码实现post.pydef remove_braces(line: str) - str: line re.sub(r\{(\w)(.*?)\}(.*?)\{\/\1\}, r\3, line) line re.sub(r\{([^}]*?)[:/](https://link.gitcode.com/i/5792e983c556de7bc766a613952b035f)\}, r\1, line) line re.sub(r\{.*?\}, r, line) return line三步策略第一步处理成对的{tag...}...{/tag}标记类似富文本闭合标签仅保留标签之间的内容第二步处理{漢字/讀音}或{漢字:讀音}模式只保留汉字部分第三步兜底删除所有剩余{...}结构。2.6 Unicode 正规化fulltohalf作用将文本中的全角/半角、兼容字符统一规范化解决 HOOK 截取文本中全角英文字母、全角标点与标准文本不一致的问题。配置中默认启用默认类型为NFKC。官方示例 ’ → ???(I guess he doesn’t want to talk to strangers...)源码实现post.pydef unicode_normalization(text: str, args: dict) - str: return unicodedata.normalize(args.get(type, NFKC), text)即 Python 标准库unicodedata.normalize()。配置中type参数提供四种选择postprocessconfig.json 中fulltohalf项模式说明NFD规范分解把组合字符拆成基字符附加符号NFC规范分解后重组默认的合成形式NFKD兼容分解全角→半角、字体变体→标准字符NFKC兼容分解后重组最常用默认值对于日文游戏全角英文、全角标点、会在翻译时造成干扰使用NFKC后即可归一为半角形式显著提升翻译引擎的识别率。2.7 截取指定行数lines_threshold_1作用只保留文本中指定数量的行用于处理 HOOK 一次性截取多行、其中只有部分行需要翻译的情况。配置参数见 postprocessconfig.jsonmaxzishu截取行数整数取值范围-9999999999999999默认1cut_reverse截取末尾开关默认true。源码实现post.pydef slice_lines(line: str, args: dict) - str: max_lines args[maxzishu] splits line.splitlines() if len(splits) abs(max_lines): reverse args.get(cut_reverse, True) splits splits[-max_lines:] if reverse else splits[:max_lines] return \n.join(splits) return line逻辑按换行符拆分行列表仅当行数超过设定值时才截取cut_reverse为真时取末尾 N 行新剧情文本通常出现在末尾为假时取开头 N 行若行数未超限则原样返回。2.8 过滤角括号 _4作用过滤 HTML 标签。文档特别说明這個實際上是過濾 HTML 標籤怕小白不知道是什麼意思所以這麼寫的名字。主要应用于TyranoScript 引擎制作的游戏——其 HOOK 截取的是 innerHTML往往夹带大量div、/div、div iddsds等标签。源码实现post.pydef remove_angle_brackets(line: str) - str: line re.sub(r(.*?), r, line) return line使用非贪婪匹配删除所有...结构。2.9 过滤换行符_6EX作用合并文本中的换行。关键细节如果来源语言不是日文换行会被替换为空格而非直接删除避免多个英文单词被拼接到一起如hello world不会被处理成helloworld。源码实现post.pydef remove_line_breaks(line: str) - str: ws getlangsrc().space line ws.join(sect for sect in line.splitlines() if sect) return linegetlangsrc().space根据当前源语言返回对应的“连接符”——日文用空字符串日文不需要空格分词其他语言用空格。这也是文档所述行为的源码印证。2.10 过滤数字_91作用过滤掉09全部数字。def remove_digits(line: str) - str: line re.sub(r([0-9]), r, line) return line2.11 过滤英文字母_92作用过滤掉AZ与az英文字母。def remove_alphabets(line: str) - str: line re.sub(r([a-zA-Z]), r, line) return line三、HOOK 去重类方法仅对 HOOK 模式生效这一类是文档重点也是实际使用中最常用、最复杂的部分。它们都带有isHookOnly: true标记仅在 HOOK 文本源下生效。核心难点在于如何判断真实文本与重复模式。3.1 HOOK 去除重复字符AAAABBBBCCCC→ABC_2适用场景游戏先绘制一遍文字、再绘制阴影、再绘制描边等HOOK 会多次截取被重复绘制的字符。官方示例恵恵恵麻麻麻ささささんんんははは再再再びびび液液液タタタブブブへへへ視視視線線線ををを落落落とととすすす。。。 → 恵麻さんは再び液タブへ視線を落とす。配置参数重复次数(若为1则自动分析去重)整数110000默认1表示自动分析重复字数也可指定确定的重复次数如 3避免分析误差保持非重复字符开关默认true。源码实现post.pyif times 2: guesstimes times else: dumptime Counter() cntx 1 lastc None for c in list(line) [0]: if c ! lastc: dumptime[cntx] 1 lastc c cntx 1 else: cntx 1 _max max(dumptime.values()) ... guesstimes sorted(xx) if guesstimes[0] 1 and len(guesstimes) 1: guesstimes guesstimes[1:] guesstimes guesstimes[0]自动分析时代码统计所有连续相同字符的“游程长度”分布取出现频次最高、且非 1 的长度作为估计的重复次数guesstimes。之后若开启“保持非重复字符”则逐字符扫描连续guesstimes个相同字符只保留 1 个若关闭则直接按line[i * guesstimes]等距取样。文档也提醒自动分析偶有不准建议手动指定确定的重数字数如该游戏固定每字符绘制 3 次。3.2 HOOK 去除重复行ABCDABCDABCD→ABCD_3适用场景游戏不逐字符重复而是整行文本快速刷新多次非反复整理而是一次性刷新多次。官方示例恵麻さんは再び液タブへ視線を落とす。恵麻さんは再び液タブへ視線を落とす。恵麻さんは再び液タブへ視線を落とす。 → 恵麻さんは再び液タブへ視線を落とす。源码实现post.pydef _3_f(line, args): times args[重复次数(若为1则自动分析去重)] if times 2: guesstimes times else: guesstimes len(line) while guesstimes 1: if line[: len(line) // guesstimes] * guesstimes line: break guesstimes - 1 line line[: len(line) // guesstimes] return line自动模式从最长可能重复次数向下尝试找到满足“开头1/N重复 N 次等于全文”的 N然后截取前1/N作为结果。同样建议对刷新次数固定的游戏直接指定次数。3.3 HOOK 去除重复行S1S1S1S2S2S2→S1S2_3_2适用场景不同句子的刷新次数不一致如第 1 句刷 3 次、第 2 句刷 2 次、第 3 句不刷只能完全交给程序分析去重。官方示例前句重复3次中句无重复后句重复2次 → 恵麻さん……ううん、恵麻ははにかむように私の名前を呼ぶ。なんてニヤしていると、恵麻さんが振り返った。私は恵麻さんの目元を優しくハンカチで拭う。源码实现post.py通过反复将文本对半长度起检测“前缀重复”模式把识别出的整段重复单元逐个剥离到缓存中最后拼接去重结果。由于场景复杂文档提示这种分析可能存在少量误差属正常现象。3.4 HOOK 去除重复行ABCDBCDCDD→ABCD_10适用场景显示文字的 HOOK 函数在每显示一个字符时都会被调用且每次参数指针向后移动导致第一次调用得到完整文本后续输出剩余子串直到长度为 0。官方示例恵麻さんは再び液タブへ視線を落とす。麻さんは再び液タブへ視線を落とす。さんは再び液タブへ視線を落とす。んは再び液タブへ視線を落とす。は再び液タブへ視線を落とす。...す。。 → 恵麻さんは再び液タブへ視線を落とす。源码实现post.py统计字符频次、从文本末尾向前追溯匹配找出最长的“真实文本”候选。3.5 HOOK 去除重复行AABABCABCD→ABCD_13EX适用场景游戏每绘制一个新字符就把前面所有已绘字符再绘制一遍前缀递增式重绘。官方示例恵麻恵麻さ恵麻さん恵麻さんは恵麻さんは再...恵麻さんは再び液タブへ視線を落とす。 → 恵麻さんは再び液タブへ視線を落とす。源码实现post.py从后向前逐步剥离“最长后缀重复”结构最后逆序拼接还原真实文本。重要提醒文档原文强调当文本有多行时该处理会每行单独按上述逻辑去重复杂度大增经常难以正确识别。如果遇到处理失败建议改用自定义 Python 处理来定制算法。四、高级自定义处理方法4.1 自定义 Python 处理_11当内置方法都不够用时可以编写 Python 脚本实现任意复杂逻辑。使用方法在文字处理设置中启用“自定义 Python 处理”点击设置按钮打开脚本文件若脚本不存在程序会自动在userconfig目录生成mypost.py及以下模板源码见 myutils/template/mypost.py生成逻辑见 myutils/utils.pydef POSTSOLVE(string: str): # 请在这里编写自定义处理 return string在POSTSOLVE函数中编写处理逻辑入参为原始文本返回值作为处理后的文本。源码实现post.pydef _mypost_process(line: str, file: str, module: str) - str: mod checkmd5reloadmodule(file, module) return mod.POSTSOLVE(line) if mod else linecheckmd5reloadmodulemyutils/utils.py会检测脚本文件的 MD5 是否变化从而在不重启程序的情况下热重载修改后的脚本——编辑保存mypost.py后无需重启即可生效非常方便调试。此外POSTSOLVE支持按游戏单独配置自定义脚本在游戏专属文本处理配置中指定posts/xxx.py详见 post.py 对savehook_new_data游戏级配置的读取逻辑。4.2 字符串替换stringreplace作用不止是替换也常用来“过滤”。例如把固定的乱码字符、反复刷新产生的倒三角字符等替换成空白来剔除。四种模式组合对应源码 myutils/utils.py 的parsemayberegexreplace正则转义escape行为关闭关闭普通字符串替换把 key 当作字面量内部会re.escape关闭开启key/value 先经过safe_escape解码如\n表示换行符再按字面量替换开启关闭按正则表达式替换开启开启先用safe_escape处理输入再按正则替换转义escape选项的意义文档强调开启转义后输入会被视为转义字符串而非字面值。例如用\n表示真正的换行符从而可以实现“仅过滤出现在换行符前后的字符”这类需求。safe_escape的实现在 myutils/utils.py本质是codecs.escape_decode。此外parsemayberegexreplace还支持几个字符串替换的高级开关可通过配置项开启whole-word整词匹配自动加\b边界与case-sensitive大小写敏感默认不敏感即默认re.IGNORECASE。合并模式字符串替换配置中若开启merge则会把全局默认替换列表与当前列表合并执行见 post.py 的string_replace便于全局规则游戏规则叠加。五、内嵌翻译与执行顺序的注意事项5.1 内嵌翻译Embed Translate下的限制文档明确提示内嵌翻译时大部分处理方法不会生效这是为了减少游戏崩溃的可能。允许使用的方法仅有 5 种它们在配置中带有isExUse: true标记過濾換行符號_6EX字串取代stringreplace自訂 Python 處理_11過濾角括號_4去除花括號{}_1这一约束在 post.py 中得到印证if not useAll and isEx and not config.get(isExUse, False): continue即在内嵌翻译模式isExTrue下凡未标记isExUse的方法都会被跳过。5.2 处理顺序postprocess_rank文档提示如果有非常複雜的錯誤形式可以透過啟用多種處理方式並調整他們的執行順序來得到豐富的處理方法組合。处理顺序由globalconfig[postprocess_rank]列表决定POSTSOLVE按该列表顺序执行每个启用的方法。不同的顺序会产生不同的结果例如“截取行数”放在“字符串替换”之前或之后其作用对象会不同例如先“去除花括号”再“Unicode 正规化”与反之对注音标记的处理路径不同。源码中postprocess_rank会与当前支持的方法集合做对齐并自动补齐新增方法post.py。用户可在设置界面拖拽调整顺序实现对复杂错误文本的“流水线式”组合清洗。5.3 游戏专属文字处理配置从 post.py 可以看到程序支持按游戏独立配置文字处理当某个游戏的textproc_follow_default为假时会读取该游戏的save_text_process_info包含rank顺序、postprocessconfig配置以及自定义mypost脚本实现“每个游戏一套处理方案、互不影响”。这对不同引擎、不同绘制方式的游戏同时游玩时非常实用。六、实战排错指南针对文档中出现的各类典型问题整理一份“症状 → 处理方案”对照表症状推荐处理方法备注文本出现エマさんԟのイԠ...这类乱码过滤非 Shift_JIS 字符仅适用日文游戏文本每字符重复多次描边/阴影效果_2去除重复字符建议指定确切重复次数整句重复 N 次_3去除重复行刷新次数固定时指定次数更稳各句重复次数不一致_3_2去除重复行S1S1S1S2S2S2可能有少量分析误差文本逐字递减子串输出_10去除重复行ABCDBCDCDD文本前缀递增式重绘_13EX去除重复行AABABCABCD复杂情况建议自定义脚本出现div等 HTML 标签过滤角括号TyranoScript 引擎常见出现{漢字/讀音}注音去除花括号{}全角英文/标点干扰翻译Unicode 正规化NFKC默认已启用多余的空白行/换行过滤换行符非日文源语言时会替换为空格固定出现的乱码片段字符串替换替换为空可配合正则、转义、整词、大小写选项极其复杂的定制清洗需求自定义 Python 处理_11保存后热重载无需重启通用建议从最小集合开始默认启用的“Unicode 正规化 _2_3”已能解决大多数常见问题遇到复杂错误时逐步叠加方法并利用顺序调整观察输出变化对重复类方法若能确定游戏的绘制次数手动指定次数通常比自动分析更可靠多行文本的复杂重复如_13EX处理失败时优先考虑编写自定义 Python 脚本每个游戏单独配置一套处理方案避免不同引擎游戏的配置互相干扰。七、相关资源索引官方文字处理文档正体中文docs/cht/textprocess.md另有 docs/zh/textprocess.md、docs/en/textprocess.md 等多语言版本处理链核心实现src/LunaTranslator/myutils/post.py默认配置与全部方法清单src/LunaTranslator/defaultconfig/postprocessconfig.json字符串替换底层解析与工具函数src/LunaTranslator/myutils/utils.py自定义 Python 处理模板src/LunaTranslator/myutils/template/mypost.py自定义 Python 处理入口封装src/LunaTranslator/transoptimi/myprocess.py文本处理在翻译流程中的调用位置src/LunaTranslator/LunaTranslator.py【免费下载链接】LunaTranslator视觉小说翻译器 / Visual Novel Translator项目地址: https://gitcode.com/GitHub_Trending/lu/LunaTranslator创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考