【问题标题】:Read values from CSV when columns names are vertical当列名垂直时从 CSV 读取值
【发布时间】:2019-12-02 11:50:21
【问题描述】:

我有一个 CSV 助手的实现,它目前以传统格式读取 CSV:

Name, Address, Age
"Foo", "Foo's address", 24
"Bar", "Bar's address", 19

我使用类映射按名称将字段映射到我的 Person 对象,方法如下:

using (var reader = new StreamReader(file, Encoding.UTF8))
{
    using (var csvReader = new CsvReader(reader))
    {
        csvReader.Configuration.RegisterClassMap<ContentMapper>();
        var records = csvReader.GetRecords<Person>().ToArray();
    }
}

我需要一个实现,它读取完全相同的数据,但采用垂直格式,该格式源自列名在第一列中是垂直的数据,并且数据在列而不是行中跟随。

Name, "Foo", "Bar"
Address, "Foo's address", "Bar's address"
Age, 24, 19

在保留原始映射的同时处理这种格式的 CSV 的最佳方法是什么?

【问题讨论】:

  • csv中的垂直格式应该是无关紧要的,它是分隔字段的逗号,而不是列位置
  • 正是问题所在。我的类映射失败,因为“Foo”和“Bar”被视为列名(与映射不匹配)。
  • 嗯,你需要知道以这种方式处理它的东西,而传统上这不是 csv 文件的工作方式
  • 如果您打算使用 csvhelper,在将 csvhelper 逻辑应用到它之前先将源数据转换为预期格式可能会有所帮助。
  • 我没有时间写出正确的答案,但我建议阅读每一行,然后使用 GetField 对其进行迭代,将结果存储在一个列表中。当你读完所有行后,遍历列表,写出第一个元素,然后是第二个等。这应该会给你一个“标准”的 csv,你可以正常读入

标签: c# csv csvhelper


【解决方案1】:

试试这个

public class Person
{
    public string Name { get; set; }
    public string Address { get; set; }
    public int Age { get; set; }


    public List<Person> ReadFile(string path)
    {
        char[] charsToTrim1 = {'\\', ' ', '"', '\"'};

        var fileData = File.ReadAllLines(path);
        var outputData = new List<Person>();

        for (var i = 0; i < fileData.Length; i++)
        {
            var tmpData = fileData[i].Split(',');

            for (var j = 0; j < tmpData.Length; j++)
            {
                var t1 = tmpData[j].Trim(charsToTrim1);
                if (j == 0)
                    continue;
                switch (i)
                {
                    case 0:
                    {
                        var tmPerson = new Person {Name = t1};
                        outputData.Add(tmPerson);
                    }
                        break;
                    case 1:
                    {
                        outputData[j - 1].Address = t1;
                    }
                        break;
                    case 2:
                    {
                        outputData[j - 1].Age = Convert.ToInt32(t1);
                    }
                        break;
                }
            }
        }


        return outputData;
    }
}

【讨论】:

  • 您在此处的回答可能适用于原始示例,但它过于具体,无法回答问题。它不能正确处理字段转义 - 即本身包含逗号的字段。
  • 哪些字段可以包含逗号?年龄 ?像16,5?或名称 Al,前?或地址 91 Pumpkin Hill Street Seattle WA 98144
  • 我说的是一般情况。应该清楚 OP 只是使用该数据作为示例,否则他们只会手动修复它;-) CSV 数据可以包含逗号、引号和其他特殊字符。如果你想了解更多en.wikipedia.org/wiki/Comma-separated_values是一个很好的解释(见第二段)
【解决方案2】:

这可能会被清理一下,但它似乎确实有效。

  • 使用 CsvHelper 将记录拉入为List&lt;dynamic&gt;
  • 将记录轮换成新的List&lt;dynamic&gt;,这样每行中的第一个字段就成为动态记录的属性名称。
  • 使用 CsvHelper 将新列表写入内存。
  • 使用 CsvHelper 使用您的 ClassMap 读回记录
public class Program
{
    public static void Main(string[] args)
    {
        var flippedRecords = new List<dynamic>();

        using (MemoryStream stream = new MemoryStream())
        using (StreamWriter writer = new StreamWriter(stream))
        using (StreamReader reader = new StreamReader(stream))
        using (CsvReader csv = new CsvReader(reader))
        {
            writer.WriteLine("Name,Foo,Bar");
            writer.WriteLine("Address,Foo's address,\"Bar's address with, comma\"");
            writer.WriteLine("Age,24,19");
            writer.Flush();
            stream.Position = 0;

            csv.Configuration.HasHeaderRecord = false;

            // Get the records from the CSV file.
            var records = csv.GetRecords<dynamic>().ToList();

            // Rotate the records into a new dynamic list.
            var rows = new List<IDictionary<string, object>>();                

            foreach (var row in records)
            {
                rows.Add(row as IDictionary<string, object>);
            }

            for (int i = 2; i <= rows[0].Count; i++)
            {
                var flippedRecord = new ExpandoObject() as IDictionary<string, object>;

                foreach (var row in rows)
                {
                    flippedRecord.Add((string)row["Field1"], row["Field" + i]);
                }

                flippedRecords.Add(flippedRecord);
            }
        }

        using (MemoryStream stream = new MemoryStream())
        using (StreamWriter writer = new StreamWriter(stream))
        using (CsvWriter csvWriter = new CsvWriter(writer))
        using (StreamReader reader = new StreamReader(stream))
        using (CsvReader csvReader = new CsvReader(reader))
        {
            // Write the new list to memory
            csvWriter.WriteRecords(flippedRecords);
            writer.Flush();
            stream.Position = 0;

            // Read in the person records using a ClassMap.
            csvReader.Configuration.RegisterClassMap<PersonMap>();

            var people = csvReader.GetRecords<Person>().ToArray();
        }
    }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }
    public int Age { get; set; }
}

public class PersonMap : ClassMap<Person>
{
    public PersonMap()
    {
        Map(m => m.FirstName).Name("Name");
        Map(m => m.Address);
        Map(m => m.Age);
    }
}

【讨论】:

    猜你喜欢
    • 2020-10-10
    • 2015-06-18
    • 1970-01-01
    • 2011-08-24
    • 2017-07-08
    • 2017-10-20
    • 2020-05-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多