首页 > 解决方案 > FileHelper 在 CSV 中读取,其中初始行指定列名

问题描述

对于上下文,我正在尝试解析 7dtd 本地化文件。该文件的内容类似于以下内容:

Key,File,Type,UsedInMainMenu,NoTranslate,english,Context / Alternate Text,german,latam,french,italian,japanese,koreana,polish,brazilian,russian,turkish,schinese,tchinese,spanish
meleeToolCrowbar,items,Tool,,,Crowbar,,,,,,,,,,,,,,

问题是,呈现的数据顺序因文件而异,其中第一行指定文件中每个条目的项目位置。我需要读入这个文件并将其与多个其他类似结构的文件合并。

我最初尝试只读入并使用逗号上的拆分字符串进行解析,但是有些字段中可能有逗号(以引号为界)。因此,当我开始研究潜在的解决方案时,FileHelpers 出现了。但是,据我所知,这使用静态列来定义属性,这不适用于我的上下文,因为 a) 列的顺序不同,b) 并非所有列都存在。

任何帮助找到解决方案将不胜感激。

标签: c#filehelpers

解决方案


如果您查看库的动态ClassBuilder部分,您可以通过添加字段来创建一个在列和您想要的类之间映射的类。然后使用CreateRecordClass对象创建运行时引擎要使用的实际类类型。

您需要解析第一行,然后计算出您拥有的列。如果您不指定任何列或类型,我相信您可以使用分隔引擎为您完成这项工作。

使用 FileHelpers 比使用 CSVHelper 更复杂,您可以在其中以简单的方式创建映射。如果您使用 CsvDataReader,那么您可以使用ClassMap在列标题和属性之间移动,甚至为您实际想要的列指定特定索引

void Main()
{
    var config = new CsvConfiguration(CultureInfo.InvariantCulture)
    {
        HasHeaderRecord = false,
    };
    using (var reader = new StreamReader("path\\to\\file.csv"))
    using (var csv = new CsvReader(reader, config))
    {
        csv.Context.RegisterClassMap<FooMap>();
        csv.Context.RegisterClassMap<BarMap>();
        var fooRecords = new List<Foo>();
        var barRecords = new List<Bar>();
        while (csv.Read())
        {
            switch (csv.GetField(0))
            {
                case "A":
                    fooRecords.Add(csv.GetRecord<Foo>());
                    break;
                case "B":
                    barRecords.Add(csv.GetRecord<Bar>());
                    break;
                default:
                    throw new InvalidOperationException("Unknown record type.");
            }
        }
    }
}

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Bar
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

public sealed class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        Map(m => m.Id).Index(1);
        Map(m => m.Name).Index(2);
    }
}

public sealed class BarMap : ClassMap<Bar>
{
    public BarMap()
    {
        Map(m => m.Id).Index(1);
        Map(m => m.Name).Index(2);
    }
}

推荐阅读