c # datatable to csv

Qualcuno potrebbe dirmi perché il seguente codice non funziona. I dati vengono salvati nel file csv, tuttavia i dati non sono separati. Tutto esiste all’interno della prima cella di ogni riga.

StringBuilder sb = new StringBuilder(); foreach (DataColumn col in dt.Columns) { sb.Append(col.ColumnName + ','); } sb.Remove(sb.Length - 1, 1); sb.Append(Environment.NewLine); foreach (DataRow row in dt.Rows) { for (int i = 0; i < dt.Columns.Count; i++) { sb.Append(row[i].ToString() + ","); } sb.Append(Environment.NewLine); } File.WriteAllText("test.csv", sb.ToString()); 

Grazie.

La seguente versione più breve si apre bene in Excel, forse il tuo problema era la virgola finale

.net = 3.5

 StringBuilder sb = new StringBuilder(); string[] columnNames = dt.Columns.Cast(). Select(column => column.ColumnName). ToArray(); sb.AppendLine(string.Join(",", columnNames)); foreach (DataRow row in dt.Rows) { string[] fields = row.ItemArray.Select(field => field.ToString()). ToArray(); sb.AppendLine(string.Join(",", fields)); } File.WriteAllText("test.csv", sb.ToString()); 

.net> = 4.0

E come ha sottolineato Tim, se sei su .net> = 4, puoi renderlo ancora più breve:

 StringBuilder sb = new StringBuilder(); IEnumerable columnNames = dt.Columns.Cast(). Select(column => column.ColumnName); sb.AppendLine(string.Join(",", columnNames)); foreach (DataRow row in dt.Rows) { IEnumerable fields = row.ItemArray.Select(field => field.ToString()); sb.AppendLine(string.Join(",", fields)); } File.WriteAllText("test.csv", sb.ToString()); 

Come suggerito da Christian, se vuoi gestire caratteri speciali che scappano nei campi, sostituisci il blocco del ciclo con:

 foreach (DataRow row in dt.Rows) { IEnumerable fields = row.ItemArray.Select(field => string.Concat("\"", field.ToString().Replace("\"", "\"\""), "\"")); sb.AppendLine(string.Join(",", fields)); } 

E, ultimo suggerimento, è ansible scrivere il contenuto CSV riga per riga anziché come un intero documento, per evitare di avere un grande documento in memoria.

Ho incluso questo in una class di estensione, che ti consente di chiamare:

 myDataTable.WriteToCsvFile("C:\\MyDataTable.csv"); 

su qualsiasi DataTable.

 public static class DataTableExtensions { public static void WriteToCsvFile(this DataTable dataTable, string filePath) { StringBuilder fileContent = new StringBuilder(); foreach (var col in dataTable.Columns) { fileContent.Append(col.ToString() + ","); } fileContent.Replace(",", System.Environment.NewLine, fileContent.Length - 1, 1); foreach (DataRow dr in dataTable.Rows) { foreach (var column in dr.ItemArray) { fileContent.Append("\"" + column.ToString() + "\","); } fileContent.Replace(",", System.Environment.NewLine, fileContent.Length - 1, 1); } System.IO.File.WriteAllText(filePath, fileContent.ToString()); } } 

Una nuova funzione di estensione basata sulla risposta di Paul Grimshaw. L’ho ripulito e ho aggiunto la possibilità di gestire dati imprevisti. (Dati vuoti, virgolette incorporate e virgole nelle intestazioni …)

