在 C# 中将数据写入 CSV 文件

Posted

技术标签:

【中文标题】在 C# 中将数据写入 CSV 文件【英文标题】:Writing data into CSV file in C# 【发布时间】:2013-09-16 10:00:03 【问题描述】:

我正在尝试使用 C# 语言逐行写入csv 文件。这是我的功能

string first = reader[0].ToString();
string second=image.ToString();
string csv = string.Format("0,1\n", first, second);
File.WriteAllText(filePath, csv);

整个函数在一个循环中运行,每一行都应该写入csv 文件。在我的情况下,下一行覆盖现有行,最后,我在 csv 文件中获得了唯一一条记录,这是最后一条记录。如何写入csv 文件中的所有行?

【问题讨论】:

宁可使用StringBuilder,然后进行一次保存? 如果这不是你每天需要完成的任务,我推荐使用 LinqPad,它带有一个方便的功能将数据写入 csv:Util.WriteCsv (mydatacollection, @"c:\temp\data.csv"); 附带说明,请确保您的 csv 值已编码。 IE。如果其中一个包含逗号或行尾字符,则可能会弄乱您的文件。我通常只使用第三方库来存储 csv 文件。 @MatthijsWessels 有什么图书馆建议吗? Are there any CSV readers/writer libraries in C#?的可能重复 【参考方案1】:

更新

在我幼稚的日子里,我建议手动执行此操作(这是一个简单问题的简单解决方案),但是由于这变得越来越流行,我建议使用库 CsvHelper 来完成所有工作安全检查等。

CSV 比问题/答案所暗示的要复杂得多。

原答案

由于您已经有一个循环,请考虑这样做:

//before your loop
    var csv = new StringBuilder();

//in your loop
    var first = reader[0].ToString();
    var second = image.ToString();
    //Suggestion made by KyleMit
    var newLine = string.Format("0,1", first, second);
    csv.AppendLine(newLine);  

//after your loop
    File.WriteAllText(filePath, csv.ToString());

或类似的东西。 我的理由是:您不需要为每个项目都写入文件,您只需打开一次流然后写入它。

你可以替换

File.WriteAllText(filePath, csv.ToString());

File.AppendAllText(filePath, csv.ToString());

如果您想将以前版本的 csv 保留在同一个文件中

C# 6

如果您使用的是 c# 6.0,那么您可以执行以下操作

var newLine = $"first,second"

编辑

这是一个link 的问题,它解释了Environment.NewLine 的作用。

【讨论】:

你也可以去掉2Environment.NewLine,用AppendLine代替Append 当您的 CSV 内容中有一个逗号需要转义时会发生什么?你需要报价。当引用需要转义时会发生什么?正确构建 CSV 文件远比这个答案所暗示的复杂。 我得到了一个“exceptionType”:“System.UnauthorizedAccessException”, 你们都很高兴知道我已经更新了答案以正确的方式做事 我已经尝试过您的解决方案,但对我(法国文化)来说,使用 System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator 而不是逗号会更好。【参考方案2】:

我强烈建议您走更乏味的路线。特别是如果您的文件很大。

using(var w = new StreamWriter(path))

    for( /* your loop */)
    
        var first = yourFnToGetFirst();
        var second = yourFnToGetSecond();
        var line = string.Format("0,1", first, second);
        w.WriteLine(line);
        w.Flush();
    

File.AppendAllText() 打开一个新文件,写入内容,然后关闭文件。与将数据写入打开的流相比,打开文件是一项占用大量资源的操作。在循环中打开\关闭文件会导致性能下降。

Johan 建议的方法通过将所有输出存储在内存中然后写入一次来解决该问题。但是(在大文件的情况下)你的程序会消耗大量的内存,甚至会因为OutOfMemoryException而崩溃

我的解决方案的另一个优点是您可以通过在输入数据中保存当前位置来实现暂停\恢复。

更新。使用在正确的位置

【讨论】:

你应该把你的 for 循环 inside using 语句。否则,您将一次又一次地重新打开文件。 好答案。如果从格式字符串中删除“2”并在同一行中将“image”替换为“second”,则正确答案。另外,不要忘记使用 w.Close(); 关闭作者。不需要 w.Flush()。 这个答案忽略了转义字符的需要。 我们还可以补充一点,这样设置您的编写器会有所帮助:new StreamWriter(path, false, Encoding.UTF8) 如果你有几亿行......你应该刷新每一行吗?【参考方案3】:

手动编写 csv 文件可能很困难,因为您的数据可能包含逗号和换行符。我建议您改用现有的库。

这个问题提到了几个选项。

Are there any CSV readers/writer libraries in C#?

【讨论】:

这是这里唯一合理正确的答案。很遗憾,尝试手动完成的其他糟糕答案有这么多赞成票。【参考方案4】:

