如何创buildCSV文件C#?
我正在寻找创buildCSV Excel文件的类。
预期function:
- 使用非常简单
- 转义逗号和引号,所以excel处理罚款
- 以时区格式导出date和date时间
你知道有这样的课程吗?
稍微不同的版本,我用reflection来写我的需求。 我不得不将对象列表导出到csv。 如果有人想要使用它的未来。
public class CsvExport<T> where T: class { public List<T> Objects; public CsvExport(List<T> objects) { Objects = objects; } public string Export() { return Export(true); } public string Export(bool includeHeaderLine) { StringBuilder sb = new StringBuilder(); //Get properties using reflection. IList<PropertyInfo> propertyInfos = typeof(T).GetProperties(); if (includeHeaderLine) { //add header line. foreach (PropertyInfo propertyInfo in propertyInfos) { sb.Append(propertyInfo.Name).Append(","); } sb.Remove(sb.Length - 1, 1).AppendLine(); } //add value for each property. foreach (T obj in Objects) { foreach (PropertyInfo propertyInfo in propertyInfos) { sb.Append(MakeValueCsvFriendly(propertyInfo.GetValue(obj, null))).Append(","); } sb.Remove(sb.Length - 1, 1).AppendLine(); } return sb.ToString(); } //export to a file. public void ExportToFile(string path) { File.WriteAllText(path, Export()); } //export as binary data. public byte[] ExportToBytes() { return Encoding.UTF8.GetBytes(Export()); } //get the csv value for field. private string MakeValueCsvFriendly(object value) { if (value == null) return ""; if (value is Nullable && ((INullable)value).IsNull) return ""; if (value is DateTime) { if (((DateTime)value).TimeOfDay.TotalSeconds == 0) return ((DateTime)value).ToString("yyyy-MM-dd"); return ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss"); } string output = value.ToString(); if (output.Contains(",") || output.Contains("\"")) output = '"' + output.Replace("\"", "\"\"") + '"'; return output; } }
使用示例:(根据评论更新)
CsvExport<BusinessObject> csv= new CsvExport<BusinessObject>(GetBusinessObjectList()); Response.Write(csv.Export());
请原谅我
但是我认为一个公开的开放源代码库是一个更好的方法来共享代码,并做出贡献,更正和补充,如“我解决了这个问题,我解决了这个问题”
所以我做了一个简单的git-repository从主题的起始代码和所有的增加:
https://github.com/jitbit/CsvExport
我还自己添加了一些有用的修复程序。 每个人都可以添加build议,分叉贡献等等等等。把我的叉子,所以我把它们合并回到回购。
PS。 我发布了Chris的所有版权声明。 @克里斯,如果你反对这个想法 – 让我知道,我会杀了它。
读取和写入CSV文件的另一个好的解决scheme是filehelpers (开源)。
如何使用string.Join而不是所有foreach循环?
如果有人希望我把它转换成IEnumerable的扩展方法:
public static class ListExtensions { public static string ExportAsCSV<T>(this IEnumerable<T> listToExport, bool includeHeaderLine, string delimeter) { StringBuilder sb = new StringBuilder(); IList<PropertyInfo> propertyInfos = typeof(T).GetProperties(); if (includeHeaderLine) { foreach (PropertyInfo propertyInfo in propertyInfos) { sb.Append(propertyInfo.Name).Append(","); } sb.Remove(sb.Length - 1, 1).AppendLine(); } foreach (T obj in listToExport) { T localObject = obj; var line = String.Join(delimeter, propertyInfos.Select(x => SanitizeValuesForCSV(x.GetValue(localObject, null), delimeter))); sb.AppendLine(line); } return sb.ToString(); } private static string SanitizeValuesForCSV(object value, string delimeter) { string output; if (value == null) return ""; if (value is DateTime) { output = ((DateTime)value).ToLongDateString(); } else { output = value.ToString(); } if (output.Contains(delimeter) || output.Contains("\"")) output = '"' + output.Replace("\"", "\"\"") + '"'; output = output.Replace("\n", " "); output = output.Replace("\r", ""); return output; } }
这个class上的工作很棒。 简单易用。 我修改了这个类,在导出的第一行包含了一个标题。 我想分享一下:
使用:
CsvExport myExport = new CsvExport(); myExport.addTitle = String.Format("Name: {0},{1}", lastName, firstName));
类:
public class CsvExport { List<string> fields = new List<string>(); public string addTitle { get; set; } // string for the first row of the export List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>(); Dictionary<string, object> currentRow { get { return rows[rows.Count - 1]; } } public object this[string field] { set { if (!fields.Contains(field)) fields.Add(field); currentRow[field] = value; } } public void AddRow() { rows.Add(new Dictionary<string, object>()); } string MakeValueCsvFriendly(object value) { if (value == null) return ""; if (value is Nullable && ((INullable)value).IsNull) return ""; if (value is DateTime) { if (((DateTime)value).TimeOfDay.TotalSeconds == 0) return ((DateTime)value).ToString("yyyy-MM-dd"); return ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss"); } string output = value.ToString(); if (output.Contains(",") || output.Contains("\"")) output = '"' + output.Replace("\"", "\"\"") + '"'; return output; } public string Export() { StringBuilder sb = new StringBuilder(); // if there is a title if (!string.IsNullOrEmpty(addTitle)) { // escape chars that would otherwise break the row / export char[] csvTokens = new[] { '\"', ',', '\n', '\r' }; if (addTitle.IndexOfAny(csvTokens) >= 0) { addTitle = "\"" + addTitle.Replace("\"", "\"\"") + "\""; } sb.Append(addTitle).Append(","); sb.AppendLine(); } // The header foreach (string field in fields) sb.Append(field).Append(","); sb.AppendLine(); // The rows foreach (Dictionary<string, object> row in rows) { foreach (string field in fields) sb.Append(MakeValueCsvFriendly(row[field])).Append(","); sb.AppendLine(); } return sb.ToString(); } public void ExportToFile(string path) { File.WriteAllText(path, Export()); } public byte[] ExportToBytes() { return Encoding.UTF8.GetBytes(Export()); } }
有一个CSV的开源库,你可以使用nuget获得: http ://joshclose.github.io/CsvHelper/
我添加了ExportToStream,所以csv不必先保存到硬盘。
public Stream ExportToStream() { MemoryStream stream = new MemoryStream(); StreamWriter writer = new StreamWriter(stream); writer.Write(Export(true)); writer.Flush(); stream.Position = 0; return stream; }
我已经添加了
public void ExportToFile(string path, DataTable tabela) { DataColumnCollection colunas = tabela.Columns; foreach (DataRow linha in tabela.Rows) { this.AddRow(); foreach (DataColumn coluna in colunas) { this[coluna.ColumnName] = linha[coluna]; } } this.ExportToFile(path); }
以前的代码不适用于旧的.NET版本。 对于3.5版本的框架使用这个其他版本:
public void ExportToFile(string path) { bool abort = false; bool exists = false; do { exists = File.Exists(path); if (!exists) { if( !Convert.ToBoolean( File.CreateText(path) ) ) abort = true; } } while (!exists || abort); if (!abort) { //File.OpenWrite(path); using (StreamWriter w = File.AppendText(path)) { w.WriteLine("hello"); } } //File.WriteAllText(path, Export()); }
非常感谢! 我修改了这个类:
- 使用可变的分隔符,而不是代码中的硬编码
- replaceMakeValueCsvFriendly中的所有newLines(\ n \ r \ n \ r)
码:
using System; using System.Collections.Generic; using System.Linq; using System.Data.SqlTypes; using System.IO; using System.Text; using System.Text.RegularExpressions; public class CsvExport { public char delim = ';'; /// <summary> /// To keep the ordered list of column names /// </summary> List<string> fields = new List<string>(); /// <summary> /// The list of rows /// </summary> List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>(); /// <summary> /// The current row /// </summary> Dictionary<string, object> currentRow { get { return rows[rows.Count - 1]; } } /// <summary> /// Set a value on this column /// </summary> public object this[string field] { set { // Keep track of the field names, because the dictionary loses the ordering if (!fields.Contains(field)) fields.Add(field); currentRow[field] = value; } } /// <summary> /// Call this before setting any fields on a row /// </summary> public void AddRow() { rows.Add(new Dictionary<string, object>()); } /// <summary> /// Converts a value to how it should output in a csv file /// If it has a comma, it needs surrounding with double quotes /// Eg Sydney, Australia -> "Sydney, Australia" /// Also if it contains any double quotes ("), then they need to be replaced with quad quotes[sic] ("") /// Eg "Dangerous Dan" McGrew -> """Dangerous Dan"" McGrew" /// </summary> string MakeValueCsvFriendly(object value) { if (value == null) return ""; if (value is INullable && ((INullable)value).IsNull) return ""; if (value is DateTime) { if (((DateTime)value).TimeOfDay.TotalSeconds == 0) return ((DateTime)value).ToString("yyyy-MM-dd"); return ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss"); } string output = value.ToString(); if (output.Contains(delim) || output.Contains("\"")) output = '"' + output.Replace("\"", "\"\"") + '"'; if (Regex.IsMatch(output, @"(?:\r\n|\n|\r)")) output = string.Join(" ", Regex.Split(output, @"(?:\r\n|\n|\r)")); return output; } /// <summary> /// Output all rows as a CSV returning a string /// </summary> public string Export() { StringBuilder sb = new StringBuilder(); // The header foreach (string field in fields) sb.Append(field).Append(delim); sb.AppendLine(); // The rows foreach (Dictionary<string, object> row in rows) { foreach (string field in fields) sb.Append(MakeValueCsvFriendly(row[field])).Append(delim); sb.AppendLine(); } return sb.ToString(); } /// <summary> /// Exports to a file /// </summary> public void ExportToFile(string path) { File.WriteAllText(path, Export()); } /// <summary> /// Exports as raw UTF8 bytes /// </summary> public byte[] ExportToBytes() { return Encoding.UTF8.GetBytes(Export()); } }
您也可以使用ADO来执行此操作: http : //weblogs.asp.net/fmarguerie/archive/2003/10/01/29964.aspx
原始类有一个问题,那就是如果你想添加一个新的列,你将在Export方法上收到KeyNotFoundException。 例如:
static void Main(string[] args) { var export = new CsvExport(); export.AddRow(); export["Region"] = "New York, USA"; export["Sales"] = 100000; export["Date Opened"] = new DateTime(2003, 12, 31); export.AddRow(); export["Region"] = "Sydney \"in\" Australia"; export["Sales"] = 50000; export["Date Opened"] = new DateTime(2005, 1, 1, 9, 30, 0); export["Balance"] = 3.45f; //Exception is throwed for this new column export.ExportToFile("Somefile.csv"); }
为了解决这个问题,并使用@KeyboardCowboy使用reflection的想法,我修改了代码,以允许添加不具有相同列的行。 您可以使用匿名类的实例。 例如:
static void Main(string[] args) { var export = new CsvExporter(); export.AddRow(new {A = 12, B = "Empty"}); export.AddRow(new {A = 34.5f, D = false}); export.ExportToFile("File.csv"); }
你可以在这里下载源代码CsvExporter 。 随意使用和修改。
现在,如果你想写的所有行都是同一个类,我创build了generics类CsvWriter.cs ,它具有更好的性能RAM使用率,是写大文件的理想select。它可以让你将格式化程序添加到你想要的数据types。 使用示例:
class Program { static void Main(string[] args) { var writer = new CsvWriter<Person>("Persons.csv"); writer.AddFormatter<DateTime>(d => d.ToString("MM/dd/yyyy")); writer.WriteHeaders(); writer.WriteRows(GetPersons()); writer.Flush(); writer.Close(); } private static IEnumerable<Person> GetPersons() { yield return new Person { FirstName = "Jhon", LastName = "Doe", Sex = 'M' }; yield return new Person { FirstName = "Jhane", LastName = "Doe", Sex = 'F', BirthDate = DateTime.Now }; } } class Person { public string FirstName { get; set; } public string LastName { get; set; } public char Sex { get; set; } public DateTime BirthDate { get; set; } }
你只需要一个function来做到这一点。 只有你必须做的是在你的解决scheme资源pipe理器中创build一个文件夹,并将csv文件存储在那里,然后将该文件导出到用户。
在我的情况下,我有一个文件夹下载。 首先,我将所有内容导出到该目录,然后将其导出到用户。 对于response.end处理,我使用了ThreadAbortException。 所以在我的解决scheme中,它是一个100%的真正的和工作的function。
protected void lnkExport_OnClick(object sender, EventArgs e) { string filename = strFileName = "Export.csv"; DataTable dt = obj.GetData(); // call the content and load it into the datatable strFileName = Server.MapPath("Downloads") + "\\" + strFileName; // creating a file in the downloads folder in your solution explorer TextWriter tw = new StreamWriter(strFileName); // using the built in class textwriter for writing your content in the exporting file string strData = "Username,Password,City"; // above line is the header for your exported file. So add headings for your coloumns in excel(.csv) file and seperate them with "," strData += Environment.NewLine; // setting the environment to the new line foreach (DataRow dr in dt.Rows) { strData += dr["Username"].ToString() + "," + dr["Password"].ToString() + "," + dr["City"].ToString(); strData += Environment.NewLine; } // everytime when loop execute, it adds a line into the file tw.Write(strData); // writing the contents in file tw.Close(); // closing the file Response.Redirect("Downloads/" + filename); // exporting the file to the user as a popup to save as.... }