Restituisce anche una stringa che è più flessibile. Restituisce Null se l’object tabella non contiene alcuna struttura.

  public static string ToCsv(this DataTable dataTable) { StringBuilder sbData = new StringBuilder(); // Only return Null if there is no structure. if (dataTable.Columns.Count == 0) return null; foreach (var col in dataTable.Columns) { if (col == null) sbData.Append(","); else sbData.Append("\"" + col.ToString().Replace("\"", "\"\"") + "\","); } sbData.Replace(",", System.Environment.NewLine, sbData.Length - 1, 1); foreach (DataRow dr in dataTable.Rows) { foreach (var column in dr.ItemArray) { if (column == null) sbData.Append(","); else sbData.Append("\"" + column.ToString().Replace("\"", "\"\"") + "\","); } sbData.Replace(",", System.Environment.NewLine, sbData.Length - 1, 1); } return sbData.ToString(); } 

Lo chiami come segue:

 var csvData = dataTableOject.ToCsv(); 

Se il codice chiamante fa riferimento all’assembly System.Windows.Forms , è ansible considerare un approccio radicalmente diverso. La mia strategia è quella di utilizzare le funzioni già fornite dal framework per realizzare questo in pochissime righe di codice e senza dover scorrere tra colonne e righe. Il codice riportato di seguito crea a livello di codice una DataGridView al volo e imposta DataGridView.DataSource sul DataTable . Successivamente, seleziono a livello di codice tutte le celle (inclusa l’intestazione) in DataGridView e richiamo DataGridView.GetClipboardContent() , inserendo i risultati negli Clipboard Windows. Quindi, si “incolla” il contenuto degli Appunti in una chiamata a File.WriteAllText() , assicurandosi di specificare la formattazione del “incolla” come TextDataFormat.CommaSeparatedValue .

Ecco il codice:

 public static void DataTableToCSV(DataTable Table, string Filename) { using(DataGridView dataGrid = new DataGridView()) { // Save the current state of the clipboard so we can restore it after we are done IDataObject objectSave = Clipboard.GetDataObject(); // Set the DataSource dataGrid.DataSource = Table; // Choose whether to write header. Use EnableWithoutHeaderText instead to omit header. dataGrid.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText; // Select all the cells dataGrid.SelectAll(); // Copy (set clipboard) Clipboard.SetDataObject(dataGrid.GetClipboardContent()); // Paste (get the clipboard and serialize it to a file) File.WriteAllText(Filename,Clipboard.GetText(TextDataFormat.CommaSeparatedValue)); // Restore the current state of the clipboard so the effect is seamless if(objectSave != null) // If we try to set the Clipboard to an object that is null, it will throw... { Clipboard.SetDataObject(objectSave); } } } 

Nota che assicurati anche di conservare il contenuto degli appunti prima di iniziare e di ripristinarlo una volta che ho finito, in modo che l’utente non riceva un sacco di spazzatura inaspettata la prossima volta che l’utente tenta di incollare. Le principali avvertenze a questo approccio sono 1) La class deve fare riferimento a System.Windows.Forms , che potrebbe non essere il caso in un livello di astrazione dei dati, 2) L’assembly dovrà essere mirato per .NET Framework 4.5, come DataGridView fa non esiste in 4.0 e 3) Il metodo fallirà se gli appunti vengono utilizzati da un altro processo.

Ad ogni modo, questo approccio potrebbe non essere adatto alla tua situazione, ma è comunque interessante e può essere un altro strumento nella tua casella degli strumenti.

L’ho fatto di recente ma includevo virgolette doppie attorno ai miei valori.

Ad esempio, modifica queste due righe:

 sb.Append("\"" + col.ColumnName + "\","); ... sb.Append("\"" + row[i].ToString() + "\","); 

Prova a cambiare sb.Append(Environment.NewLine); a sb.AppendLine(); .

 StringBuilder sb = new StringBuilder(); foreach (DataColumn col in dt.Columns) { sb.Append(col.ColumnName + ','); } sb.Remove(sb.Length - 1, 1); sb.AppendLine(); foreach (DataRow row in dt.Rows) { for (int i = 0; i < dt.Columns.Count; i++) { sb.Append(row[i].ToString() + ","); } sb.AppendLine(); } File.WriteAllText("test.csv", sb.ToString()); 

Prova a mettere ; invece di

Spero che sia d’aiuto

Leggi questo e questo ?


Una migliore implementazione sarebbe

 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" : ","); } } File.WriteAllText("test.csv", result.ToString()); 

L’errore è il separatore di lista.

Invece di scrivere sb.Append(something... + ',') dovresti inserire qualcosa come sb.Append(something... + System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator);

È necessario inserire il carattere separatore dell’elenco configurato nel sistema operativo (come nell’esempio sopra) o il separatore di elenco nel computer client in cui il file verrà guardato. Un’altra opzione potrebbe essere configurarla in app.config o web.config come parametro della propria applicazione.

