我正要执行,以产生一个Excel友好的格式写入到一个输出文件,这将在后面拾取和处理上的特定业务类的ToString()的重写。下面是该数据应该是这样的:

5555555 "LASTN SR, FIRSTN"  5555555555  13956 STREET RD     TOWNSVILLE  MI  48890   25.88   01-003-06-0934

这没什么大不了的,我只是做一个格式字符串和覆盖ToString(),但会改变ToString()的行为,我决定序列化这样的任何物体,使得ToString()执行所有衣衫褴褛跨库。

现在,我已经在的IFormatProvider 读了,而一个类实现这听起来像一个好主意,但我还是有点困惑,所有这样的逻辑应该存在以及如何建立格式化程序类。

做什么,当你需要做一个CSV,制表符分隔或其他一些非XML任意字符串出对象的,你们怎么办?

有帮助吗?

解决方案

下面是一种通用的方式用于从对象列表创建CSV,使用反射:

    public static string ToCsv<T>(string separator, IEnumerable<T> objectlist)
    {
        Type t = typeof(T);
        FieldInfo[] fields = t.GetFields();

        string header = String.Join(separator, fields.Select(f => f.Name).ToArray());

        StringBuilder csvdata = new StringBuilder();
        csvdata.AppendLine(header);

        foreach (var o in objectlist) 
            csvdata.AppendLine(ToCsvFields(separator, fields, o));

        return csvdata.ToString();
    }

    public static string ToCsvFields(string separator, FieldInfo[] fields, object o)
    {
        StringBuilder linie = new StringBuilder();

        foreach (var f in fields)
        {
            if (linie.Length > 0)
                linie.Append(separator);

            var x = f.GetValue(o);

            if (x != null)
                linie.Append(x.ToString());
        }

        return linie.ToString();
    }

许多变化可以由,如在ToCsv直接写入到一个文件(),或者与一个IEnumerable和产量语句替换StringBuilder的。

其他提示

下面是每Hejndorf的CSV想法的简化版本(没有内存开销,因为它产生每行依次)。由于普遍的需求也通过使用Concat的同时支持字段和简单属性。

更新2017年5月18日

这个例子从来没有打算成为一个完整的解决方案,只是推进张贴每Hejndorf了原来的想法。为了产生有效的CSV需要更换任何文字分隔符,在文本中,有2个分隔符字符的序列。例如一个简单的.Replace("\"", "\"\"")

更新2016年2月12日

今天再次使用自己的代码中的一个项目后,我意识到我不应该采取什么是理所当然的,当我从@Per Hejndorf的例子开始。更有意义的假设“”(逗号)的缺省的定界符,使分隔第二,可选的的,参数。我自己的库版本还提供了控制标题行是否应该返回,因为有时候你只需要数据第3 header参数。

e.g。

public static IEnumerable<string> ToCsv<T>(IEnumerable<T> objectlist, string separator = ",", bool header = true)
{
    FieldInfo[] fields = typeof(T).GetFields();
    PropertyInfo[] properties = typeof(T).GetProperties();
    if (header)
    {
        yield return String.Join(separator, fields.Select(f => f.Name).Concat(properties.Select(p=>p.Name)).ToArray());
    }
    foreach (var o in objectlist)
    {
        yield return string.Join(separator, fields.Select(f=>(f.GetValue(o) ?? "").ToString())
            .Concat(properties.Select(p=>(p.GetValue(o,null) ?? "").ToString())).ToArray());
    }
}

<强>所以你然后使用这样为逗号分隔:

foreach (var line in ToCsv(objects))
{
    Console.WriteLine(line);
}

或类似这样的另一个定界符(例如TAB):

foreach (var line in ToCsv(objects, "\t"))
{
    Console.WriteLine(line);
}

实际例子

<强>写入列表以逗号分隔的CSV文件

using (TextWriter tw = File.CreateText("C:\testoutput.csv"))
{
    foreach (var line in ToCsv(objects))
    {
        tw.WriteLine(line);
    }
}

或写制表符分隔的

using (TextWriter tw = File.CreateText("C:\testoutput.txt"))
{
    foreach (var line in ToCsv(objects, "\t"))
    {
        tw.WriteLine(line);
    }
}

如果您有复杂的字段/属性,你将需要过滤出来的选择条款。


以前的版本和细节如下:

下面是每Hejndorf的CSV想法的简化版本(没有内存开销,因为它产生依次在每个行)的并且仅具有4行代码:)

