【问题标题】:Convert DataTable to CSV stream将 DataTable 转换为 CSV 流
【发布时间】:2010-10-27 15:42:11
【问题描述】:

当前有一个 DataTable,但希望通过 WebHandler 将其流式传输给用户。 FileHelpersCommonEngine.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());

不幸的是,由于我事先不知道列,因此我无法事先创建类。

【问题讨论】:

标签: c# csv datatable


【解决方案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(@"{0}tablea{1}.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);
              

              【讨论】:

                猜你喜欢
                • 2020-09-04
                • 2011-11-01
                • 1970-01-01
                • 2017-03-21
                • 1970-01-01
                • 1970-01-01
                • 2015-04-14
                • 1970-01-01
                相关资源
                最近更新 更多