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


197

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

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文件中的所有行?


宁可使用a StringBuilder,然后保存一次?
Johan 2013年

1
如果这不是您每天需要完成的任务,我建议您使用LinqPad,它带有一个方便的功能,可将数据写入csv:Util.WriteCsv (mydatacollection, @"c:\temp\data.csv");
Marco

3
另外,请确保您的csv值已编码。也就是说,如果其中之一包含逗号或行尾字符,则可能会弄乱您的文件。我通常只将第三方lib用于csv东西。
Matthijs Wessels 2014年

@MatthijsWessels有图书馆建议吗?
阿拉什·莫塔梅迪

可能重复的代码在C#
利亚姆

Answers:


311

更新

在我幼稚的日子里,我建议手动执行此操作(这是对一个简单问题的简单解决方案),但是由于这种方法越来越流行,因此我建议使用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}"

编辑

这是一个指向问题的链接,该链接解释了Environment.NewLine作用。


1
@Rajat不,因为2是换行符
Johan

4
您也可以摆脱{2}Environment.NewLine和使用AppendLine,而不是Append
KyleMit

37
当您的CSV内容中的逗号需要转义时,会发生什么?您需要引号。当报价需要转义时会发生什么?正确构建CSV文件比此答案所暗示的要复杂得多。
MgSam

1
我得到了“ exceptionType”:“ System.UnauthorizedAccessException”,
Chit Khine

3
我已经尝试了您的解决方案,但对我来说(法国文化),通过使用System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator而不是逗号,效果更好。
A.Pissicat '18

89

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

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建议的方法通过将所有输出存储在内存中然后写入一次来解决该问题。但是(如果文件很大)您的程序将消耗大量RAM,甚至崩溃OutOfMemoryException

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

更新。放置在正确的位置


1
你应该把你的for循环using语句。否则,您将一次又一次地重新打开该文件。
奥利弗·2013年

2
好答案。如果从格式字符串中删除“ {2}”,并在同一行中将“ image”替换为“ second”,则正确答案。另外,不要忘记使用w.Close()关闭编写器。w.Flush()不需要。
movAX13h 2014年

6
这个答案忽略了转义字符的需要。
MgSam

我们还可以补充一下,这有助于设置您的
编写器吗

如果您有几亿行...您应该刷新每一行吗?
Ardianto Suhendar '19


17

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

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

1
您可能会发现在构建大文件时使用此方法会遇到内存压力。如果删除了.ToList,则allLines将是IEnumerbale <object []>。然后,您可以选择它而不是“ for eaching”,即allines,Select(line => csv.AppendLine(string.Join(“,”,line)))),这将为您提供IEnumerable <string>。现在可以通过WriteAllLines方法将其传递给File。现在,这意味着整个过程都是懒惰的,您不需要将所有内容都拉到内存中,但是仍然可以获得满意的语法。
RhysC

我正在使用您的方法写入csv文件。这很棒!我的最新实现要求我使用File.AppendAllLines(filePath, lines, Encoding.ASCII);而不是,File.WriteAllText(filePath, csv.ToString()); 所以,我做的事情很笨拙。使用StringBuilder构建csv之后,我将其转换为List <string>以获取对AppendAllLines的调用的IEnumerable,它将不接受csv.ToString()作为参数: List<string> lines = new List<string>(); lines.Add(csv.ToString(0, csv.Length));您有更好的方法吗?
Patricia

12

您不必考虑每次都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数据将正确显示在每一列中。
gman

10

只需使用AppendAllText代替:

File.AppendAllText(filePath, csv);

AppendAllText的唯一缺点是,如果文件不存在,它将抛出错误,因此必须检查

抱歉,在阅读文档之前是金发时刻。无论如何,如果文件存在,则WriteAllText方法将覆盖文件中先前写入的所有内容。

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

string csv = string.Format("{0},{1}{2}", first, image, Environment.NewLine);

2
@Rajat不,这会在您的图像数据之后立即写入换行符。
暗影巫师为您耳边2013年

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

为什么这里没有字符串生成器?
Seabizkit

1
您使用哪个RFC编写此代码 .Replace(",", ";") + ','
vt100

如果您有超过20000行和30列,
Vikrant

6

代替重新发明轮子,可以使用库。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,假


如果您想了解有关库配置和可能性的更多信息,可以在这里进行


您可能必须将CultureInfo传递给StreamWriter才能起作用。
alif

3

处理逗号

为了在使用时处理值内的逗号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");
}

希望这会有所帮助。


2

这是一个有关使用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);
        }
    }
}

2
欢迎使用指向解决方案的链接,但请确保没有该链接的情况下,您的回答是有用的:在链接周围添加上下文,以便您的其他用户可以了解它的含义和含义,然后引用您所使用页面中最相关的部分如果目标页面不可用,请重新链接。只是链接的答案可能会被删除。
Shree

啊,我明白了。感谢您的反馈。我已经突出显示了更改。请投票。
Bloggins


0

这是另一个可以轻松创建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文章。


0

摆脱覆盖问题的一种简单方法是使用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);
    }
} 

0
enter code here

字符串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();

0
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')) ? $"{q}{v.Replace(q, q + q)}{q}" : v
                )));
    }

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

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

StreamWriter writer = new StreamWriter("myfile.csv");
writer.WriteCSVLine(new[]{"A", "B"});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.