C#读取损坏/格式错误的txt/json文件时如何防止程序崩溃
问题描述
开发了一款可读写JSON格式txt文件的应用,常规场景运行正常,但偶尔会出现txt/json文件因未知原因损坏,导致文件读取环节程序崩溃的问题。
相关实现代码
User 类
public class User { public string UserName { get; set; } }
usersFile.txt(JSON格式正常存储内容)
[{"UserName":"someUserName"}]
读取逻辑类
public static string myUsersFolder = @"c:\myUsersFilder"; string usersFile = Path.Combine(myUsersFolder, "usersFile.txt"); public void readUsersFromFile() { try { if (!File.Exists(usersFile)) throw new FileNotFoundException();// 文件不存在时抛出异常 string jsonContent = File.ReadAllText(Path.Combine(myUsersFolder, usersFile)); List<User> users = JsonConvert.DeserializeObject<List<User>>(jsonContent); foreach (var u in users) { User user = new User(); user.UserName = u.UserName; UsersObservableCollection.Add(user); } } catch (FileNotFoundException f) { Console.WriteLine("Couldn't read users from file: " + f.Message); } }
触发的异常
'The invocation of the constructor on type 'MyProgramName.ViewModel.ViewModelLocator' that matches the specified binding constraints threw an exception.'
具体问题表现:当usersFile.txt出现格式损坏,例如缺失}花括号时(示例如下),程序会直接崩溃:
[{"UserName":"someUserName"] // 缺失右花括号,程序崩溃
核心诉求:如何识别格式错误/损坏的txt/json文件,避免程序因文件格式异常崩溃?
问题根因
现有代码崩溃的核心原因有3个:
- 异常捕获范围不全:仅捕获了
FileNotFoundException,JSON格式错误触发的JsonReaderException、反序列化结果为null触发的NullReferenceException、文件读写权限/占用触发的IOException都没有被处理,异常直接向上抛出到ViewModel构造函数,最终导致程序崩溃。 - 路径拼接错误:已经通过
Path.Combine生成了文件的完整绝对路径usersFile,读取时再次拼接myUsersFolder和usersFile,会生成非法路径,本身就会触发路径格式异常。 - 无容错机制:遇到损坏文件没有备份、重置逻辑,每次启动都会读取损坏文件重复触发崩溃。
修复方案
不建议在反序列化前单独做JSON格式校验:前置校验需要额外把整个JSON内容解析一遍,和反序列化的解析逻辑完全重复,只会增加性能开销,直接在反序列化流程中捕获异常+做容错处理即可。
具体修改点:
- 修正路径重复拼接的bug,直接使用预生成的完整文件路径读取内容
- 扩大异常捕获范围,覆盖文件操作、JSON解析全流程可能出现的所有异常
- 反序列化时配置容错规则,遇到非核心格式错误时尽量完成解析,不直接抛出异常
- 增加损坏文件兜底逻辑:解析失败时自动备份损坏文件,生成新的合法空文件,避免下次启动重复崩溃
- 遍历集合前做判空校验,跳过非法条目,避免空引用异常
修改后的完整读取代码如下:
public static string myUsersFolder = @"c:\myUsersFilder"; string usersFile = Path.Combine(myUsersFolder, "usersFile.txt"); public void readUsersFromFile() { try { // 自动创建不存在的存储目录 if (!Directory.Exists(myUsersFolder)) { Directory.CreateDirectory(myUsersFolder); } // 文件不存在时直接初始化空的合法JSON文件,无需手动抛异常 if (!File.Exists(usersFile)) { File.WriteAllText(usersFile, "[]"); return; } string jsonContent = File.ReadAllText(usersFile); // 带容错配置反序列化 List<User> users = JsonConvert.DeserializeObject<List<User>>(jsonContent, new JsonSerializerSettings { // 遇到类定义中不存在的字段时自动忽略,不抛异常 MissingMemberHandling = MissingMemberHandling.Ignore, // 遇到非核心解析错误时标记为已处理,尽量完成解析 Error = (sender, args) => { args.ErrorContext.Handled = true; } }); // 判空后再遍历,避免空引用异常 if (users != null) { foreach (var u in users) { // 跳过用户名为空的非法条目 if (string.IsNullOrWhiteSpace(u?.UserName)) continue; User user = new User(); user.UserName = u.UserName; UsersObservableCollection.Add(user); } } else { // 解析结果为空说明文件损坏,备份损坏文件后生成新的空文件 string backupPath = Path.Combine(myUsersFolder, $"usersFile_bad_{DateTime.Now:yyyyMMddHHmmss}.txt"); if (File.Exists(usersFile)) File.Move(usersFile, backupPath); File.WriteAllText(usersFile, "[]"); } } catch (Exception ex) { // 捕获所有流程异常,仅记录日志,不向上抛出 Console.WriteLine($"读取用户文件失败: {ex.Message}"); // 极端异常场景下尝试重置文件,保证下次启动正常 try { if (File.Exists(usersFile)) File.Delete(usersFile); File.WriteAllText(usersFile, "[]"); } catch { } } }
额外优化建议
- 写入JSON文件时统一使用
JsonConvert.SerializeObject生成标准格式内容,不要手动拼接JSON字符串,从写入端降低格式损坏概率。 - 如果需要明确识别JSON格式损坏场景,可以在反序列化前加一段轻量判断,捕获专门的解析异常:
try { JToken.Parse(jsonContent); } catch (JsonReaderException) { // 此处可单独记录JSON格式损坏的日志 }
内容的提问来源于stack exchange,提问作者fs_tigre
相关产品推荐
相关产品推荐

