Ghi dữ liệu vào tệp CSV bằng C #


197

Tôi đang cố gắng viết thành một csvhàng theo từng hàng bằng ngôn ngữ C #. Đây là chức năng của tôi

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

Toàn bộ hàm chạy bên trong một vòng lặp và mỗi hàng sẽ được ghi vào csvtệp. Trong trường hợp của tôi, hàng tiếp theo ghi đè lên hàng hiện có và cuối cùng, tôi nhận được một bản ghi duy nhất trong tệp csv là bản cuối cùng. Làm thế nào tôi có thể viết tất cả các hàng trong csvtập tin?


Thay vì sử dụng một StringBuildervà sau đó thực hiện một tiết kiệm?
Johan

1
Nếu đây không phải là nhiệm vụ mà bạn cần hoàn thành hàng ngày, tôi khuyên bạn nên sử dụng LinqPad, đi kèm với chức năng tiện dụng để ghi dữ liệu vào csv:Util.WriteCsv (mydatacollection, @"c:\temp\data.csv");
Marco

3
Trên một lưu ý phụ, đảm bảo giá trị csv của bạn được mã hóa. Tức là nếu một trong số chúng chứa dấu phẩy hoặc ký tự cuối dòng thì nó có thể làm hỏng tệp của bạn. Tôi thường chỉ sử dụng lib của bên thứ ba cho công cụ csv.
Matthijs Wessels

@MatthijsWessels Bất kỳ đề xuất thư viện?
Arash Motamedi

Câu trả lời:


311

CẬP NHẬT

Quay trở lại những ngày ngây thơ của tôi, tôi đã đề nghị làm điều này bằng tay (đó là một giải pháp đơn giản cho một câu hỏi đơn giản), tuy nhiên do điều này ngày càng trở nên phổ biến, tôi khuyên bạn nên sử dụng thư viện CsvHelper để kiểm tra an toàn, v.v.

CSV phức tạp hơn nhiều so với những gì câu hỏi / câu trả lời gợi ý.

Câu trả lời gốc

Khi bạn đã có một vòng lặp, hãy xem xét thực hiện như thế này:

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

Hoặc một cái gì đó để hiệu ứng này. Lý do của tôi là: bạn sẽ không cần phải ghi vào tệp cho mỗi mục, bạn sẽ chỉ được mở luồng một lần và sau đó viết vào đó.

Bạn có thể thay thế

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

với

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

nếu bạn muốn giữ các phiên bản trước của csv trong cùng một tệp

C 6

Nếu bạn đang sử dụng c # 6.0 thì bạn có thể làm như sau

var newLine = $"{first},{second}"

BIÊN TẬP

Đây là một liên kết đến một câu hỏi giải thích những gì Environment.NewLinelàm.


1
@Rajat không, vì 2 là một dòng mới
Johan

4
Bạn cũng có thể thoát khỏi {2}Environment.NewLinevà sử dụng AppendLinethay vìAppend
KyleMit

37
Và điều gì xảy ra khi nội dung CSV của bạn có dấu phẩy cần thoát? Bạn cần báo giá. Và điều gì xảy ra khi một trích dẫn cần thoát? Xây dựng chính xác các tệp CSV phức tạp hơn nhiều so với câu trả lời này.
MgSam

1
Tôi đã nhận được "ngoại lệ": "System.UnauthorizedAccessException",
Chit Khine

3
Tôi đã thử giải pháp của bạn, nhưng đối với tôi (văn hóa Pháp) nó hoạt động tốt hơn bằng cách sử dụng System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparatorthay vì dấu phẩy.
A.Pissicat

89

Tôi rất muốn giới thiệu bạn để đi con đường tẻ nhạt hơn. Đặc biệt nếu kích thước tập tin của bạn lớn.

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()mở một tệp mới, viết nội dung và sau đó đóng tệp. Mở tệp là một hoạt động tốn nhiều tài nguyên, hơn là ghi dữ liệu vào luồng mở. Mở \ đóng tệp trong vòng lặp sẽ làm giảm hiệu suất.

Cách tiếp cận được đề xuất bởi Johan giải quyết vấn đề đó bằng cách lưu trữ tất cả đầu ra trong bộ nhớ và sau đó viết nó một lần. Tuy nhiên (trong trường hợp tệp lớn), chương trình của bạn sẽ tiêu tốn một lượng lớn RAM và thậm chí bị sậpOutOfMemoryException

