c # datável para csv

Alguém poderia me dizer por que o código a seguir não está funcionando. Os dados são salvos no arquivo csv, no entanto, os dados não são separados. Tudo existe dentro da primeira célula de cada linha.

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

Obrigado.

A seguinte versão abreviada abre bem no Excel, talvez o seu problema foi a vírgula à direita

.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 como Tim apontou, se você está em .net> = 4, você pode torná-lo ainda mais curto:

 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 sugerido por Christian, se você quiser manipular caracteres especiais que escapam em campos, substitua o bloco de loop por:

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

E última sugestão, você poderia escrever o conteúdo csv linha por linha em vez de como um documento inteiro, para evitar ter um grande documento na memory.

Eu embrulhei isso em uma class de extensão, que permite chamar:

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

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

Uma nova function de extensão baseada na resposta de Paul Grimshaw. Eu limpei e adicionei a capacidade de lidar com dados inesperados. (Dados vazios, cotações embutidas e vírgulas nos títulos …)

Também retorna uma string que é mais flexível. Retorna Null se o object de tabela não contiver nenhuma estrutura.

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

Você o chama da seguinte maneira:

 var csvData = dataTableOject.ToCsv(); 

Se o seu código de chamada estiver System.Windows.Forms referência à assembly System.Windows.Forms , você poderá considerar uma abordagem radicalmente diferente. Minha estratégia é usar as funções já fornecidas pela estrutura para fazer isso em poucas linhas de código e sem precisar percorrer colunas e linhas. O que o código abaixo faz é programaticamente criar um DataGridView on the fly e defina o DataGridView.DataSource para o DataTable . Em seguida, seleciono programaticamente todas as células (incluindo o header) no DataGridView e chamo DataGridView.GetClipboardContent() , colocando os resultados na Clipboard do Windows. Em seguida, eu ‘ File.WriteAllText() ‘ o conteúdo da área de transferência em uma chamada para File.WriteAllText() , certificando-se de especificar a formatação do ‘colar’ como TextDataFormat.CommaSeparatedValue .

Aqui está o 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 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); } } } 

Observe que eu também me certifico de preservar o conteúdo da área de transferência antes de começar, e restaurá-lo assim que eu terminar, para que o usuário não obtenha um monte de lixo inesperado na próxima vez que o usuário tentar colar. A principal ressalva para essa abordagem é 1) Sua class deve referenciar System.Windows.Forms , o que pode não ser o caso em uma camada de abstração de dados, 2) Seu assembly terá que ser direcionado para a estrutura .NET 4.5, como DataGridView faz não existe em 4.0 e 3) O método falhará se a área de transferência estiver sendo usada por outro processo.

De qualquer forma, essa abordagem pode não ser adequada à sua situação, mas é interessante, no entanto, e pode ser outra ferramenta na sua checkbox de ferramentas.

Eu fiz isso recentemente, mas incluí aspas duplas em torno dos meus valores.

Por exemplo, altere estas duas linhas:

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

Tente alterar sb.Append(Environment.NewLine); para 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()); 

Tente colocar ; em vez de

Espero que ajude

Leia isto e isso ?


Uma melhor implementação seria

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

O erro é o separador de lista.

Em vez de escrever sb.Append(something... + ',') você deve colocar algo como sb.Append(something... + System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator);

Você deve colocar o caractere separador de lista configurado em seu sistema operacional (como no exemplo acima) ou o separador de lista na máquina cliente onde o arquivo será assistido. Outra opção seria configurá-lo no app.config ou web.config como um parâmetro de sua aplicação.

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

Note que o ToList() no final é importante; Eu preciso de algo para forçar uma avaliação de expressão. Se eu fosse o code golfing, eu poderia usar Min() vez disso.

Observe também que o resultado terá uma nova linha no final por causa da última chamada para AppendLine() . Você pode não querer isso. Você pode simplesmente chamar TrimEnd() para removê-lo.

Aqui está um aprimoramento do post do vc-74 que lida com vírgulas da mesma maneira que o Excel. O Excel coloca aspas em torno dos dados, se os dados tiverem uma vírgula, mas não citarem se os dados não tiverem uma vírgula.

  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 escrever em um arquivo, acho que o método a seguir é o mais eficiente e direto: (você pode adicionar aspas se quiser)

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

Possivelmente, a maneira mais fácil será usar:

https://github.com/ukushu/DataExporter

especialmente no caso de seus dados de tabela contendo caracteres /r/n ou símbolo separador dentro de suas células de DataTable.

só você precisa escrever o seguinte 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 o CSV do Excel:

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

No caso de alguém mais se deparar com isso, eu estava usando File.ReadAllText para obter dados CSV e, em seguida, eu modifiquei e escrevi de volta com File.WriteAllText . Os \ R \ n CRLFs estavam bons, mas as guias \ t foram ignoradas quando o Excel o abriu. (Todas as soluções neste segmento até agora usam um delimitador de vírgula, mas isso não importa.) O Bloco de Notas mostrou o mesmo formato no arquivo resultante como na origem. Um Diff até mostrou os arquivos como idênticos. Mas eu tenho uma pista quando abri o arquivo no Visual Studio com um editor binário. O arquivo de origem era Unicode, mas o destino era ASCII . Para corrigir, modifiquei ReadAllText e WriteAllText com o terceiro conjunto de argumentos como System.Text.Encoding.Unicode e, a partir daí, o Excel conseguiu abrir o arquivo atualizado.

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 todos os dados ainda estiverem na primeira célula, isso significa que o aplicativo com o qual você abriu o arquivo está esperando outro delimitador. O MSExcel pode manipular a vírgula como delimitador, a menos que você tenha especificado o contrário.