public static IEnumerable<string> ToCsv<T>(string separator, IEnumerable<T> objectlist)
{
    FieldInfo[] fields = typeof(T).GetFields();
    yield return String.Join(separator, fields.Select(f => f.Name).ToArray());
    foreach (var o in objectlist)
    {
        yield return string.Join(separator, fields.Select(f=>(f.GetValue(o) ?? "").ToString()).ToArray());
    }
}

您可以遍历它是这样的:

foreach (var line in ToCsv(",", objects))
{
    Console.WriteLine(line);
}

其中objects是对象的强类型化的列表。

的这种变化包括公共字段和简单公共属性:

public static IEnumerable<string> ToCsv<T>(string separator, IEnumerable<T> objectlist)
{
    FieldInfo[] fields = typeof(T).GetFields();
    PropertyInfo[] properties = typeof(T).GetProperties();
    yield return String.Join(separator, fields.Select(f => f.Name).Concat(properties.Select(p=>p.Name)).ToArray());
    foreach (var o in objectlist)
    {
        yield return string.Join(separator, fields.Select(f=>(f.GetValue(o) ?? "").ToString())
            .Concat(properties.Select(p=>(p.GetValue(o,null) ?? "").ToString())).ToArray());
    }
}

作为一个经验我主张只重写的toString作为调试工具的规则,如果它是业务逻辑应该是类/接口上的显式方法。

对于这样简单的序列我建议有一个单独的类,它知道你的CSV输出库和你的业务对象,做序列化,而不是推到系列化的业务对象本身。

此方式你最终产生的模型的视图,每个输出格式的类。

对于更复杂的序列化,你正在试图写出一个对象图的持久性我会考虑把它在业务类 - 但只有当它使更清晰的代码

与我迄今发现的解决方案的问题是,他们不会让你导出属性的子集,但只有整个对象。大多数时候,当我们需要的数据在CSV出口,我们需要一个精确的方式“量体裁衣”的格式,所以我通过传递的类型Func<T, string>的参数数组来创建这个简单的扩展方法,可以让我做指定映射。

public static string ToCsv<T>(this IEnumerable<T> list, params Func<T, string>[] properties)
{
    var columns = properties.Select(func => list.Select(func).ToList()).ToList();

    var stringBuilder = new StringBuilder();

    var rowsCount = columns.First().Count;

    for (var i = 0; i < rowsCount; i++)
    {
        var rowCells = columns.Select(column => column[i]);

        stringBuilder.AppendLine(string.Join(",", rowCells));
    }

    return stringBuilder.ToString();
}

<强>用法:

philosophers.ToCsv(x => x.LastName, x => x.FirstName)

<强>生成:

Hayek,Friedrich
Rothbard,Murray
Brent,David

我有一个问题的高科技型魔法的变化是两个属性具有相同的值,只有一个会得到填充。这似乎已经固定它:

        public static IEnumerable<string> ToCsv<T>(string separator, IEnumerable<T> objectlist)
    {
        FieldInfo[] fields = typeof(T).GetFields();
        PropertyInfo[] properties = typeof(T).GetProperties();
        yield return String.Join(separator, fields.Select(f => f.Name).Union(properties.Select(p => p.Name)).ToArray());
        foreach (var o in objectlist)
        {
            yield return string.Join(separator, (properties.Select(p => (p.GetValue(o, null) ?? "").ToString())).ToArray());
        }
    }

飘编码的回答是非常有益的。我为了处理文本小鬼将软管输出作了一些修改它。

 /******************************************************/
    public static IEnumerable<string> ToCsv<T>(IEnumerable<T> objectlist, string separator = ",", bool header = true)
    {
       FieldInfo[] fields = typeof(T).GetFields();
       PropertyInfo[] properties = typeof(T).GetProperties();
       string str1;
       string str2;

       if(header)
       {
          str1 = String.Join(separator, fields.Select(f => f.Name).Concat(properties.Select(p => p.Name)).ToArray());
          str1 = str1 + Environment.NewLine;
          yield return str1;
       }
       foreach(var o in objectlist)
       {
          //regex is to remove any misplaced returns or tabs that would
          //really mess up a csv conversion.
          str2 = string.Join(separator, fields.Select(f => (Regex.Replace(Convert.ToString(f.GetValue(o)), @"\t|\n|\r", "") ?? "").Trim())
             .Concat(properties.Select(p => (Regex.Replace(Convert.ToString(p.GetValue(o, null)), @"\t|\n|\r", "") ?? "").Trim())).ToArray());

          str2 = str2 + Environment.NewLine;
          yield return str2;
       }
    }
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top