将 DataTable 转换为 CSV 流
Posted
技术标签:
【中文标题】将 DataTable 转换为 CSV 流【英文标题】:Convert DataTable to CSV stream 【发布时间】:2010-10-27 15:42:11 【问题描述】:当前有一个 DataTable,但希望通过 WebHandler 将其流式传输给用户。 FileHelpers 有 CommonEngine.DataTableToCsv(dt, "file.csv")
。但是,它将其保存到文件中。如何将其保存到流中?当我知道高级的列或者它们没有改变时,我知道该怎么做,但是我想直接从数据表中生成列标题。
如果我知道我刚刚创建类的列:
[DelimitedRecord(",")]
public class MailMergeFields
[FieldQuoted()]
public string FirstName;
[FieldQuoted()]
public string LastName;
然后使用 FileHelperEngine 并添加记录:
FileHelperEngine engine = new FileHelperEngine(typeof(MailMergeFields));
MailMergeFields[] merge = new MailMergeFields[dt.Rows.Count + 1];
// add headers
merge[0] = new MailMergeFields();
merge[0].FirstName = "FirstName";
merge[0].LastName = "LastName";
int i = 1;
// add records
foreach (DataRow dr in dt.Rows)
merge[i] = new MailMergeFields();
merge[i].FirstName = dr["Forename"];
merge[i].LastName = dr["Surname"];
i++;
最后写入一个流:
TextWriter writer = new StringWriter();
engine.WriteStream(writer, merge);
context.Response.Write(writer.ToString());
不幸的是,由于我事先不知道列,因此我无法事先创建类。
【问题讨论】:
文件助手库是开源的。你为什么不破解它并添加你自己的方法? 您可以查看gist.github.com/riyadparvez/4467668 @user:此要点包含一个错误,即无法正确处理带有逗号的条目。见***.com/q/769621/1461424 【参考方案1】:你可以自己快速写一些东西:
public static class Extensions
public static string ToCSV(this DataTable table)
var result = new StringBuilder();
for (int i = 0; i < table.Columns.Count; i++)
result.Append(table.Columns[i].ColumnName);
result.Append(i == table.Columns.Count - 1 ? "\n" : ",");
foreach (DataRow row in table.Rows)
for (int i = 0; i < table.Columns.Count; i++)
result.Append(row[i].ToString());
result.Append(i == table.Columns.Count - 1 ? "\n" : ",");
return result.ToString();
然后进行测试:
public static void Main()
DataTable table = new DataTable();
table.Columns.Add("Name");
table.Columns.Add("Age");
table.Rows.Add("John Doe", "45");
table.Rows.Add("Jane Doe", "35");
table.Rows.Add("Jack Doe", "27");
var bytes = Encoding.GetEncoding("iso-8859-1").GetBytes(table.ToCSV());
MemoryStream stream = new MemoryStream(bytes);
StreamReader reader = new StreamReader(stream);
Console.WriteLine(reader.ReadToEnd());
编辑:回复你的 cmets:
这取决于您希望如何格式化 csv,但通常如果文本包含特殊字符,您希望将其括在双引号中,即:“my,text”。您可以在创建 csv 的代码中添加检查以检查特殊字符并将文本括在双引号中(如果是)。至于 .NET 2.0 的东西,只需在您的类中创建它作为辅助方法,或者在方法声明中删除单词 this 并像这样调用它:Extensions.ToCsv(table);
【讨论】:
当数据包含引号、换行符或逗号时怎么办? 这对我有用。根据我的需要进行了一些调整。 Excel 将换行符显示为 ?虽然(从 SQL Server 导出时有同样的问题) 建议在 ToCSV 函数中添加以下行:string s = result.ToString(); s = s.TrimEnd(new char[] '\r', '\n' ); return s;
【参考方案2】:
更新 1
我已将其修改为使用 StreamWriter,添加一个选项以检查您的输出中是否需要列标题。
public static bool DataTableToCSV(DataTable dtSource, StreamWriter writer, bool includeHeader)
if (dtSource == null || writer == null) return false;
if (includeHeader)
string[] columnNames = dtSource.Columns.Cast<DataColumn>().Select(column => "\"" + column.ColumnName.Replace("\"", "\"\"") + "\"").ToArray<string>();
writer.WriteLine(String.Join(",", columnNames));
writer.Flush();
foreach (DataRow row in dtSource.Rows)
string[] fields = row.ItemArray.Select(field => "\"" + field.ToString().Replace("\"", "\"\"") + "\"").ToArray<string>();
writer.WriteLine(String.Join(",", fields));
writer.Flush();
return true;
如您所见,您可以通过初始 StreamWriter 选择输出,如果您使用 StreamWriter(Stream BaseStream),可以将csv写入MemeryStream、FileStream等
起源
我有一个简单的数据表到 csv 函数,它对我很有帮助:
public static void DataTableToCsv(DataTable dt, string csvFile)
StringBuilder sb = new StringBuilder();
var columnNames = dt.Columns.Cast<DataColumn>().Select(column => "\"" + column.ColumnName.Replace("\"", "\"\"") + "\"").ToArray();
sb.AppendLine(string.Join(",", columnNames));
foreach (DataRow row in dt.Rows)
var fields = row.ItemArray.Select(field => "\"" + field.ToString().Replace("\"", "\"\"") + "\"").ToArray();
sb.AppendLine(string.Join(",", fields));
File.WriteAllText(csvFile, sb.ToString(), Encoding.Default);
【讨论】:
【参考方案3】:如果你可以把你的数据表变成一个 IEnumerable 这应该对你有用...
Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment;filename=FileName.csv");
Response.Charset = "";
Response.ContentType = "application/text";
Response.Output.Write(ExampleClass.ConvertToCSV(GetListOfObject(), typeof(object)));
Response.Flush();
Response.End();
public static string ConvertToCSV(IEnumerable col, Type type)
StringBuilder sb = new StringBuilder();
StringBuilder header = new StringBuilder();
// Gets all properies of the class
PropertyInfo[] pi = type.GetProperties();
// Create CSV header using the classes properties
foreach (PropertyInfo p in pi)
header.Append(p.Name + ",");
sb.AppendLine(header.ToString().Remove(header.Length));
foreach (object t in col)
StringBuilder body = new StringBuilder();
// Create new item
foreach (PropertyInfo p in pi)
object o = p.GetValue(t, null);
body.Append(o.ToString() + ",");
sb.AppendLine(body.ToString().Remove(body.Length));
return sb.ToString();
【讨论】:
谢谢,仅供参考 - header.Length & body.Length 应该是 -1【参考方案4】:我不知道这是否可以从 VB 转换为 C#,但如果您不想在数字周围加上引号,您可以按如下方式比较数据类型..
public string DataTableToCSV(DataTable dt)
StringBuilder sb = new StringBuilder();
if (dt == null)
return "";
try
// Create the header row
for (int i = 0; i <= dt.Columns.Count - 1; i++)
// Append column name in quotes
sb.Append("\"" + dt.Columns[i].ColumnName + "\"");
// Add carriage return and linefeed if last column, else add comma
sb.Append(i == dt.Columns.Count - 1 ? "\n" : ",");
foreach (DataRow row in dt.Rows)
for (int i = 0; i <= dt.Columns.Count - 1; i++)
// Append value in quotes
//sb.Append("""" & row.Item(i) & """")
// OR only quote items that that are equivilant to strings
sb.Append(object.ReferenceEquals(dt.Columns[i].DataType, typeof(string)) || object.ReferenceEquals(dt.Columns[i].DataType, typeof(char)) ? "\"" + row[i] + "\"" : row[i]);
// Append CR+LF if last field, else add Comma
sb.Append(i == dt.Columns.Count - 1 ? "\n" : ",");
return sb.ToString;
catch (Exception ex)
// Handle the exception however you want
return "";
【讨论】:
【参考方案5】:如果您希望在不创建文件的情况下将 CSV 流式传输给用户,那么我发现以下方法是最简单的方法。您可以使用任何扩展/方法来创建 ToCsv() 函数(它根据给定的 DataTable 返回一个字符串)。
var report = myDataTable.ToCsv();
var bytes = Encoding.GetEncoding("iso-8859-1").GetBytes(report);
Response.Buffer = true;
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=report.csv");
Response.ContentType = "text/csv";
Response.BinaryWrite(bytes);
Response.End();
【讨论】:
【参考方案6】:我使用了以下代码,从某人的博客中窃取(请原谅缺乏引用)。它通过引用每个字段值以相当优雅的方式处理引号、换行符和逗号。
/// <summary>
/// Converts the passed in data table to a CSV-style string.
/// </summary>
/// <param name="table">Table to convert</param>
/// <returns>Resulting CSV-style string</returns>
public static string ToCSV(this DataTable table)
return ToCSV(table, ",", true);
/// <summary>
/// Converts the passed in data table to a CSV-style string.
/// </summary>
/// <param name="table">Table to convert</param>
/// <param name="includeHeader">true - include headers<br/>
/// false - do not include header column</param>
/// <returns>Resulting CSV-style string</returns>
public static string ToCSV(this DataTable table, bool includeHeader)
return ToCSV(table, ",", includeHeader);
/// <summary>
/// Converts the passed in data table to a CSV-style string.
/// </summary>
/// <param name="table">Table to convert</param>
/// <param name="includeHeader">true - include headers<br/>
/// false - do not include header column</param>
/// <returns>Resulting CSV-style string</returns>
public static string ToCSV(this DataTable table, string delimiter, bool includeHeader)
var result = new StringBuilder();
if (includeHeader)
foreach (DataColumn column in table.Columns)
result.Append(column.ColumnName);
result.Append(delimiter);
result.Remove(--result.Length, 0);
result.Append(Environment.NewLine);
foreach (DataRow row in table.Rows)
foreach (object item in row.ItemArray)
if (item is DBNull)
result.Append(delimiter);
else
string itemAsString = item.ToString();
// Double up all embedded double quotes
itemAsString = itemAsString.Replace("\"", "\"\"");
// To keep things simple, always delimit with double-quotes
// so we don't have to determine in which cases they're necessary
// and which cases they're not.
itemAsString = "\"" + itemAsString + "\"";
result.Append(itemAsString + delimiter);
result.Remove(--result.Length, 0);
result.Append(Environment.NewLine);
return result.ToString();
【讨论】:
【参考方案7】:您可以尝试使用类似的东西。 在这种情况下,我使用一个存储过程来获取更多数据表并导出所有数据表 使用 CSV。
using System;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.IO;
namespace bo
class Program
static private void CreateCSVFile(DataTable dt, string strFilePath)
#region Export Grid to CSV
// Create the CSV file to which grid data will be exported.
StreamWriter sw = new StreamWriter(strFilePath, false);
int iColCount = dt.Columns.Count;
// First we will write the headers.
//DataTable dt = m_dsProducts.Tables[0];
for (int i = 0; i < iColCount; i++)
sw.Write(dt.Columns[i]);
if (i < iColCount - 1)
sw.Write(";");
sw.Write(sw.NewLine);
// Now write all the rows.
foreach (DataRow dr in dt.Rows)
for (int i = 0; i < iColCount; i++)
if (!Convert.IsDBNull(dr[i]))
sw.Write(dr[i].ToString());
if (i < iColCount -1 )
sw.Write(";");
sw.Write(sw.NewLine);
sw.Close();
#endregion
static void Main(string[] args)
string strConn = "connection string to sql";
string direktorij = @"d:";
SqlConnection conn = new SqlConnection(strConn);
SqlCommand command = new SqlCommand("sp_ado_pos_data", conn);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add('@skl_id', SqlDbType.Int).Value = 158;
SqlDataAdapter adapter = new SqlDataAdapter(command);
DataSet ds = new DataSet();
adapter.Fill(ds);
for (int i = 0; i < ds.Tables.Count; i++)
string datoteka = (string.Format(@"0tablea1.csv", direktorij, i));
DataTable tabela = ds.Tables[i];
CreateCSVFile(tabela,datoteka );
Console.WriteLine("Generišem tabelu 0", datoteka);
Console.ReadKey();
【讨论】:
【参考方案8】:public void CreateCSVFile(DataTable dt, string strFilePath,string separator)
#region Export Grid to CSV
// Create the CSV file to which grid data will be exported.
StreamWriter sw = new StreamWriter(strFilePath, false);
int iColCount = dt.Columns.Count;
for (int i = 0; i < iColCount; i++)
sw.Write(dt.Columns[i]);
if (i < iColCount - 1)
sw.Write(separator);
sw.Write(sw.NewLine);
// Now write all the rows.
foreach (DataRow dr in dt.Rows)
for (int i = 0; i < iColCount; i++)
if (!Convert.IsDBNull(dr[i]))
sw.Write(dr[i].ToString());
if (i < iColCount - 1)
sw.Write(separator);
sw.Write(sw.NewLine);
sw.Close();
#endregion
【讨论】:
【参考方案9】:BFree 的回答对我有用。我需要将流直接发布到浏览器。我想这是一个常见的选择。为此,我在 BFree 的 Main() 代码中添加了以下内容:
//StreamReader reader = new StreamReader(stream);
//Console.WriteLine(reader.ReadToEnd());
string fileName = "fileName.csv";
HttpContext.Current.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
HttpContext.Current.Response.AddHeader("content-disposition", string.Format("attachment;filename=0", fileName));
stream.Position = 0;
stream.WriteTo(HttpContext.Current.Response.OutputStream);
【讨论】:
以上是关于将 DataTable 转换为 CSV 流的主要内容,如果未能解决你的问题,请参考以下文章