3d
This commit is contained in:
202
scripts/test_json_import_fix.dart
Normal file
202
scripts/test_json_import_fix.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user