我使用两个解析解决方案,因为它很容易维护

// Prepare the values
var allLines = (from trade in proposedTrades
                select new object[] 
                 
                    trade.TradeType.ToString(), 
                    trade.AccountReference, 
                    trade.SecurityCodeType.ToString(), 
                    trade.SecurityCode, 
                    trade.ClientReference, 
                    trade.TradeCurrency, 
                    trade.AmountDenomination.ToString(), 
                    trade.Amount, 
                    trade.Units, 
                    trade.Percentage, 
                    trade.SettlementCurrency, 
                    trade.FOP, 
                    trade.ClientSettlementAccount, 
                    string.Format("\"0\"", trade.Notes),                             
                ).ToList();

// Build the file content
var csv = new StringBuilder();
allLines.ForEach(line => 

    csv.AppendLine(string.Join(",", line));            
);

File.WriteAllText(filePath, csv.ToString());

【讨论】:

在构建大文件时,您可能会发现使用这种方法会遇到内存压力。如果您删除了 .ToList,那么 allLines 将是一个 IEnumerbale。然后,您可以选择它而不是“for eaching”,即 alllines,Select(line => csv.AppendLine(string.Join(",", line))),这将为您提供一个 IEnumerable。这现在可以只传递给 File 但 WriteAllLines 方法。现在这意味着整个事情都是惰性的,您不需要将所有内容都拉入内存,但您仍然可以获得满意的语法。 我正在使用您的方法写入 csv 文件。这很棒!我最新的实现要求我使用File.AppendAllLines(filePath, lines, Encoding.ASCII); 而不是File.WriteAllText(filePath, csv.ToString()); 所以,我正在做一些非常笨拙的事情。使用 StringBuilder 构建 csv 后,我将其转换为 List 以获取用于调用 AppendAllLines 的 IEnumerable,它不会接受 csv.ToString() 作为参数:List<string> lines = new List<string>(); lines.Add(csv.ToString(0, csv.Length)); 你有更好的方法吗? ?【参考方案5】:

您可以考虑打开文件一次,然后将整个内容写入一次,而不是每次都调用AppendAllText()

var file = @"C:\myOutput.csv";

using (var stream = File.CreateText(file))

    for (int i = 0; i < reader.Count(); i++)
    
        string first = reader[i].ToString();
        string second = image.ToString();
        string csvRow = string.Format("0,1", first, second);

        stream.WriteLine(csvRow);
    

【讨论】:

@aMazing:抱歉,打错了。现已修复。 这个答案包括扩展名必须是 csv。如果是xls,数据会全部出现在一列中……csv会正确出现在每一列中。【参考方案6】:

您可以改用AppendAllText

File.AppendAllText(filePath, csv);

正如documentation of WriteAllText 所说:

如果目标文件已经存在,则覆盖

另外,请注意,您当前的代码没有使用正确的新行,例如在记事本中,您会将其全部视为一长行。将代码更改为此具有适当的新行:

string csv = string.Format("0,12", first, image, Environment.NewLine);

【讨论】:

【参考方案7】:

可以使用库,而不是重新发明***。 CsvHelper 非常适合创建和读取 csv 文件。它的读写操作是基于流的,因此也支持大数据量的操作。


你可以像下面这样编写你的 csv。

using(var textWriter = new StreamWriter(@"C:\mypath\myfile.csv"))

    var writer = new CsvWriter(textWriter, CultureInfo.InvariantCulture);
    writer.Configuration.Delimiter = ",";

    foreach (var item in list)
    
        writer.WriteField( "a" );
        writer.WriteField( 2 );
        writer.WriteField( true );
        writer.NextRecord();
    


由于库使用反射,它将采用任何类型并直接解析它。

public class CsvRow

    public string Column1  get; set; 
    public bool Column2  get; set; 

    public CsvRow(string column1, bool column2)
    
        Column1 = column1;
        Column2 = column2;
    


IEnumerable<CsvRow> rows = new [] 
    new CsvRow("value1", true),
    new CsvRow("value2", false)