4 righe di codice:

 public static string ToCSV(DataTable tbl) { StringBuilder strb = new StringBuilder(); //column headers strb.AppendLine(string.Join(",", tbl.Columns.Cast() .Select(s => "\"" + s.ColumnName + "\""))); //rows tbl.AsEnumerable().Select(s => strb.AppendLine( string.Join(",", s.ItemArray.Select( i => "\"" + i.ToString() + "\"")))).ToList(); return strb.ToString(); } 

Si noti che la ToList() alla fine è importante; Ho bisogno di qualcosa per forzare una valutazione dell’espressione. Se fossi il golf a codice, potrei usare Min() .

Si noti inoltre che il risultato avrà una nuova riga alla fine a causa dell’ultima chiamata a AppendLine() . Potresti non volerlo. Puoi semplicemente chiamare TrimEnd() per rimuoverlo.

Ecco un miglioramento del post di vc-74 che gestisce le virgole nello stesso modo di Excel. Excel inserisce le virgolette sui dati se i dati hanno una virgola ma non cita se i dati non hanno una virgola.

  public static string ToCsv(this DataTable inDataTable, bool inIncludeHeaders = true) { var builder = new StringBuilder(); var columnNames = inDataTable.Columns.Cast().Select(column => column.ColumnName); if (inIncludeHeaders) builder.AppendLine(string.Join(",", columnNames)); foreach (DataRow row in inDataTable.Rows) { var fields = row.ItemArray.Select(field => field.ToString().WrapInQuotesIfContains(",")); builder.AppendLine(string.Join(",", fields)); } return builder.ToString(); } public static string WrapInQuotesIfContains(this string inString, string inSearchString) { if (inString.Contains(inSearchString)) return "\"" + inString+ "\""; return inString; } 

Per scrivere su un file, penso che il metodo seguente sia il più efficiente e diretto: (puoi aggiungere le virgolette se vuoi)

 public static void WriteCsv(DataTable dt, string path) { using (var writer = new StreamWriter(path)) { writer.WriteLine(string.Join(",", dt.Columns.Cast().Select(dc => dc.ColumnName))); foreach (DataRow row in dt.Rows) { writer.WriteLine(string.Join(",", row.ItemArray)); } } } 

Forse, il modo più semplice sarà usare:

https://github.com/ukushu/DataExporter

specialmente nel caso dei dati di dati contenenti /r/n caratteri o separatori all’interno delle tue celle DataTable.

hai solo bisogno di scrivere il seguente codice:

 Csv csv = new Csv("\t");//Needed delimiter var columnNames = dt.Columns.Cast(). Select(column => column.ColumnName).ToArray(); csv.AddRow(columnNames); foreach (DataRow row in dt.Rows) { var fields = row.ItemArray.Select(field => field.ToString()).ToArray; csv.AddRow(fields); } csv.Save(); 
 StringBuilder sb = new StringBuilder(); SaveFileDialog fileSave = new SaveFileDialog(); IEnumerable columnNames = tbCifSil.Columns.Cast(). Select(column => column.ColumnName); sb.AppendLine(string.Join(",", columnNames)); foreach (DataRow row in tbCifSil.Rows) { IEnumerable fields = row.ItemArray.Select(field =>string.Concat("\"", field.ToString().Replace("\"", "\"\""), "\"")); sb.AppendLine(string.Join(",", fields)); } fileSave.ShowDialog(); File.WriteAllText(fileSave.FileName, sb.ToString()); 
 public void ExpoetToCSV(DataTable dtDataTable, string strFilePath) { StreamWriter sw = new StreamWriter(strFilePath, false); //headers for (int i = 0; i < dtDataTable.Columns.Count; i++) { sw.Write(dtDataTable.Columns[i].ToString().Trim()); if (i < dtDataTable.Columns.Count - 1) { sw.Write(","); } } sw.Write(sw.NewLine); foreach (DataRow dr in dtDataTable.Rows) { for (int i = 0; i < dtDataTable.Columns.Count; i++) { if (!Convert.IsDBNull(dr[i])) { string value = dr[i].ToString().Trim(); if (value.Contains(',')) { value = String.Format("\"{0}\"", value); sw.Write(value); } else { sw.Write(dr[i].ToString().Trim()); } } if (i < dtDataTable.Columns.Count - 1) { sw.Write(","); } } sw.Write(sw.NewLine); } sw.Close(); } 

Per simulare Excel CSV:

 public static string Convert(DataTable dt) { StringBuilder sb = new StringBuilder(); IEnumerable columnNames = dt.Columns.Cast(). Select(column => column.ColumnName); sb.AppendLine(string.Join(",", columnNames)); foreach (DataRow row in dt.Rows) { IEnumerable fields = row.ItemArray.Select(field => { string s = field.ToString().Replace("\"", "\"\""); if(s.Contains(',')) s = string.Concat("\"", s, "\""); return s; }); sb.AppendLine(string.Join(",", fields)); } return sb.ToString().Trim(); } 

Nel caso in cui qualcun altro si imbatta in questo, stavo usando File.ReadAllText per ottenere dati CSV e quindi ho modificato e scritto di nuovo con File.WriteAllText . I \ r \ n CRLF andavano bene, ma le tabs \ t venivano ignorate quando Excel lo apriva. (Tutte le soluzioni in questo thread finora utilizzano un delimitatore di virgola ma non importa.) Blocco note ha mostrato lo stesso formato nel file risultante come nel sorgente. Un Diff ha anche mostrato i file come identici. Ma ho avuto un indizio quando ho aperto il file in Visual Studio con un editor binario. Il file sorgente era Unicode ma il target era ASCII . Per risolvere il problema, ho modificato sia ReadAllText che WriteAllText con il terzo argomento impostato su System.Text.Encoding.Unicode e da lì Excel è stato in grado di aprire il file aggiornato.

FYR

 private string ExportDatatableToCSV(DataTable dtTable) { StringBuilder sbldr = new StringBuilder(); if (dtTable.Columns.Count != 0) { foreach (DataColumn col in dtTable.Columns) { sbldr.Append(col.ColumnName + ','); } sbldr.Append("\r\n"); foreach (DataRow row in dtTable.Rows) { foreach (DataColumn column in dtTable.Columns) { sbldr.Append(row[column].ToString() + ','); } sbldr.Append("\r\n"); } } return sbldr.ToString(); } 

se tutti i dati sono ancora nella prima cella, significa che l’applicazione con cui hai aperto il file si aspetta un altro delimitatore. MSExcel può gestire la virgola come delimitatore se non diversamente specificato.