2009-05-20 1 views
28

В настоящее время есть DataTable, но вы хотите передать его пользователю через WebHandler. FileHelpers имеет CommonEngine.DataTableToCsv(dt, "file.csv"). Однако он сохраняет файл. Как я могу сохранить его в потоке? Я знаю, как это сделать, когда я знаю столбцы в расширенном или они не меняются, но я хочу сгенерировать заголовки столбцов прямо из таблицы данных.Преобразование DataTable в 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()); 

К сожалению, поскольку я не знаю столбцов перед рукой, я не могу создать класс перед рукой.

+0

Вы можете проверить это https://gist.github.com/riyadparvez/4467668 – user

+0

Файл Помощники библиотека с открытым исходным кодом. Почему бы вам не взломать его и добавить свой собственный метод? – Keltex

+0

@user: Этот текст содержит ошибку, в которой записи с запятыми обрабатываются неправильно. См. Http://stackoverflow.com/q/769621/1461424 – Krumia

ответ

65

Вы можете просто написать что-то быстро себя:

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()); 
    } 

EDIT: Ваши комментарии:

Это зависит от того, как вы хотите отформатировать csv, но обычно, если текст содержит специальные символы, вы хотите заключить его в двойные кавычки, то есть: «my, text». Вы можете добавить проверку кода, который создает csv для проверки специальных символов, и заключает текст в двойные кавычки, если он есть. Что касается .NET 2.0, просто создайте его как вспомогательный метод в своем классе или удалите это слово в объявлении метода и вызовите его так: Extensions.ToCsv (table);

+18

Как насчет того, когда данные включают в себя цитату, новую строку или запятую? – SamWM

+0

Также с использованием .NET 2.0 – SamWM

+0

Это работает для меня. Поправился немного для моих нужд. Excel отображает новые строки как? хотя (имеют такую ​​же проблему при экспорте с SQL Server) – SamWM

2

Если вы можете превратить ваш DataTable в 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(); 
     } 
+1

Спасибо, fyi - header.Length & body.Length должно быть -1 – downatone

1

Вы можете попробовать использовать что-то вроде этого. В этом случае я использовал одну хранимую процедуру для получения большего количества таблиц данных и экспорта всех из них с использованием 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(); 
    } 
    } 
} 
0

Ответ BFree работал для меня. Мне нужно было отправить поток прямо в браузер. Я бы предположил, что это обычная альтернатива. Я добавил следующее Main BFree по коду(), чтобы сделать это:

//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); 
1

Я использовал следующий код, расхищаться чьем-то блоге (пожалуйста, простите отсутствие цитат). Он заботится о котировках, новой строке и запятой разумно изящным способом, вызывая каждое значение поля.

/// <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(); 
    } 
12

Update 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 и т.д.

происхождения

у меня есть простой DataTable к функции 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); 
    } 
1
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 
     } 
2

Я не знаю, если это преобразуется из 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 ""; 
    } 

} 
2

Если вы хотите, чтобы поток 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(); 

 Смежные вопросы

  • Нет связанных вопросов^_^