You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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内容解析一遍,和反序列化的解析逻辑完全重复,只会增加性能开销,直接在反序列化流程中捕获异常+做容错处理即可。
具体修改点:

  1. 修正路径重复拼接的bug,直接使用预生成的完整文件路径读取内容
  2. 扩大异常捕获范围,覆盖文件操作、JSON解析全流程可能出现的所有异常
  3. 反序列化时配置容错规则,遇到非核心格式错误时尽量完成解析,不直接抛出异常
  4. 增加损坏文件兜底逻辑:解析失败时自动备份损坏文件,生成新的合法空文件,避免下次启动重复崩溃
  5. 遍历集合前做判空校验,跳过非法条目,避免空引用异常

修改后的完整读取代码如下:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 00:45:31