大模型手搓文件对比工具(7)乱码解决了,换行也不乱

大模型手搓文件对比工具(7)乱码解决了,换行也不乱
上一期把差异块操作做完后开发计划进入 P1编码与换行保真。这个功能看上去没有差异算法那么直观。文件能打开文字能修改点击保存也没有报错很容易让人觉得编辑器已经够用了。但旧版一直有个隐患所有文本都按 UTF-8 读取再按 UTF-8 写回。遇到普通 UTF-8 文件当然没问题。遇到 GBK、GB18030 或 UTF-16轻一点是打开后一片乱码重一点是用户没看出来编码已经变了保存后把原文件直接改坏。换行也一样Windows 项目里的 CRLF 可能被统一成 LF文件内容没变Git 却显示整份文件都被修改。所以这一期不加新按钮先把文件读写这条链路补完整打开时识别编码判断不准就让人确认保存时保持左右两侧各自的编码、BOM 和换行不做任何静默转换。先拿几个真实文件试一下正式改代码前我准备了几类测试文件UTF-8 和 UTF-8 BOM。UTF-16 LE、UTF-16 BE包含带 BOM 和无 BOM 两种情况。GB18030、GBK、GB2312 中文文件。左侧 CRLF、右侧 LF。同一个文件里同时存在 CRLF、LF 和 CR。最后一行有换行和没有换行。旧版的问题马上就暴露出来了。UTF-16 文件里经常带有0x00字节而旧版二进制判断只要看到 NUL 就认为这不是文本。结果一个正常的 UTF-16 配置文件还没有进入编码识别就先被挡在编辑器外面。另外GB18030 和 GBK 并没有像 UTF-8 BOM 那样明确的文件头。短文本可能同时满足多种编码的解码规则程序即使猜出一个结果也不能把“猜测”包装成“确定”。这两个问题决定了编码功能不能只加一句Charset.forName(detectedName)识别顺序、可信程度、用户确认和保存策略都要一起设计。能确定的自动打开不能确定的先看内容这次把识别结果分成了几种情况。1. BOM 可以直接确认文件开头如果出现下面这些字节编码基本没有歧义字节头编码EF BB BFUTF-8 BOMFF FEUTF-16 LE BOMFE FFUTF-16 BE BOMBOM 不交给正文和差异算法但会单独保存下来。以后写文件时再按原样加回去。2. UTF-8 必须严格校验Java 默认解码遇到非法字节时可能使用替换字符继续执行。编辑器里看到的黑色菱形或问号很多时候就是这样来的。这次改成CharsetDecoder的严格模式decoder.onMalformedInput(CodingErrorAction.REPORT);decoder.onUnmappableCharacter(CodingErrorAction.REPORT);整份文件可以严格解码并且内容看起来像正常文本才把它作为可靠 UTF-8 自动打开。3. UTF-16 要排在二进制判断前面无 BOM 的 UTF-16 不能直接确认但英文和数字文本通常会在奇数位或偶数位出现规律性的0x00。程序先检查这种字节分布再使用 UTF-16 LE 或 BE 严格解码验证。只有 BOM、UTF-16 候选和文本编码候选都失败并且控制字节比例明显异常时才提示文件可能是二进制。这样正常 UTF-16 文件不会再因为 NUL 字节被提前拒绝。4. 传统中文编码只给建议GB18030、GBK、GB2312 和 Big5 使用juniversalchardet 2.4.0辅助判断。这个库负责提供候选程序再逐个执行严格解码和文本合理性检查。统计检测的结果仍然只是建议。特别是文件很短时GBK 和 GB18030 都可能解出看似正常的中文。对这类结果编辑器不会直接放行而是打开预览窗口。左边选择编码右边实时显示内容。看起来正常再点确认某个候选无法完整解码时直接显示错误不拿替换字符凑出一份“差不多能看”的预览。“按另一种编码打开”和“转换编码”必须分开做 UI 方案时我特意把两个容易混淆的操作拆开了。重新按其他编码打开处理的是“刚才读错了”。程序重新读取磁盘上的原始字节按用户选择的编码解释不写文件。如果当前有未保存修改先提示放弃修改。转换并保存编码处理的是“我就是要改文件格式”。程序拿当前已经正确显示的文字使用目标编码重新写入文件。操作前显示当前编码、目标编码、换行状态和文件路径还要再确认一次。这两个操作如果只做成一个编码下拉框使用者很难知道点完以后只是重新读取还是已经把磁盘文件改了。对文件工具来说这种含糊比多一个菜单更危险。最终编码名称放在左右文件标题里。点击左侧编码只操作左侧点击右侧编码只操作右侧。左右文件可以一个是 GB18030另一个是 UTF-8 BOM保存后仍然各用各的格式。换行不能只记一个全局值编码解决以后还有换行。最简单的做法是读取文本时判断它主要使用 LF 还是 CRLF然后保存时把所有行统一成这个格式。普通文件看起来没问题但遇到混合换行就会改变大量字节。这次继续改造原来的LineDocument让每一行都记录自己的结尾第 1 行namecompare CRLF 第 2 行statusrunning LF 第 3 行message完成 NONENONE表示最后一行没有换行。它不是 LF也不是一个可以随手补上的空字符。修改一行文字时只替换文字不动该行原来的结尾。新插入的行使用这个文件最常见的换行方式。保存时把“行内容 原结尾”逐行拼回去因此 CRLF、LF、单独 CR、混合换行和末尾无换行都能保留。标题旁边会显示CRLF、LF、CR或“混合换行”。鼠标放上去还能看到每种换行的数量以及文件末尾是否有换行。差异块复制时到底听谁的格式这一期和上一期的差异块操作有直接关系。假设左侧文件是 GB18030 CRLF右侧文件是 UTF-8 BOM LF。现在把左侧一处差异复制到右侧右侧应该变成什么格式最终规则是复制的是文字内容不复制已有目标文件的编码。目标文件已经存在时继续使用目标侧编码、BOM 和首选换行。目标侧原有行能复用的换行继续保留。新增到目标侧的行使用目标侧首选换行。目标文件原本不存在时才继承来源文件的编码、BOM 和换行。也就是说把 GB18030 左侧的一段中文复制到 UTF-8 BOM 右侧右侧仍然是 UTF-8 BOM。用户想把右侧也转成 GB18030需要单独执行“转换并保存编码”。这条边界必须明确否则每次点击差异箭头都有可能顺便改变整个文件格式。保存时不能用问号糊弄过去读取需要严格解码保存也一样。例如在 UTF-8 文件里输入一个 Emoji再选择转换为 GBK。GBK 无法表示这个字符。如果直接调用常规编码方法某些写法会用?替代然后照常保存。用户直到下次打开文件才发现内容已经丢了。现在所有保存都先在内存中使用CharsetEncoder完整编码并把不可映射字符设置为REPORT。只要有一个字符无法表示整次保存就停止磁盘文件不动。“全部保存”还会先检查左右两侧。任意一侧编码失败两侧都不写避免左边保存成功、右边失败后留下半套状态。普通保存的顺序变成了按每行记录的结尾重建完整文本。使用当前侧原编码严格编码。编码全部成功后加回原 BOM。写入磁盘。更新已保存快照和界面状态。写文件之前多做一次完整检查成本不高但能避免无法恢复的字符丢失。实现时没有照着方案机械加类方案阶段列过TextFileDocument、LineEndingParser、EncodingConversionService等对象。真正开始改代码后没有把每个名字都变成一个新文件。当前项目规模不大原来的LineDocument已经负责行内容和差异块修改。直接把逐行换行信息扩展进去比再加一层包装更清楚。最终主要职责是TextEncodingDetectorBOM、UTF-8、UTF-16 和传统中文编码候选判断。FileEncoding记录字符集、BOM、可信级别和识别来源。TextFileCodec严格解码、预览、编码和写入。TextFileSnapshot保存某一侧的原始字节、编码和文档状态。LineDocument保存每行文字、每行换行和首选换行。DiffEditorFrame负责确认窗口、编码菜单、转换确认和保存交互。方案的作用是先划清职责不是强迫代码最后一定有多少个类。能在现有模型里讲清楚的逻辑没有必要为了结构图再套一层。最终运行效果下面这张真实截图里左侧是 UTF-16 LE BOM CRLF右侧是 UTF-8 BOM 混合换行。两边编码和换行状态分别显示差异块按钮、占位行、保存和重新加载仍然正常。普通编辑不会把左侧转成 UTF-8也不会把右侧的混合换行统一掉。传统中文编码也做了单独验证。下面是左侧 GB18030、右侧 UTF-8 的真实运行结果GB18030 文件第一次打开时先经过“推测编码、内容预览、用户确认”确认后中文能够正常进入编辑器。保存后重新读取文字、编码和 CRLF 都保持不变。真实窗口检查还发现了一个字体问题。代码区使用等宽字体时中文在部分 Windows 环境中会显示成方框。最后把代码字体改为 Java 的逻辑等宽字体让系统负责回退中文字库占位提示继续使用微软雅黑。这个问题单元测试检查不到只能启动 Swing 看实际效果。这次实际修改了哪些内容1. 编码识别支持 UTF-8、UTF-8 BOM。支持 UTF-16 LE/BE包含 BOM 和明显的无 BOM 文件。支持 GB18030、GBK、GB2312 候选。Big5 可作为检测候选和手动选择。UTF-16 判断提前到普通二进制判断之前。传统编码低可信时必须先预览确认。2. 文件格式保真左右文件分别记录编码和 BOM。每一行单独保存 LF、CRLF、CR 或无换行。保留混合换行和文件末尾换行状态。差异块复制保留已有目标文件格式。目标文件不存在时继承来源格式。3. 编辑器交互左右标题分别显示编码和换行状态。支持重新按其他编码读取原始字节。支持明确转换编码并保存。未保存状态下重新读取会先确认。目标编码无法表示当前字符时阻止保存。4. 构建和依赖增加juniversalchardet 2.4.0。更新运行类路径和第三方依赖说明。继续兼容 Java 8。5. 测试BOM 和无 BOM 编码识别。UTF-16 NUL 字节判断。GB18030 中文预览与保存。UTF-8 BOM、UTF-16、GB18030 字节往返。GBK 无法表示字符时拒绝保存。LF、CRLF、CR、混合换行和末尾无换行。差异块复制后的目标侧换行规则。原有差异算法、差异块应用、行对齐、过滤规则全部回归。真实 Swing 窗口和中文字体检查。最终一共跑了 7 组回归测试全部通过。测试不只比较 Java 字符串还会重新读取写出的字节确认 BOM、中文内容和换行没有在保存后变化。最终版提示词如果需要给一个现有 Java Swing 文本工具补上同类能力可以直接使用下面这份提示词请继续开发一个现有的 Java 8 Swing 文件对比工具。工具已经支持文件和目录 SHA-256 对比、可折叠目录树、过滤规则、左右同步、F5 刷新、差异块级 双向操作、撤销、重新加载和分别保存。 本次实现 P1“编码与换行保真”。目标是让左右文件可以使用不同编码和换行 普通编辑、差异块复制与保存都不能静默改变文件格式或丢失字符。 功能要求 1. 支持 UTF-8、UTF-8 BOM、UTF-16 LE/BE、GB18030、GBK、GB2312 Big5 作为检测候选和手动选择。 2. 编码识别按 BOM、严格 UTF-8、UTF-16 字节分布、统计检测的顺序执行。 3. UTF-16 识别必须早于普通 NUL 二进制判断避免把 UTF-16 文本误判为二进制。 4. 统计检测只能提供候选。传统中文编码或低可信结果必须显示内容预览 由用户确认后才能进入可编辑状态。 5. 所有解码使用 CharsetDecoder 严格模式非法输入和不可映射字符必须报错 不能静默替换。 6. 左右两侧分别保存 Charset、BOM、识别来源、可信状态和原始字节 不能使用一个全局编码覆盖两侧。 7. 每一行分别记录 LF、CRLF、CR 或无换行支持混合换行和末尾无换行保真。 8. 修改已有行不能改变原换行新增行使用目标文件首选换行。 9. 差异块复制默认只复制文字。目标文件存在时保留目标编码、BOM 和换行 目标文件不存在时才继承来源文件格式。 10. 在左右文件标题中分别显示编码和换行状态混合换行要有明显状态和统计提示。 11. 严格区分“重新按其他编码打开”和“转换并保存编码”前者重新解释 磁盘原始字节且不写文件后者转换当前文本并明确写盘。 12. 当前有未保存修改时重新按编码打开必须先确认放弃修改。 13. 编码转换前显示当前编码、目标编码、BOM、换行状态、操作侧和文件路径 并明确另一侧不会改变。 14. 所有保存使用 CharsetEncoder 严格模式并先在内存完成编码。 目标编码无法表示任何字符时阻止写入不能替换成问号。 15. 全部保存必须先校验左右两侧任意一侧编码失败时两侧均不写入。 16. 保留现有差异块操作、撤销、占位行、联动滚动、重新加载和未保存提示。 17. 编码检测、编解码、文件格式元数据和 Swing 交互要分开不把检测逻辑 直接堆进窗口事件代码。 18. 引入第三方检测库前检查 Java 8 兼容性、许可证、运行时依赖和离线分发方式 并更新启动脚本及第三方声明。 19. 增加回归测试至少覆盖 UTF-8 BOM、UTF-16 LE/BE、GB18030、GBK、 纯 ASCII、空文件、非法 UTF-8、NUL、不可映射字符、混合换行和末尾无换行。 20. 增加字节级往返测试文件不修改直接保存后编码、BOM、文字和换行保持一致。 21. 完成后编译全部源码和测试运行真实 Swing 窗口使用中文 GB18030、 UTF-16 BOM 和混合换行文件检查字体、预览、保存和左右独立状态。 修改前先阅读现有代码和开发计划给出执行计划、数据模型、识别顺序、 保存语义和 UI 效果图。我确认后再修改正式代码。实现完成后更新 README、 开发计划、第三方依赖说明并生成真实运行截图。下一步按照开发计划下一项是 P1“同步预览、备份和失败恢复”。目前目录同步可以把不同文件复制到另一侧但点击以后就直接执行。真正放到项目目录里使用还需要在写盘前列出新增、覆盖和跳过的文件让人确认方向和目标根目录重要文件覆盖前可以选择备份某一项失败后也要知道哪些已经完成、哪些没有执行。编码保真解决的是“保存时别把文件格式改坏”同步安全解决的是“批量覆盖前先让我看清楚”。这两项补齐后工具才更适合处理真实项目目录。计划里后面还有扫描进度与取消、对比历史缓存、过滤规则预设和发布打包。顺序已经写进开发计划不再临时看到一个小问题就改变主线。最后这一期没有增加一个特别显眼的大功能但它处理的是文件工具最不能含糊的地方。乱码还能在打开时发现静默改编码、统一换行或用问号替换字符往往要到提交代码、部署配置甚至下次打开文件时才会暴露。到那时原来的字节和格式可能已经找不回来了。现在左右两侧可以各用各的编码和换行程序判断不准时会停下来让人确认保存前也会先检查是否能完整编码。它还不支持所有地区编码检测短文本也不可能百分之百准确但至少不会装作自己一定猜对了。目前整体使用体验仍然有不少需要优化的地方。同步缺少预览和备份大目录没有进度与取消常用对比任务也还不能直接从历史记录恢复。后面继续按开发计划逐项补不急着把功能清单写得很长先把每一次文件读写做稳。