有人可以告诉我为什么以下代码无效。数据将保存到csv文件中,但数据不会分开。它全部存在于每行的第一个单元格中。
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());
感谢。
答案 0 :(得分:201)
以下较短版本在Excel中打开正常,也许您的问题是尾随逗号
StringBuilder sb = new StringBuilder();
string[] columnNames = dt.Columns.Cast<DataColumn>().
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&gt; = 4,你可以缩短它:
StringBuilder sb = new StringBuilder();
IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().
Select(column => column.ColumnName);
sb.AppendLine(string.Join(",", columnNames));
foreach (DataRow row in dt.Rows)
{
IEnumerable<string> fields = row.ItemArray.Select(field => field.ToString());
sb.AppendLine(string.Join(",", fields));
}
File.WriteAllText("test.csv", sb.ToString());
正如Christian所建议的那样,如果你想处理在字段中转义的特殊字符,请将循环块替换为:
foreach (DataRow row in dt.Rows)
{
IEnumerable<string> fields = row.ItemArray.Select(field =>
string.Concat("\"", field.ToString().Replace("\"", "\"\""), "\""));
sb.AppendLine(string.Join(",", fields));
}
最后一个建议是,您可以逐行编写csv内容而不是整个文档,以避免在内存中包含大文档。
答案 1 :(得分:32)
我把它包装成一个扩展类,允许你调用:
myDataTable.WriteToCsvFile("C:\\MyDataTable.csv");
在任何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());
}
}
答案 2 :(得分:19)
基于Paul Grimshaw答案的新扩展功能。我清理了它并添加了处理意外数据的能力。 (空数据,嵌入式引号和标题中的逗号......)
它还返回一个更灵活的字符串。如果表对象不包含任何结构,则返回Null。
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();
}
您可以按如下方式调用它:
var csvData = dataTableOject.ToCsv();
答案 3 :(得分:8)
如果您的调用代码引用了System.Windows.Forms
程序集,您可能会考虑采用完全不同的方法。
我的策略是使用框架已经提供的函数在很少的代码行中实现这一点,而不必遍历列和行。以下代码的作用是以编程方式动态创建DataGridView
并将DataGridView.DataSource
设置为DataTable
。接下来,我以编程方式选择DataGridView
中的所有单元格(包括标题)并调用DataGridView.GetClipboardContent()
,将结果放入Windows Clipboard
。然后,我将剪贴板的内容粘贴到File.WriteAllText()
的调用中,确保将“粘贴”的格式指定为TextDataFormat.CommaSeparatedValue
。
以下是代码:
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);
}
}
}
注意我还要确保在开始之前保留剪贴板的内容,并在完成后恢复它,这样用户在下次用户尝试粘贴时就不会收到大量意外垃圾。这种方法的主要警告是1)你的类必须引用System.Windows.Forms
,这可能不是数据抽象层中的情况,2)你的程序集必须以.NET 4.5框架为目标,就像DataGridView一样在4.0中不存在,3)如果另一个进程正在使用剪贴板,该方法将失败。
无论如何,这种方法可能不适合您的情况,但它仍然很有趣,并且可以是您工具箱中的另一个工具。
答案 4 :(得分:6)
我最近这样做了,但是在我的价值观中加了双引号。
例如,更改以下两行:
sb.Append("\"" + col.ColumnName + "\",");
...
sb.Append("\"" + row[i].ToString() + "\",");
答案 5 :(得分:6)
尝试将sb.Append(Environment.NewLine);
更改为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());
答案 6 :(得分:5)
尝试放置;
而不是,
希望有所帮助
答案 7 :(得分:5)
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());
答案 8 :(得分:5)
错误是列表分隔符。
不应该写sb.Append(something... + ',')
,而应该添加sb.Append(something... + System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator);
您必须在操作系统中配置列表分隔符(如上例所示),或者在将要监视文件的客户端计算机中放置列表分隔符。另一种选择是在app.config或web.config中将其配置为应用程序的参数。
答案 9 :(得分:4)
4行代码:
public static string ToCSV(DataTable tbl)
{
StringBuilder strb = new StringBuilder();
//column headers
strb.AppendLine(string.Join(",", tbl.Columns.Cast<DataColumn>()
.Select(s => "\"" + s.ColumnName + "\"")));
//rows
tbl.AsEnumerable().Select(s => strb.AppendLine(
string.Join(",", s.ItemArray.Select(
i => "\"" + i.ToString() + "\"")))).ToList();
return strb.ToString();
}
请注意,最后的ToList()
很重要;我需要一些东西来强制进行表达式评估。如果我打码打高尔夫球,我可以改用Min()
。
另请注意,由于上次调用AppendLine()
,结果最后会有换行符。你可能不想要这个。您只需致电TrimEnd()
即可将其删除。
答案 10 :(得分:3)
这是对vc-74帖子的增强,它以与Excel相同的方式处理逗号。如果数据有逗号,Excel会在数据周围加上引号,但如果数据没有逗号,则不引用数据。
public static string ToCsv(this DataTable inDataTable, bool inIncludeHeaders = true)
{
var builder = new StringBuilder();
var columnNames = inDataTable.Columns.Cast<DataColumn>().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;
}
答案 11 :(得分:1)
要写入文件,我认为以下方法是最有效和最简单的方法:(如果需要,可以添加引号)
public static void WriteCsv(DataTable dt, string path)
{
using (var writer = new StreamWriter(path)) {
writer.WriteLine(string.Join(",", dt.Columns.Cast<DataColumn>().Select(dc => dc.ColumnName)));
foreach (DataRow row in dt.Rows) {
writer.WriteLine(string.Join(",", row.ItemArray));
}
}
}
答案 12 :(得分:1)
可能最简单的方法是使用:
https://github.com/ukushu/DataExporter
特别是在您的dataTable单元格中包含/r/n
个字符或分隔符符号的数据表数据的情况下。
只需要编写以下代码:
Csv csv = new Csv("\t");//Needed delimiter
var columnNames = dt.Columns.Cast<DataColumn>().
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();
答案 13 :(得分:1)
StringBuilder sb = new StringBuilder();
SaveFileDialog fileSave = new SaveFileDialog();
IEnumerable<string> columnNames = tbCifSil.Columns.Cast<DataColumn>().
Select(column => column.ColumnName);
sb.AppendLine(string.Join(",", columnNames));
foreach (DataRow row in tbCifSil.Rows)
{
IEnumerable<string> fields = row.ItemArray.Select(field =>string.Concat("\"", field.ToString().Replace("\"", "\"\""), "\""));
sb.AppendLine(string.Join(",", fields));
}
fileSave.ShowDialog();
File.WriteAllText(fileSave.FileName, sb.ToString());
答案 14 :(得分:1)
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();
}
答案 15 :(得分:1)
模仿Excel CSV:
public static string Convert(DataTable dt)
{
StringBuilder sb = new StringBuilder();
IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().
Select(column => column.ColumnName);
sb.AppendLine(string.Join(",", columnNames));
foreach (DataRow row in dt.Rows)
{
IEnumerable<string> 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();
}
答案 16 :(得分:0)
如果有其他人偶然发现,我使用 File.ReadAllText 来获取CSV数据,然后我修改了它并用 File.WriteAllText 将其写回来。 \ r \ n CRLF很好,但Excel打开时忽略了\ t标签。 (到目前为止,此线程中的所有解决方案都使用逗号分隔符,但这并不重要。)记事本在结果文件中显示与源中相同的格式。 Diff甚至将文件显示为相同。但是当我使用二进制编辑器在Visual Studio中打开文件时,我得到了一个线索。源文件是Unicode,但目标是 ASCII 。为了解决这个问题,我修改了ReadAllText和WriteAllText,并将第三个参数设置为 System.Text.Encoding.Unicode ,从那里Excel就能够打开更新的文件。
答案 17 :(得分:0)
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();
}
答案 18 :(得分:0)
这是我的解决方案,基于Paul Grimshaw和Anthony VO的先前答案。 我已经提交了code in a C# project on Github。
我的主要贡献是消除显式创建和处理StringBuilder
的工作,而只使用IEnumerable
。这样可以避免在内存中分配大缓冲区。
public static class Util
{
public static string EscapeQuotes(this string self) {
return self?.Replace("\"", "\"\"") ?? "";
}
public static string Surround(this string self, string before, string after) {
return $"{before}{self}{after}";
}
public static string Quoted(this string self, string quotes = "\"") {
return self.Surround(quotes, quotes);
}
public static string QuotedCSVFieldIfNecessary(this string self) {
return (self == null) ? "" : self.Contains('"') ? self.Quoted() : self;
}
public static string ToCsvField(this string self) {
return self.EscapeQuotes().QuotedCSVFieldIfNecessary();
}
public static string ToCsvRow(this IEnumerable<string> self){
return string.Join(",", self.Select(ToCsvField));
}
public static IEnumerable<string> ToCsvRows(this DataTable self) {
yield return self.Columns.OfType<object>().Select(c => c.ToString()).ToCsvRow();
foreach (var dr in self.Rows.OfType<DataRow>())
yield return dr.ItemArray.Select(item => item.ToString()).ToCsvRow();
}
public static void ToCsvFile(this DataTable self, string path) {
File.WriteAllLines(path, self.ToCsvRows());
}
}
此方法与将IEnumerable
转换为DataTable as asked here很好地结合在一起。
答案 19 :(得分:0)
DataTable dt = yourData();
StringBuilder csv = new StringBuilder();
int dcCounter = 0;
foreach (DataColumn dc in dt.Columns)
{
csv.Append(dc);
if (dcCounter != dt.Columns.Count - 1)
{
csv.Append(",");
}
dcCounter++;
}
csv.AppendLine();
int numOfDc = dt.Columns.Count;
foreach (DataRow dr in dt.Rows)
{
int colIndex = 0;
while (colIndex <= numOfDc - 1)
{
var colVal = dr[colIndex].ToString();
if (colVal != null && colVal != "")
{
DateTime isDateTime;
if (DateTime.TryParse(colVal, out isDateTime))
{
csv.Append(Convert.ToDateTime(colVal).ToShortDateString());
}
else
{
csv.Append(dr[colIndex]);
}
}
else
{
csv.Append("N/A");
}
if (colIndex != numOfDc - 1)
{
csv.Append(",");
}
colIndex++;
}
csv.AppendLine();
我还需要做一些数据覆盖,这就是为什么有一些“ if else”语句的原因。我需要确保,如果某个字段为空来代替输入“ N / A”,或者如果“日期”字段的格式设置为“ 01/01/1900:00”,那么它将被保存为“ 01/01/1900”代替。
答案 20 :(得分:0)
StringBuilder sb = new StringBuilder();
foreach (DataColumn col in table.Columns)
{
sb.Append(col.ColumnName + ";");
}
foreach (DataRow row in table.Rows)
{
sb.AppendLine();
foreach (DataColumn col in table.Columns)
{
sb.Append($@"{Convert.ToString(row[col])}" + ";");
}
}
File.WriteAllText(path, sb.ToString());
答案 21 :(得分:-1)
如果所有数据仍在第一个单元格中,则表示您打开文件的应用程序需要另一个分隔符。 除非另有说明,否则MSExcel可以将逗号作为分隔符处理。