这里有一个简化版的Per Hejndorf的CSV想法(没有内存开销,它会逐行产生)。由于受欢迎的要求,它还通过使用
Concat
支持字段和简单属性。
更新于2017年5月18日
此示例从未旨在成为完整的解决方案,只是推进了Per Hejndorf发布的原始思路。要生成有效的CSV,您需要将文本中的任何文本分隔符字符替换为2个分隔符字符的序列,例如:
.Replace("\"", "\"\"")
。
更新于2016年2月12日
今天在项目中再次使用我的代码后,我意识到我不应该从
@Per Hejndorf
的示例中认为什么都是理所当然的。假设默认分隔符为“,”(逗号),并使分隔符成为第二个可选参数更有意义。我自己的库版本还提供了第三个
header
参数,用于控制是否返回标题行,因为有时您只想要数据。
例如:
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);
}
}
如果您有复杂的字段/属性,您需要将它们从选择子句中过滤出来。
以下是早期版本和详细信息:
这是Per 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());
}
}
XmlSerializer.Serialize(...)
所做的那样。这将解决大多数内存不足的问题。 - Spoikeif (x != null)
是否合适。如果你有一个带有字段{ a, b, c }
的对象,并且不同的实例中,a
、b
或c
中的某些可能为null
,那么你将无法从中生成有效的 CSV 并恢复这些实例。更好的方法是将 null 值替换为NULL
或空白条目。 - Ivaylo Slavov