This commit is contained in:
Developer
2026-04-25 01:18:50 +08:00
parent 7d5d95d5e0
commit 3c90407bb5
193 changed files with 32355 additions and 2284 deletions

View File

@@ -0,0 +1,202 @@
// 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<String, dynamic>) {
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');
}
}