使用CsvHelper编写CSV文件时的自定义转换

9

最近我一直在进行CSV读写操作,发现了一个非常棒的工具CsvHelper。但是我遇到了一个小问题,我使用自定义转换器读取文件,但是当我将它们写回去时,格式就丢失了。我的CSV格式如下:

67,1234-1,20150115,750,20150115,1340,549,549,406,0,FRG

字段 20150115,750 映射到一个称为Start的单个DateTime字段(即,01/15/2015 7:50 AM)。我的类映射如下:

public sealed class DutyMap : CsvClassMap<Duty>
{
    static readonly CultureInfo enUS = new CultureInfo("en-US");

    public DutyMap()
    {
        Map(m => m.PersonId).Index(0);
        Map(m => m.DutyName).Index(1);
        Map(m => m.Start).ConvertUsing(row => ParseDate(row.GetField<String>(2), row.GetField<String>(3)));
        Map(m => m.End).ConvertUsing(row => ParseDate(row.GetField<String>(4), row.GetField<String>(5)));
        Map(m => m.DutyTime1).Index(6);
        Map(m => m.DutyTime2).Index(7);
        Map(m => m.FlightTime).Index(8);
        Map(m => m.CreditHours).Index(9);
        Map(m => m.DutyType).Index(10);
    }

    private static DateTime ParseDate(string date, string time)
    {
        DateTime ret;

        if (time.Length < 4)
            time = new String('0', 4 - time.Length) + time;

        if (!DateTime.TryParseExact(date + time, "yyyyMMddHHmm", enUS, DateTimeStyles.None, out ret))
            throw new FormatException(String.Format("Could not parse DateTime.  Date: {0} Time: {1}", date, time));

        return ret;
    }
}

这很好用,现在我可以像这样调用解析整个文件:
var csv = new CsvReader(sr);
csv.Configuration.HasHeaderRecord = false;
csv.Configuration.RegisterClassMap<DutyMap>();

Data = csv.GetRecords<Duty>().ToList();

然而,当我写入文件时:

csv.WriteRecords(Data);

文件的写出形式如下:
67,7454-1,1/15/2015 7:50:00 AM,1/15/2015 1:40:00 PM,549,549,406,0,FPG

阅读文档后,我并没有找到可以在写入时指定对话函数的方法,只有在读取时可以。目前我找到的唯一解决方案是手动逐条记录。

var csv = new CsvWriter(sw);
foreach (var item in Data)
{
    csv.WriteField(item.PersonId);
    csv.WriteField(item.DutyName);
    csv.WriteField(item.Start.ToString("yyyyMMdd"));
    csv.WriteField(item.Start.ToString("Hmm"));
    csv.WriteField(item.End.ToString("yyyyMMdd"));
    csv.WriteField(item.End.ToString("Hmm"));
    csv.WriteField(item.DutyTime1);
    csv.WriteField(item.DutyTime2);
    csv.WriteField(item.FlightTime);
    csv.WriteField(item.CreditHours);
    csv.WriteField(item.DutyType);

    csv.NextRecord();
}

有更好的方法吗?谢谢!

2个回答

18

您可以使用自定义类型转换器 (https://github.com/JoshClose/CsvHelper/wiki/Custom-TypeConverter) 来完成。只需覆盖 ConvertToString 方法即可。

public class TestConverter : DefaultTypeConverter
{
    public override string ConvertToString(TypeConverterOptions options, object value)
    {
        return base.ConvertToString(options, value);
    }
}

并在映射时指定转换器:

Map(m => m.PersonId).Index(0).TypeConverter<TestConverter>();

谢谢!我开始看了一下,但是文档有点简略。我会继续深入研究的! - Mike Christensen
哦,这个能让我在CSV文件中将一个字段(“开始”)拆分成两个不同的字段吗?记住,我必须将日期部分和时间部分分开。 - Mike Christensen
2
只需使用多个不同列名的Map语句: Map(m => m.PersonId).Index(0).Name("col1"); Map(m => m.PersonId).Index(0).Name("col2"); - Daniel Luberda
1
我必须使用这段代码: public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData) - Vasily Hall

0
如果您在这里寻找一种将双精度浮点数转换为往返兼容格式的方法,您可以使用下面的代码,它使用G17格式
using (var writer = new StreamWriter("C:/path..."))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
    csv.Context.TypeConverterOptionsCache.AddOptions<double?>(new TypeConverterOptions 
    { 
        Formats = new string[] { "G17" } 
    });

    csv.WriteRecords(collection);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接