C# 将数据表转换为 CSV 流
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/888181/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Convert DataTable to CSV stream
提问by SamWM
Currently have a DataTable, but wish to stream it to the user via a WebHandler. FileHelpershas CommonEngine.DataTableToCsv(dt, "file.csv")
. However it saves it to a file. How can I save it to a stream instead? I know how to do it when I know the columns in advanced or they don't change, but I want to generate the column headings straight from the data table.
当前有一个 DataTable,但希望通过 WebHandler 将其流式传输给用户。FileHelpers有CommonEngine.DataTableToCsv(dt, "file.csv")
. 但是它会将它保存到一个文件中。如何将其保存到流中?当我知道高级列或它们没有更改时,我知道该怎么做,但我想直接从数据表中生成列标题。
If I know the columns I just create the class:
如果我知道列我只是创建类:
[DelimitedRecord(",")]
public class MailMergeFields
{
[FieldQuoted()]
public string FirstName;
[FieldQuoted()]
public string LastName;
}
Then use FileHelperEngine and add the records:
然后使用 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++;
}
Finally write to a stream:
最后写入流:
TextWriter writer = new StringWriter();
engine.WriteStream(writer, merge);
context.Response.Write(writer.ToString());
Unfortunately as I don't know the columns before hand, I can't create the class before hand.
不幸的是,由于我事先不知道列,因此我无法事先创建该类。
采纳答案by BFree
You can just write something quickly yourself:
你可以自己快速写一些东西:
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();
}
}
And to test:
并测试:
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("Hyman 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());
}
EDIT: Re your comments:
编辑:回复您的评论:
It depends on how you want your csv formatted but generally if the text contains special characters, you want to enclose it in double quotes ie: "my,text". You can add checking in the code that creates the csv to check for special characters and encloses the text in double quotes if it is. As for the .NET 2.0 thing, just create it as a helper method in your class or remove the word this in the method declaration and call it like so : Extensions.ToCsv(table);
这取决于您希望如何格式化 csv,但通常如果文本包含特殊字符,您希望将其括在双引号中,即:“my,text”。您可以在创建 csv 的代码中添加检查以检查特殊字符,如果是,则将文本括在双引号中。至于 .NET 2.0 的东西,只需在您的类中将其创建为辅助方法或删除方法声明中的 this 并像这样调用它: Extensions.ToCsv(table);
回答by Jon Jones
If you can turn your datatable into an IEnumerable this should work for you...
如果你可以把你的数据表变成一个 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();
}
回答by adopilot
You can try using something like this. In this case I used one stored procedure to get more data tables and export all of them using CSV.
你可以尝试使用这样的东西。在这种情况下,我使用了一个存储过程来获取更多数据表并使用 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(@"{0}tablea{1}.csv", direktorij, i));
DataTable tabela = ds.Tables[i];
CreateCSVFile(tabela,datoteka );
Console.WriteLine("Generi?em tabelu {0}", datoteka);
}
Console.ReadKey();
}
}
}
回答by ShawnFeatherly
BFree's answer worked for me. I needed to post the stream right to the browser. Which I'd imagine is a common alternative. I added the following to BFree's Main() code to do this:
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);
回答by gls123
I've used the following code, pillaged from someone's blog (pls forgive lack of citation). It takes care of quotations, newline and comma in a reasonably elegant way by quoting out each field value.
我使用了以下代码,从某人的博客中掠夺(请原谅缺少引用)。它通过引用每个字段值以相当优雅的方式处理引号、换行符和逗号。
/// <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();
}
回答by SuperLucky
Update 1
更新 1
I have modified it to use StreamWriter instead, add an option to check if you need column headers in your output.
我已将其修改为使用 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;
}
As you can see, you can choose the output by initial StreamWriter, if you use StreamWriter(Stream BaseStream), you can write csv into MemeryStream, FileStream, etc.
如您所见,您可以通过初始StreamWriter选择输出,如果您使用StreamWriter(Stream BaseStream),您可以将csv写入MemeryStream、FileStream等。
Origin
起源
I have an easy datatable to csv function, it serves me well:
我有一个简单的数据表到 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);
}
回答by Sumon Banerjee
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
}
回答by Daniel Powell
I don't know if this converted from VB to C# ok but if you don't want quotes around your numbers, you might compare the data type as follows..
我不知道这是否从 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 "";
}
}
回答by Paul
If you wish to stream the CSV out to the user without creating a file then I found the following to be the simplest method. You can use any extension/method to create the ToCsv() function (which returns a string based on the given DataTable).
如果您希望在不创建文件的情况下将 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();