Một ưu điểm khác của giải pháp của tôi là bạn có thể thực hiện tạm dừng \ tiếp tục bằng cách lưu vị trí hiện tại trong dữ liệu đầu vào.

cập nhật. Đặt ở vị trí phù hợp


1
Bạn nên đặt cho vòng lặp bên trong các usingtuyên bố. Nếu không, bạn sẽ mở lại tập tin hết lần này đến lần khác.
Oliver

2
Câu trả lời tốt. Câu trả lời đúng nếu bạn xóa "{2}" khỏi chuỗi định dạng và thay thế "hình ảnh" bằng "giây" trong cùng một dòng. Ngoài ra, đừng quên đóng nhà văn bằng w.Close (); w.Flush () là không cần thiết.
MovAX13h

6
Câu trả lời này bỏ qua sự cần thiết phải thoát khỏi nhân vật.
MgSam

Chúng tôi cũng có thể thêm rằng nó giúp đặt nhà văn của bạn như thế này: StreamWriter mới (đường dẫn, sai, Encoding.UTF8)
Charkins12

nếu bạn có vài trăm triệu dòng ... bạn có nên tuôn ra từng dòng không?
Ardianto SuhWiki

30

Viết tệp csv bằng tay có thể khó khăn vì dữ liệu của bạn có thể chứa dấu phẩy và dòng mới. Tôi đề nghị bạn sử dụng một thư viện hiện có để thay thế.

Câu hỏi này đề cập đến một vài lựa chọn.

Có thư viện trình đọc / ghi CSV nào trong C # không?


4
Đây là câu trả lời hợp lý duy nhất ở đây. Thật xấu hổ khi những câu trả lời tồi tệ khác cố gắng làm điều đó bằng tay có rất nhiều sự ủng hộ.
MgSam

17

Tôi sử dụng giải pháp hai phân tích vì nó rất dễ bảo trì