;
using(var textWriter = new StreamWriter(@"C:\mypath\myfile.csv")

    var writer = new CsvWriter(textWriter, CultureInfo.InvariantCulture);
    writer.Configuration.Delimiter = ",";
    writer.WriteRecords(rows);

值1,真

值2,假


如果您想了解有关库配置和可能性的更多信息,可以这样做here。

【讨论】:

您可能需要将 CultureInfo 传递给 StreamWriter 才能正常工作。【参考方案8】:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;

public partial class CS : System.Web.UI.Page

    protected void ExportCSV(object sender, EventArgs e)
    
        string constr = ConfigurationManager.ConnectionStrings["constr"].ConnectionString;
        using (SqlConnection con = new SqlConnection(constr))
        
            using (SqlCommand cmd = new SqlCommand("SELECT * FROM Customers"))
            
                using (SqlDataAdapter sda = new SqlDataAdapter())
                
                    cmd.Connection = con;
                    sda.SelectCommand = cmd;
                    using (DataTable dt = new DataTable())
                    
                        sda.Fill(dt);

                        //Build the CSV file data as a Comma separated string.
                        string csv = string.Empty;

                        foreach (DataColumn column in dt.Columns)
                        
                            //Add the Header row for CSV file.
                            csv += column.ColumnName + ',';
                        

                        //Add new line.
                        csv += "\r\n";

                        foreach (DataRow row in dt.Rows)
                        
                            foreach (DataColumn column in dt.Columns)
                            
                                //Add the Data rows.
                                csv += row[column.ColumnName].ToString().Replace(",", ";") + ',';
                            

                            //Add new line.
                            csv += "\r\n";
                        

                        //Download the CSV file.
                        Response.Clear();
                        Response.Buffer = true;
                        Response.AddHeader("content-disposition", "attachment;filename=SqlExport.csv");
                        Response.Charset = "";
                        Response.ContentType = "application/text";
                        Response.Output.Write(csv);
                        Response.Flush();
                        Response.End();
                    
                
            
        
    

【讨论】:

为什么这里没有字符串生成器? 您使用哪个 RFC 编写此内容:.Replace(",", ";") + ',' 如果您有超过 20000 行和 30 列,这种方式耗时太长【参考方案9】:

处理逗号

对于在使用 string.Format(...) 时处理值内的逗号,以下方法对我有用:

var newLine = string.Format("\"0\",\"1\",\"2\"",
                              first,
                              second,
                              third                                    
                              );
csv.AppendLine(newLine);

因此,将它与 Johan 的答案结合起来,它看起来像这样:

//before your loop
var csv = new StringBuilder();

//in your loop
  var first = reader[0].ToString();
  var second = image.ToString();
  //Suggestion made by KyleMit
  var newLine = string.Format("\"0\",\"1\"", first, second);
  csv.AppendLine(newLine);  

//after your loop
File.WriteAllText(filePath, csv.ToString());

返回 CSV 文件

如果您只是想返回文件而不是将其写入某个位置,这是我如何完成它的一个示例:

从存储过程

public FileContentResults DownloadCSV()

  // I have a stored procedure that queries the information I need
  SqlConnection thisConnection = new SqlConnection("Data Source=sv12sql;User ID=UI_Readonly;Password=SuperSecure;Initial Catalog=DB_Name;Integrated Security=false");
  SqlCommand queryCommand = new SqlCommand("spc_GetInfoINeed", thisConnection);
  queryCommand.CommandType = CommandType.StoredProcedure;

  StringBuilder sbRtn = new StringBuilder();

  // If you want headers for your file
  var header = string.Format("\"0\",\"1\",\"2\"",
                             "Name",
                             "Address",
                             "Phone Number"
                            );
  sbRtn.AppendLine(header);

  // Open Database Connection
  thisConnection.Open();
  using (SqlDataReader rdr = queryCommand.ExecuteReader())
  
    while (rdr.Read())
    
      // rdr["COLUMN NAME"].ToString();
      var queryResults = string.Format("\"0\",\"1\",\"2\"",
                                        rdr["Name"].ToString(),
                                        rdr["Address".ToString(),
                                        rdr["Phone Number"].ToString()
                                       );
      sbRtn.AppendLine(queryResults);
    
  
  thisConnection.Close();

  return File(new System.Text.UTF8Encoding().GetBytes(sbRtn.ToString()), "text/csv", "FileName.csv");

来自列表

/* To help illustrate */
public static List<Person> list = new List<Person>();

/* To help illustrate */
public class Person

  public string name;
  public string address;
  public string phoneNumber;


/* The important part */
public FileContentResults DownloadCSV()

  StringBuilder sbRtn = new StringBuilder();

  // If you want headers for your file
  var header = string.Format("\"0\",\"1\",\"2\"",
                             "Name",
                             "Address",
                             "Phone Number"
                            );
  sbRtn.AppendLine(header);

  foreach (var item in list)
  
      var listResults = string.Format("\"0\",\"1\",\"2\"",
                                        item.name,
                                        item.address,
                                        item.phoneNumber
                                       );
      sbRtn.AppendLine(listResults);
    
  

  return File(new System.Text.UTF8Encoding().GetBytes(sbRtn.ToString()), "text/csv", "FileName.csv");

希望这会有所帮助。

【讨论】:

【参考方案10】:

这是一个关于使用 C# 创建 csv 文件的简单教程,您可以对其进行编辑和扩展以满足您自己的需要。

首先,您需要创建一个新的 Visual Studio C# 控制台应用程序,执行此操作需要遵循一些步骤。

示例代码将在您指定的位置创建一个名为 MyTest.csv 的 csv 文件。文件的内容应该是 3 个命名列,前 3 行包含文本。

https://tidbytez.com/2018/02/06/how-to-create-a-csv-file-with-c/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace CreateCsv

    class Program
    
        static void Main()
        
            // Set the path and filename variable "path", filename being MyTest.csv in this example.
            // Change SomeGuy for your username.
            string path = @"C:\Users\SomeGuy\Desktop\MyTest.csv";

            // Set the variable "delimiter" to ", ".
            string delimiter = ", ";

            // This text is added only once to the file.
            if (!File.Exists(path))
            
                // Create a file to write to.
                string createText = "Column 1 Name" + delimiter + "Column 2 Name" + delimiter + "Column 3 Name" + delimiter + Environment.NewLine;
                File.WriteAllText(path, createText);
            

            // This text is always added, making the file longer over time
            // if it is not deleted.
            string appendText = "This is text for Column 1" + delimiter + "This is text for Column 2" + delimiter + "This is text for Column 3" + delimiter + Environment.NewLine;
            File.AppendAllText(path, appendText);

            // Open the file to read from.
            string readText = File.ReadAllText(path);
            Console.WriteLine(readText);
        
    

【讨论】:

欢迎提供解决方案链接,但请确保您的答案在没有它的情况下有用:add context around the link 这样您的其他用户就会知道它是什么以及为什么会出现,然后引用最相关的您链接到的页面的一部分,以防目标页面不可用。 Answers that are little more than a link may be deleted. 啊,我明白了。感谢您的反馈。我已突出显示更改。请投票。【参考方案11】:
public static class Extensions

    public static void WriteCSVLine(this StreamWriter writer, IEnumerable<string> fields)
    
        const string q = @"""";
        writer.WriteLine(string.Join(",",
            fields.Select(
                v => (v.Contains(',') || v.Contains('"') || v.Contains('\n') || v.Contains('\r')) ? $"qv.Replace(q, q + q)q" : v
                )));
    

    public static void WriteCSVLine(this StreamWriter writer, params string[] fields) => WriteCSVLine(writer, (IEnumerable<string>)fields);

这应该允许您非常简单地编写一个 csv 文件。用法:

StreamWriter writer = new ("myfile.csv");
writer.WriteCSVLine("A", "B"); // A,B

【讨论】:

【参考方案12】:

您可能只需要添加一个换行符"\n\r"

【讨论】:

【参考方案13】:

这是另一个轻松创建 CSV 文件的开源库,Cinchoo ETL

List<dynamic> objs = new List<dynamic>();

dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = @"Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);

dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Tom";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);

using (var parser = new ChoCSVWriter("emp.csv").WithFirstLineHeader())

    parser.Write(objs);

更多信息,请阅读CodeProject关于使用的文章。

【讨论】:

【参考方案14】:

摆脱覆盖问题的一种简单方法是使用File.AppendText 在文件末尾追加一行

void Main()

    using (System.IO.StreamWriter sw = System.IO.File.AppendText("file.txt"))
              
        string first = reader[0].ToString();
        string second=image.ToString();
        string csv = string.Format("0,1\n", first, second);
        sw.WriteLine(csv);
    
 

【讨论】:

【参考方案15】:
enter code here

string string_value=string.Empty;

        for (int i = 0; i < ur_grid.Rows.Count; i++)
        
            for (int j = 0; j < ur_grid.Rows[i].Cells.Count; j++)
            
                if (!string.IsNullOrEmpty(ur_grid.Rows[i].Cells[j].Text.ToString()))
                
                    if (j > 0)
                        string_value= string_value+ "," + ur_grid.Rows[i].Cells[j].Text.ToString();
                    else
                    
                        if (string.IsNullOrEmpty(string_value))
                            string_value= ur_grid.Rows[i].Cells[j].Text.ToString();
                        else
                            string_value= string_value+ Environment.NewLine + ur_grid.Rows[i].Cells[j].Text.ToString();
                    
                
            
        


        string where_to_save_file = @"d:\location\Files\sample.csv";
        File.WriteAllText(where_to_save_file, string_value);

        string server_path = "/site/Files/sample.csv";
        Response.ContentType = ContentType;
        Response.AppendHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(server_path));
        Response.WriteFile(server_path);
        Response.End();

【讨论】:

以上是关于在 C# 中将数据写入 CSV 文件的主要内容,如果未能解决你的问题,请参考以下文章

在 S3 中将每个分区数据写入单个文件中

在pyspark(2.2.0)中将CSV文件写入AWS时如何分配访问控制列表(ACL)?

如何在 for 循环中将结果写入多个 CSV 文件

在 CSV 文件中写入 C# 对象列表 [关闭]

在c#中将XML文件转换为csv文件格式

在 PHP 中将 CSV 写入不带附件的文件