c # datatable a csv

¿Podría alguien decirme por qué el siguiente código no funciona? Los datos se guardan en el archivo csv, sin embargo, los datos no están separados. Todo existe dentro de la primera celda de cada fila.

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

Gracias.

La siguiente versión más corta se abre bien en Excel, tal vez su problema fue la coma final

.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

Y como Tim señaló, si estás en .net> = 4, puedes hacerlo aún más corto:

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

Como lo sugirió Christian, si desea manejar caracteres especiales que escapen en los campos, reemplace el bloque de bucle por:

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

Y la última sugerencia, podría escribir el contenido CSV línea por línea en lugar de como un documento completo, para evitar tener un gran documento en la memoria.

Envolví esto en una clase de extensión, que te permite llamar:

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

en cualquier 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 nueva función de extensión basada en la respuesta de Paul Grimshaw. Lo limpié y agregué la capacidad de manejar datos inesperados. (Datos vacíos, comillas insertadas y comas en los títulos …)

También devuelve una cadena que es más flexible. Devuelve nulo si el objeto de tabla no contiene ninguna estructura.

  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 llamas de la siguiente manera:

 var csvData = dataTableOject.ToCsv(); 

Si su código de llamada hace referencia al ensamblado System.Windows.Forms , puede considerar un enfoque radicalmente diferente. Mi estrategia es usar las funciones que ya proporciona el marco para lograr esto en muy pocas líneas de código y sin tener que recorrer las columnas y las filas. Lo que hace el código a continuación es crear programáticamente un DataGridView sobre la marcha y establecer el DataGridView.DataSource en la DataTable . A continuación, selecciono programáticamente todas las celdas (incluido el encabezado) en DataGridView y llamo a DataGridView.GetClipboardContent() , colocando los resultados en el Clipboard Windows. Luego, ‘pego’ el contenido del portapapeles en una llamada a File.WriteAllText() , asegurándome de especificar el formato de ‘paste’ como TextDataFormat.CommaSeparatedValue .

Aquí está el código:

 public static void DataTableToCSV(DataTable Table, string Filename) { using(DataGridView dataGrid = new DataGridView()) { // Save the current state of the clipboard so we can restre 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); } } } 

Tenga en cuenta que también me aseguro de preservar el contenido del portapapeles antes de comenzar, y restaurarlo una vez que haya terminado, para que el usuario no obtenga una gran cantidad de basura inesperada la próxima vez que el usuario intente pegar. Las advertencias principales para este enfoque son 1) Su clase tiene que hacer referencia a System.Windows.Forms , que puede no ser el caso en una capa de abstracción de datos, 2) Su ensamblado deberá estar orientado para .NET 4.5 framework, como lo hace DataGridView no existe en 4.0, y 3) El método fallará si el portapapeles está siendo utilizado por otro proceso.

De todos modos, este enfoque puede no ser el adecuado para su situación, pero no deja de ser interesante, y puede ser otra herramienta en su caja de herramientas.

Hice esto recientemente pero incluí comillas dobles alrededor de mis valores.

Por ejemplo, cambie estas dos líneas:

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

Pruebe a cambiar 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()); 

Intenta poner ; en lugar de ,

Espero eso ayude

Lee esto y esto ?


Una mejor implementación sería

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

El error es el separador de lista.

En lugar de escribir sb.Append(something... + ',') debe poner algo como sb.Append(something... + System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator);

Debe poner el carácter de separador de lista configurado en su sistema operativo (como en el ejemplo anterior), o el separador de lista en la máquina del cliente donde se va a ver el archivo. Otra opción sería configurarlo en app.config o web.config como un parámetro de su aplicación.

4 líneas de código:

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

Tenga en cuenta que ToList() al final es importante; Necesito algo para forzar una evaluación de expresión. Si fuera código de golf, podría usar Min() lugar.

También tenga en cuenta que el resultado tendrá una nueva línea al final debido a la última llamada a AppendLine() . Puede que no quieras esto. Simplemente puede llamar a TrimEnd() para eliminarlo.

Aquí hay una mejora de la publicación de vc-74 que maneja las comas de la misma manera que Excel. Excel coloca comillas alrededor de los datos si los datos tienen una coma, pero no cita si los datos no tienen una coma.

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

Para escribir en un archivo, creo que el siguiente método es el más eficiente y directo: (Puede agregar citas si lo desea)

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

Posiblemente, la manera más fácil será usar:

https://github.com/ukushu/DataExporter

especialmente en el caso de los datos de la tabla de datos que contienen /r/n caracteres o el símbolo separador dentro de sus celdas de dataTable.

lo único que necesita es escribir el siguiente código:

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

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

En caso de que alguien más se tropiece con esto, estaba usando File.ReadAllText para obtener datos CSV y luego lo modifiqué y lo escribí de nuevo con File.WriteAllText . Los \ r \ n CRLF estaban bien, pero las \ t tabs se ignoraron cuando Excel lo abrió. (Todas las soluciones en este hilo hasta ahora usan un delimitador de comas, pero eso no importa). El Bloc de notas mostró el mismo formato en el archivo resultante que en el código fuente. A Diff incluso mostró los archivos como idénticos. Pero tengo una pista cuando abrí el archivo en Visual Studio con un editor binario. El archivo de origen era Unicode pero el objective era ASCII . Para solucionarlo, modifiqué tanto ReadAllText como WriteAllText con el tercer argumento establecido como System.Text.Encoding.Unicode , y desde allí Excel pudo abrir el archivo actualizado.

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

si todos los datos aún están en la primera celda, significa que la aplicación con la que abrió el archivo espera otro delimitador. MSExcel puede manejar la coma como delimitador a menos que haya especificado lo contrario.