// 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
Bạn có thể thấy bạn gặp áp lực bộ nhớ với phương pháp này khi xây dựng các tệp lớn. Nếu bạn đã xóa .ToList thì allLines sẽ là IEnumerbale <object []>. Sau đó, bạn có thể chọn trên đó thay vì "cho mỗi lần", ví dụ như allines, Chọn (line => csv.AppendLine (string.Join (",", line)) sẽ cung cấp cho bạn một chuỗi <chuỗi>. Điều này bây giờ có thể được chuyển đến Tệp nhưng phương thức WriteAllLines. Điều này bây giờ có nghĩa là toàn bộ điều này là lười biếng và bạn không cần phải kéo mọi thứ vào bộ nhớ, nhưng bạn vẫn nhận được cú pháp bạn hài lòng.
RhysC

Tôi đang sử dụng phương pháp của bạn để ghi vào tệp csv. Thật tuyệt vời! Việc triển khai mới nhất của tôi yêu cầu tôi sử dụng File.AppendAllLines(filePath, lines, Encoding.ASCII);thay vì File.WriteAllText(filePath, csv.ToString()); vậy, tôi đang làm một việc gì đó rất vụng về. Sau khi xây dựng csv bằng StringBuilder, tôi chuyển đổi nó thành Danh sách <chuỗi> để có được một IEnumerable cho cuộc gọi đến AppendAllLines, điều này sẽ không chấp nhận csv.ToString () làm tham số: List<string> lines = new List<string>(); lines.Add(csv.ToString(0, csv.Length));Bạn có cách nào tốt hơn để làm điều này không?
Patricia

12

Thay vì gọi mỗi lần AppendAllText()bạn có thể nghĩ về việc mở tệp một lần và sau đó viết toàn bộ nội dung một lần:

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

@aMazed: Xin lỗi, lỗi đánh máy. Hiện đã được sửa.
Oliver

Câu trả lời này bao gồm phần mở rộng phải là csv. Nếu là xls, tất cả dữ liệu sẽ xuất hiện trong một cột ... csv nó sẽ xuất hiện chính xác trong mỗi cột.
gman

10

Thay vào đó, chỉ cần sử dụng AppendAllText:

File.AppendAllText(filePath, csv);

Nhược điểm duy nhất của AppendAllText là nó sẽ gây ra lỗi khi tệp không tồn tại, vì vậy điều này phải được kiểm tra

Xin lỗi, khoảnh khắc tóc vàng trước khi đọc tài liệu . Dù sao, phương thức WriteAllText sẽ ghi đè lên bất cứ thứ gì đã được ghi trước đó trong tệp, nếu tệp tồn tại.

Lưu ý rằng mã hiện tại của bạn không sử dụng các dòng mới phù hợp, ví dụ như trong Notepad, bạn sẽ thấy tất cả là một dòng dài. Thay đổi mã thành này để có dòng mới phù hợp:

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

2
@Rajat không, điều này sẽ viết ký tự dòng mới ngay sau dữ liệu hình ảnh của bạn.
Shadow Wizard là tai cho bạn

7

Thay vì phát minh lại bánh xe, một thư viện có thể được sử dụng. CsvHelperlà tuyệt vời để tạo và đọc các tập tin csv. Các hoạt động đọc và ghi của nó dựa trên luồng và do đó cũng hỗ trợ các hoạt động với một lượng lớn dữ liệu.


Bạn có thể viết csv của bạn như sau.

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

Khi thư viện đang sử dụng sự phản chiếu, nó sẽ lấy bất kỳ loại nào và phân tích nó trực tiếp.

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

giá trị1, đúng

giá trị 2, sai


Nếu bạn muốn đọc thêm về cấu hình thư viện và khả năng bạn có thể làm như vậy ở đây .


Bạn có thể phải chuyển CultureInfo cho StreamWriter để làm việc này.
alif

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

Tại sao không có trình tạo chuỗi ở đây?
Seabizkit

1
RFC nào bạn đã sử dụng để viết này : .Replace(",", ";") + ',' ?
vt100

Cách này mất quá nhiều thời gian nếu bạn có hơn 20000 hàng và 30 cột
Vikrant

3

Xử lý dấu phẩy

Để xử lý dấu phẩy bên trong các giá trị khi sử dụng string.Format(...), phần sau đây đã giúp tôi:

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

Vì vậy, để kết hợp nó với câu trả lời của Johan, nó sẽ giống như thế này:

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

Trả lại tệp CSV

Nếu bạn chỉ đơn giản muốn trả lại tệp thay vì ghi nó vào một vị trí, thì đây là một ví dụ về cách tôi hoàn thành nó:

Từ một thủ tục lưu trữ

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

Từ một danh sách

/* 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");
}

Hy vọng điều này là hữu ích.


2

Đây là một hướng dẫn đơn giản về cách tạo tệp csv bằng C # mà bạn sẽ có thể chỉnh sửa và mở rộng để phù hợp với nhu cầu của riêng bạn.

Trước tiên, bạn sẽ cần tạo một ứng dụng bảng điều khiển Visual Studio C # mới, có các bước để làm theo để làm điều này.

Mã ví dụ sẽ tạo một tệp csv có tên MyTest.csv ở vị trí bạn chỉ định. Nội dung của tệp phải là 3 cột có tên với văn bản trong 3 hàng đầu tiên.

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
Liên kết đến một giải pháp được hoan nghênh, nhưng vui lòng đảm bảo câu trả lời của bạn hữu ích mà không cần đến nó: thêm ngữ cảnh xung quanh liên kết để người dùng của bạn sẽ có ý tưởng về nó là gì và tại sao lại có, sau đó trích dẫn phần có liên quan nhất của trang bạn ' liên kết lại trong trường hợp trang đích không có sẵn. Câu trả lời ít hơn một liên kết có thể bị xóa.
Shree

Ah tôi thấy. Cảm ơn vì bạn đã phản hồi. Tôi đã thực hiện các thay đổi được tô sáng. Xin hãy bỏ phiếu.
Bloggins


0

Đây là một thư viện mã nguồn mở khác để tạo tệp CSV dễ dàng, 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);
}

Để biết thêm thông tin, xin vui lòng đọc bài viết CodeProject về cách sử dụng.


0

Một cách đơn giản để thoát khỏi vấn đề ghi đè là sử dụng File.AppendTextđể nối thêm dòng ở cuối tệp dưới dạng

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

chuỗi 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);
}

Điều này sẽ cho phép bạn viết một tệp csv khá đơn giản. Sử dụng:

StreamWriter writer = new StreamWriter("myfile.csv");
writer.WriteCSVLine(new[]{"A", "B"});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.