You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在C#中解析指定格式.dat文件并映射至DTO的技术问询

如何在C#中解析特定格式的.dat文件并转换为DTO对象

我来帮你一步步解决这个问题——解析这种以关键字驱动、制表符分隔的.dat文件并转成C# DTO其实不难,我们可以分几个步骤来实现:

第一步:定义对应的DTO类

首先,我们需要创建一个DTO类来映射文件中的各个字段,根据你给出的格式,我们可以这样定义:

public class ItemDto
{
    // VNUM对应两个数值
    public (int Min, int Max) Vnum { get; set; }
    // NAME是字符串
    public string Name { get; set; } = string.Empty;
    // INDEX、TYPE、FLAG等是整数数组
    public int[] Index { get; set; } = Array.Empty<int>();
    public int[] Type { get; set; } = Array.Empty<int>();
    public int[] Flag { get; set; } = Array.Empty<int>();
    public int[] Data { get; set; } = Array.Empty<int>();
    public int[] Buff { get; set; } = Array.Empty<int>();
    // LINEDESC对应一个ID和描述字符串
    public (int Id, string Desc) LineDesc { get; set; }
}

第二步:读取文件并分割条目

你的文件中每个条目用#======================================================== 作为分隔符,我们先读取整个文件内容,然后分割成单个条目:

// 替换成你的.dat文件路径
var filePath = @"C:\your-path\items.dat";
var fileContent = File.ReadAllText(filePath);
// 定义条目分隔符
var entrySeparator = "#======================================================== ";
// 分割成单个条目,移除空条目
var entries = fileContent.Split(new[] { entrySeparator }, StringSplitOptions.RemoveEmptyEntries);

第三步:解析单个条目为DTO

接下来我们需要编写一个解析方法,把每个条目字符串转换成ItemDto对象。核心思路是:

  1. 把条目按制表符分割成一个个token
  2. 遍历token,根据关键字(VNUM、NAME等)来收集对应的值
private static ItemDto ParseEntry(string entry)
{
    var dto = new ItemDto();
    // 按制表符分割所有token,移除空项
    var tokens = entry.Split('\t', StringSplitOptions.RemoveEmptyEntries);
    int currentIndex = 0;

    while (currentIndex < tokens.Length)
    {
        switch (tokens[currentIndex])
        {
            case "VNUM":
                // VNUM后跟着2个整数
                if (currentIndex + 2 < tokens.Length
                    && int.TryParse(tokens[currentIndex+1], out var vnumMin)
                    && int.TryParse(tokens[currentIndex+2], out var vnumMax))
                {
                    dto.Vnum = (vnumMin, vnumMax);
                    currentIndex += 3; // 跳过VNUM和两个值
                }
                else
                {
                    // 解析失败时跳过当前关键字,也可以添加日志记录
                    currentIndex++;
                }
                break;
            case "NAME":
                // NAME后跟着1个字符串
                if (currentIndex + 1 < tokens.Length)
                {
                    dto.Name = tokens[currentIndex+1];
                    currentIndex += 2;
                }
                else
                {
                    currentIndex++;
                }
                break;
            case "INDEX":
            case "TYPE":
            case "FLAG":
            case "DATA":
            case "BUFF":
                // 这些关键字后跟着若干整数,直到遇到下一个关键字
                var intValues = new List<int>();
                currentIndex++; // 跳过当前关键字
                while (currentIndex < tokens.Length && !IsKeyword(tokens[currentIndex]))
                {
                    if (int.TryParse(tokens[currentIndex], out var val))
                    {
                        intValues.Add(val);
                    }
                    currentIndex++;
                }
                // 根据关键字赋值到对应的属性
                switch (tokens[currentIndex - intValues.Count - 1])
                {
                    case "INDEX": dto.Index = intValues.ToArray(); break;
                    case "TYPE": dto.Type = intValues.ToArray(); break;
                    case "FLAG": dto.Flag = intValues.ToArray(); break;
                    case "DATA": dto.Data = intValues.ToArray(); break;
                    case "BUFF": dto.Buff = intValues.ToArray(); break;
                }
                break;
            case "LINEDESC":
                // LINEDESC后跟着1个整数和1个字符串
                if (currentIndex + 2 < tokens.Length
                    && int.TryParse(tokens[currentIndex+1], out var ldId))
                {
                    dto.LineDesc = (ldId, tokens[currentIndex+2]);
                    currentIndex += 3;
                }
                else
                {
                    currentIndex++;
                }
                break;
            case "END":
                // 遇到END标记,直接结束解析
                currentIndex = tokens.Length;
                break;
            default:
                // 未知关键字,直接跳过
                currentIndex++;
                break;
        }
    }
    return dto;
}

// 辅助方法:判断当前token是否是定义的关键字
private static bool IsKeyword(string token)
{
    var keywords = new HashSet<string> 
    { 
        "VNUM", "NAME", "INDEX", "TYPE", "FLAG", "DATA", "BUFF", "LINEDESC", "END" 
    };
    return keywords.Contains(token);
}

第四步:批量解析所有条目

最后,我们遍历所有分割后的条目,调用解析方法得到完整的DTO列表:

var itemDtos = new List<ItemDto>();
foreach (var entry in entries)
{
    var item = ParseEntry(entry);
    itemDtos.Add(item);
}

// 现在itemDtos里就是所有解析完成的对象了

一些注意事项

  • 制表符确认:确保你的.dat文件确实是用**制表符(\t)**分隔字段,不是空格——你明确提到了这一点,所以解析时一定要用\t分割,不要用空格。
  • 错误处理:上面的代码做了基本的解析容错,但在实际项目中,建议添加日志记录(比如解析失败的token、条目内容),方便排查格式异常的情况。
  • 扩展性:如果以后文件格式新增了关键字,只需要扩展switch语句和IsKeyword方法中的关键字集合即可。

内容的提问来源于stack exchange,提问作者PepB

火山引擎 最新活动