如何将 DataTable 转换为 CSV?
有人可以告诉我为什么下面的代码不起作用。数据保存到 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());
Could somebody please tell me why the following code is not working. The data is saved into the csv file, however the data is not separated. It all exists within the first cell of each row.
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());
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(19)
以下较短的版本在 Excel 中可以正常打开,也许您的问题是尾随逗号
.net = 3.5
.net >= 4.0
正如蒂姆指出的,如果您使用 .net>=4,您可以使其更短
: Christian建议,如果您想处理字段中转义的特殊字符,请将循环块替换为:
最后一个建议,您可以逐行写入csv内容而不是作为整个文档,以避免内存中出现大文档。
The following shorter version opens fine in Excel, maybe your issue was the trailing comma
.net = 3.5
.net >= 4.0
And as Tim pointed out, if you are on .net>=4, you can make it even shorter:
As suggested by Christian, if you want to handle special characters escaping in fields, replace the loop block by:
And last suggestion, you could write the csv content line by line instead of as a whole document, to avoid having a big document in memory.
调用:
我将其包装到一个扩展类中,它允许您在任何 DataTable 上
I wrapped this up into an extension class, which allows you to call:
on any DataTable.
基于 Paul Grimshaw 答案的新扩展函数。我清理了它并添加了处理意外数据的能力。 (标题中的空数据、嵌入式引号和逗号...)
它还返回一个更灵活的字符串。如果表对象不包含任何结构,则返回 Null。
您可以这样称呼它:
A new extension function based on Paul Grimshaw's answer. I cleaned it up and added the ability to handle unexpected data. (Empty Data, Embedded Quotes, and comma's in the headings...)
It also returns a string which is more flexible. It returns Null if the table object does not contain any structure.
You call it as follows:
如果您的调用代码引用 System.Windows.Forms 程序集,您可能会考虑采用完全不同的方法。
我的策略是使用框架已经提供的功能,以很少的代码行来完成此任务,而无需循环遍历列和行。下面的代码所做的是以编程方式动态创建一个
DataGridView
并将DataGridView.DataSource
设置为DataTable
。接下来,我以编程方式选择DataGridView
中的所有单元格(包括标题)并调用DataGridView.GetClipboardContent()
,将结果放入 WindowsClipboard
代码>.然后,我将剪贴板的内容“粘贴”到对File.WriteAllText()
的调用中,确保将“粘贴”的格式指定为TextDataFormat.CommaSeparatedValue
。这是代码:
请注意,我还确保在开始之前保留剪贴板的内容,并在完成后恢复它,这样用户下次尝试粘贴时就不会收到一堆意外的垃圾。这种方法的主要注意事项是 1) 您的类必须引用 System.Windows.Forms,这在数据抽象层中可能不是这种情况,2) 您的程序集必须以 . NET 4.5 框架,因为 DataGridView 在 4.0 中不存在,并且 3) 如果剪贴板正被另一个进程使用,该方法将失败。
无论如何,这种方法可能不适合您的情况,但它仍然很有趣,并且可以成为您工具箱中的另一个工具。
If your calling code is referencing the
System.Windows.Forms
assembly, you may consider a radically different approach.My strategy is to use the functions already provided by the framework to accomplish this in very few lines of code and without having to loop through columns and rows. What the code below does is programmatically create a
DataGridView
on the fly and set theDataGridView.DataSource
to theDataTable
. Next, I programmatically select all the cells (including the header) in theDataGridView
and callDataGridView.GetClipboardContent()
, placing the results into the WindowsClipboard
. Then, I 'paste' the contents of the clipboard into a call toFile.WriteAllText()
, making sure to specify the formatting of the 'paste' asTextDataFormat.CommaSeparatedValue
.Here is the code:
Notice I also make sure to preserve the contents of the clipboard before I begin, and restore it once I'm done, so the user does not get a bunch of unexpected garbage next time the user tries to paste. The main caveats to this approach is 1) Your class has to reference
System.Windows.Forms
, which may not be the case in a data abstraction layer, 2) Your assembly will have to be targeted for .NET 4.5 framework, as DataGridView does not exist in 4.0, and 3) The method will fail if the clipboard is being used by another process.Anyways, this approach may not be right for your situation, but it is interesting none the less, and can be another tool in your toolbox.
我最近这样做了,但在我的值周围添加了双引号。
例如,更改这两行:
I did this recently but included double quotes around my values.
For example, change these two lines:
尝试将 sb.Append(Environment.NewLine); 更改为 sb.AppendLine();。
Try changing
sb.Append(Environment.NewLine);
tosb.AppendLine();
.4行代码:
注意最后的
ToList()
很重要;我需要一些东西来强制进行表达式求值。如果我正在打代码高尔夫球,我可以使用Min()
代替。另请注意,由于最后一次调用
AppendLine()
,结果末尾将有一个换行符。你可能不想要这个。您只需调用TrimEnd()
即可将其删除。4 lines of code:
Note that the
ToList()
at the end is important; I need something to force an expression evaluation. If I was code golfing, I could useMin()
instead.Also note that the result will have a newline at the end because of the last call to
AppendLine()
. You may not want this. You can simply callTrimEnd()
to remove it.要写入文件,我认为以下方法是最有效和最直接的:(如果需要,可以添加引号)
To write to a file, I think the following method is the most efficient and straightforward: (You can add quotes if you want)
尝试使用
;
而不是,
希望有帮助
Try to put
;
instead of,
Hope it helps
错误是列表分隔符。
不要编写
sb.Append(something... + ',')
你应该写类似sb.Append(something... + System.Globalization.CultureInfo.CurrentCulture.TextInfo. ListSeparator);
您必须放置在操作系统中配置的列表分隔符(如上例所示),或者将列表分隔符放置在要监视文件的客户端计算机中。另一种选择是在 app.config 或 web.config 中将其配置为应用程序的参数。
The error is the list separator.
Instead of writing
sb.Append(something... + ',')
you should put something likesb.Append(something... + System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator);
You must put the list separator character configured in your operating system (like in the example above), or the list separator in the client machine where the file is going to be watched. Another option would be to configure it in the app.config or web.config as a parammeter of your application.
阅读此和这个?
A better implementation would be
Read this and this?
A better implementation would be
模仿 Excel CSV:
To mimic Excel CSV:
这是对 vc-74 帖子的增强,它以与 Excel 相同的方式处理逗号。如果数据有逗号,Excel 会在数据两边加上引号,但如果数据没有逗号,则不会引用数据。
Here is an enhancement to vc-74's post that handles commas the same way Excel does. Excel puts quotes around data if the data has a comma but doesn't quote if the data doesn't have a comma.
这是我的解决方案,基于 Paul Grimshaw 和 安东尼·VO。
我已在 Github 上的 C# 项目中提交了 代码。
我的主要贡献是消除了显式创建和操作
StringBuilder
的情况,而只使用IEnumerable
。这避免了在内存中分配大缓冲区。这种方法与将 IEnumerable 转换为 DataTable 完美结合
Here is my solution, based on previous answers by Paul Grimshaw and Anthony VO.
I've submitted the code in a C# project on Github.
My main contribution is to eliminate explicitly creating and manipulating a
StringBuilder
and instead working only withIEnumerable
. This avoids the allocation of a big buffer in memory.This approach combines nicely with converting
IEnumerable
to DataTable as asked here.可能,最简单的方法是使用:
https://github.com/ukushu/DataExporter
< strong>尤其是在数据表的数据在数据表单元格内包含
/r/n
字符或分隔符的情况下。几乎所有其他答案都不适用于此类单元格。您只需要编写以下代码:
Possibly, most easy way will be to use:
https://github.com/ukushu/DataExporter
especially in case of your data of datatable containing
/r/n
characters or separator symbol inside of your dataTable cells. Almost all of other answers will not work with such cells.only you need is to write the following code:
大多数现有答案很容易导致
OutOfMemoryException
,因此我决定编写自己的答案。不要这样做:
使用 DataSet + StringBuilder 会导致数据一次占用内存 3 倍:
DataSet
StringBuilder< /code>
相反,您应该将每一行分别写入
FileStream
。无需在内存中创建整个 CSV。更好的是,使用 DataReader 而不是 DataSet。这样,您可以从数据库中一一读取数十亿条记录,并将其一一写入文件。
如果您不介意使用 CSV 的外部库,我可以推荐最流行的 CsvHelper,它没有依赖性。
Most existing answers can easily cause
OutOfMemoryException
, so I decided to write my own answer.DON' T DO THIS:
using a DataSet + StringBuilder causes the data to occupy the memory 3x at once:
DataSet
StringBuilder
StringBuilder.ToString()
;Instead you should write each row to a
FileStream
separately. There is no need to create the whole CSV in memory.Even better, use a DataReader instead DataSet. That way you can read from database billions of records one by one a write the to a file one by one.
If you don't mind using an external library for CSV, I can recommend the most popular CsvHelper, which has no dependencies.
为了防止其他人偶然发现这一点,我使用 File.ReadAllText 获取 CSV 数据,然后修改它并使用 File.WriteAllText 将其写回。 \r\n CRLF 没问题,但当 Excel 打开它时 \t 选项卡被忽略。 (到目前为止,该线程中的所有解决方案都使用逗号分隔符,但这并不重要。)记事本在结果文件中显示的格式与源文件中的格式相同。差异甚至显示文件是相同的。但当我使用二进制编辑器在 Visual Studio 中打开该文件时,我得到了线索。源文件是 Unicode,但目标文件是 ASCII。为了解决这个问题,我修改了 ReadAllText 和 WriteAllText,将第三个参数设置为 System.Text.Encoding.Unicode,然后 Excel 就能够打开更新后的文件。
In case anyone else stumbles on this, I was using File.ReadAllText to get CSV data and then I modified it and wrote it back with File.WriteAllText. The \r\n CRLFs were fine but the \t tabs were ignored when Excel opened it. (All solutions in this thread so far use a comma delimiter but that doesn't matter.) Notepad showed the same format in the resulting file as in the source. A Diff even showed the files as identical. But I got a clue when I opened the file in Visual Studio with a binary editor. The source file was Unicode but the target was ASCII. To fix, I modified both ReadAllText and WriteAllText with third argument set as System.Text.Encoding.Unicode, and from there Excel was able to open the updated file.