如果我有一个名为"Car"的对象列表:
public class Car
{
public string Name;
public int Year;
public string Model;
}
如何将对象列表,例如List<Car>转换为csv格式?
String Escape(String s)
{
StringBuilder sb = new StringBuilder();
bool needQuotes = false;
foreach (char c in s.ToArray())
{
switch (c)
{
case '"': sb.Append("\\\""); needQuotes = true; break;
case ' ': sb.Append(" "); needQuotes = true; break;
case ',': sb.Append(","); needQuotes = true; break;
case '\t': sb.Append("\\t"); needQuotes = true; break;
case '\n': sb.Append("\\n"); needQuotes = true; break;
default: sb.Append(c); break;
}
}
if (needQuotes)
return "\"" + sb.ToString() + "\"";
else
return sb.ToString();
}
public void SerializeAsCsv(Stream stream)
{
stream.Write(Escape(Name));
stream.Write(",");
stream.Write(Year.ToString());
stream.Write(",");
stream.Write(Escape(Model));
stream.Write("\n");
}
foreach (Car car in list)
{
car.SerializeAsCsv(stream);
}
FileHelpers是一个免费且易于使用的.NET库,可用于在文件、字符串或流中导入/导出固定长度或分隔记录的数据。
这将确保处理行终止、转义等所有可能出现的问题都是正确的,符合RFC-4180标准。我一直在寻找解决方案,但是我找到的答案都没有满足我的简单需求。后来我意识到我的自动CRUD代码已经有了答案。我重新利用它并得出了以下代码:
using System.Reflection;
/// <summary>
/// Using a bit of reflection to build up the strings.
/// </summary>
public static string ToCsvHeader(this object obj)
{
Type type = obj.GetType();
var properties = type.GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance);
string result = string.Empty;
Array.ForEach(properties, prop =>
{
result += prop.Name + ",";
});
return (!string.IsNullOrEmpty(result) ? result.Substring(0, result.Length - 1) : result);
}
public static string ToCsvRow(this object obj)
{
Type type = obj.GetType();
var properties = type.GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance);
string result = string.Empty;
Array.ForEach(properties, prop =>
{
var value = prop.GetValue(obj, null);
var propertyType = prop.PropertyType.FullName;
if (propertyType == "System.String")
{
// wrap value incase of commas
value = "\"" + value + "\"";
}
result += value + ",";
});
return (!string.IsNullOrEmpty(result) ? result.Substring(0, result.Length - 1) : result);
}
var burgers = new List<Cheeseburger>();
var output = burgers.ToCsvHeader();
output += Environment.NewLine;
burgers.ForEach(burger =>
{
output += burger.ToCsvRow();
output += Environment.NewLine;
});
var path = "[where ever you want]";
System.IO.File.WriteAllText(path, output);
也许有更好的方法来编写上述两种方法,但对于我的情况来说,这完美地解决了问题。希望对他人有所帮助。
public String ToCSVRow()
{
return Name + "," + Year.ToString() + "," + Model;
}
然后只需在需要的地方执行类似这样的操作。
using (StreamWriter file = new StreamWriter(@"C:\Wherever\yourfilename.txt"))
{
foreach (var item in yourlist)
{
file.WriteLine(item.ToCSVRow());
}
}
我会按照这篇文章的建议实现一些额外的序列化行为。如果你想要更高级一点,可以在项目属性中创建一个设置。该设置将决定你的类是否使用csv或默认序列化。然后,你可以通过这里所示的技术来访问它。考虑使用静态构造函数来读取appsettings并使布尔值可访问到你的序列化代码。Vlads'的代码看起来很棒,只需将其插入到你的代码中即可。此外,你还可以考虑其他更理想的方法来改变你的序列化行为。
或者创建一个名为“SerializeAsCSV”的接口,并像这样使用它:
// MyCoolClass.csv的部分内容:
public class MyCoolClass : ISerializeAsCSV, IDisposable
{
protected static bool serializesToCSV = false;
static MyCoolClass()
{
serializesToCSV =
(typeof(MyCoolClass).GetInterface("GrooveySoft.Shared.Interfaces.ISerializeAsCSV") == null)
? false
: true;
}
public MyCoolClass(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
{
// your stuff here
}
public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
{
// your stuff here
}
}
// ISerializeAsCSV.cs 的内容
using System.Runtime.Serialization;
namespace GrooveySoft.Shared.Interfaces
{
/// <summary>
/// indicates that implementor will serialize to csv format
/// </summary>
public interface ISerializeAsCSV : ISerializable
{
}
}
这应该能让你开始了……我还没有编译和测试过,但你可以得到大致的想法。
组合逗号分隔的值时,我总是喜欢指向被低估的string.Join(string separator, string[] elements)
静态方法,但如果有辅助库存在,那可能更好。