// 2026-04-24 | test_json_import_fix.dart | 鸿蒙端JSON导入解析失败诊断脚本 // 诊断文件: 小妈厨房 - 数据导出.json // 错误: FormatException: Unexpected character (at line 116, character 2) import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; void main() async { final filePath = r'e:\Program Files (x86)\WeGame\apps\2821981550\FileRecv\小妈厨房 - 数据导出.json'; final file = File(filePath); if (!await file.exists()) { print('❌ 文件不存在: $filePath'); return; } final bytes = await file.readAsBytes(); print('📁 文件大小: ${bytes.length} bytes'); _checkBom(bytes); _checkEncoding(bytes); _checkLine116(bytes); _tryParseWithFixes(bytes); } void _checkBom(Uint8List bytes) { print('\n=== BOM 检测 ==='); if (bytes.length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) { print('✅ 检测到 UTF-8 BOM (EF BB BF) 在位置 0'); } else if (bytes.length >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE) { print('✅ 检测到 UTF-16 LE BOM (FF FE)'); } else if (bytes.length >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF) { print('✅ 检测到 UTF-16 BE BOM (FE FF)'); } else { print('❌ 未检测到 BOM'); } if (bytes.isNotEmpty) { print('前20字节 hex: ${bytes.take(20).map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}'); print('前20字节 char: ${bytes.take(20).map((b) => b >= 32 && b < 127 ? String.fromCharCode(b) : '.').join()}'); } } void _checkEncoding(Uint8List bytes) { print('\n=== 编码检测 ==='); try { final utf8Str = utf8.decode(bytes, allowMalformed: true); print('UTF-8 解码成功,长度: ${utf8Str.length}'); print('前100字符: ${utf8Str.substring(0, utf8Str.length.clamp(0, 100))}'); final hasBomChar = utf8Str.startsWith('\uFEFF'); if (hasBomChar) { print('⚠️ 字符串以 BOM 字符 \\uFEFF 开头'); } final nullCount = '\x00'.allMatches(utf8Str).length; if (nullCount > 0) { print('⚠️ 发现 $nullCount 个 null 字节 (\\x00)'); } } catch (e) { print('❌ UTF-8 解码失败: $e'); } try { final latin1Str = latin1.decode(bytes); print('Latin-1 解码成功,长度: ${latin1Str.length}'); } catch (e) { print('❌ Latin-1 解码失败: $e'); } } void _checkLine116(Uint8List bytes) { print('\n=== 第116行诊断 ==='); try { final content = utf8.decode(bytes, allowMalformed: true); final lines = content.split('\n'); print('总行数: ${lines.length}'); if (lines.length >= 116) { final line115 = lines.length > 115 ? lines[115] : '<不存在>'; final line116 = lines.length > 116 ? lines[116] : '<不存在>'; final line117 = lines.length > 117 ? lines[117] : '<不存在>'; print('第115行 (${line115.length}字符): ${line115.substring(0, line115.length.clamp(0, 200))}'); print('第116行 (${line116.length}字符): ${line116.substring(0, line116.length.clamp(0, 200))}'); print('第117行 (${line117.length}字符): ${line117.substring(0, line117.length.clamp(0, 200))}'); if (line116.length >= 2) { final charAt1 = line116.codeUnitAt(0); final charAt2 = line116.codeUnitAt(1); print('第116行字符1(位置0): U+${charAt1.toRadixString(16).padLeft(4, "0")} = ${_charInfo(charAt1)}'); print('第116行字符2(位置1): U+${charAt2.toRadixString(16).padLeft(4, "0")} = ${_charInfo(charAt2)}'); if (charAt2 < 32 || charAt2 > 126) { print('⚠️ 第116行第2个字符是非ASCII/控制字符!'); } } print('\n第116行所有字符的码点:'); for (var i = 0; i < line116.length.clamp(0, 50); i++) { final c = line116.codeUnitAt(i); if (c < 32 || c > 126) { print(' 位置$i: U+${c.toRadixString(16).padLeft(4, "0")} ${_charInfo(c)} ⚠️'); } } } else { print('⚠️ 文件不足116行'); } } catch (e) { print('❌ 行分析失败: $e'); } } String _charInfo(int codeUnit) { if (codeUnit == 0) return 'NULL'; if (codeUnit == 0xFEFF) return 'BOM (ZERO WIDTH NO-BREAK SPACE)'; if (codeUnit == 9) return 'TAB'; if (codeUnit == 10) return 'LF'; if (codeUnit == 13) return 'CR'; if (codeUnit < 32) return '控制字符'; if (codeUnit <= 126) return "'${String.fromCharCode(codeUnit)}'"; if (codeUnit <= 255) return 'Latin-1扩展'; return 'Unicode ${String.fromCharCode(codeUnit)}'; } void _tryParseWithFixes(Uint8List bytes) { print('\n=== 尝试各种修复方案解析 ==='); try { var content = utf8.decode(bytes, allowMalformed: true); print('\n方案1: 原始 utf8.decode(allowMalformed: true)'); try { jsonDecode(content); print(' ✅ JSON 解析成功!'); } catch (e) { print(' ❌ 失败: $e'); } print('\n方案2: 移除 BOM 字符'); try { final trimmed = content.replaceFirst(RegExp('^\\uFEFF'), ''); jsonDecode(trimmed); print(' ✅ JSON 解析成功!(移除BOM后)'); } catch (e) { print(' ❌ 失败: $e'); } print('\n方案3: 移除所有控制字符(保留\\n\\r\\t)'); try { final cleaned = content.replaceAll(RegExp(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]'), ''); jsonDecode(cleaned); print(' ✅ JSON 解析成功!(移除控制字符后)'); } catch (e) { print(' ❌ 失败: $e'); } print('\n方案4: trim 后解析'); try { jsonDecode(content.trim()); print(' ✅ JSON 解析成功!(trim后)'); } catch (e) { print(' ❌ 失败: $e'); } print('\n方案5: 完整清理流程 (推荐)'); try { String cleaned = content; cleaned = cleaned.replaceFirst(RegExp('^\\uFEFF'), ''); cleaned = cleaned.replaceAll(RegExp(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]'), ''); cleaned = cleaned.trim(); final result = jsonDecode(cleaned); print(' ✅ JSON 解析成功!'); if (result is Map) { print(' 📊 顶层 keys: ${result.keys.toList()}'); if (result.containsKey('_meta')) { print(' 📋 _meta: ${result["_meta"]}'); } for (final key in result.keys) { if (key == '_meta') continue; final val = result[key]; if (val is List) { print(' 📦 $key: ${val.length} 条数据'); } else if (val is Map) { print(' 📦 $key: Map with ${val.length} entries'); } } } else if (result is List) { print(' 📦 顶层是 List: ${result.length} 条数据'); } } catch (e) { print(' ❌ 失败: $e'); } } catch (e) { print('❌ UTF-8 解码失败: $e'); } }