Menulis data ke file CSV dalam C #

198

Saya mencoba menulis ke csvfile baris demi baris menggunakan bahasa C #. Inilah fungsi saya

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

Seluruh fungsi berjalan di dalam satu lingkaran, dan setiap baris harus ditulis ke csvfile. Dalam kasus saya, baris berikutnya menimpa baris yang ada dan pada akhirnya, saya mendapatkan satu-satunya catatan dalam file csv yang merupakan yang terakhir. Bagaimana saya bisa menulis semua baris dalam csvfile?

rampuriyaaa
sumber
Alih-alih menggunakan StringBuilderdan kemudian membuat satu menyimpan?
Johan
1
Jika ini bukan tugas yang harus Anda penuhi setiap hari, saya sarankan menggunakan LinqPad, yang dilengkapi dengan fungsi praktis untuk menulis data ke dalam csv:Util.WriteCsv (mydatacollection, @"c:\temp\data.csv");
Marco
3
Di samping catatan, pastikan nilai csv Anda dikodekan. Yaitu jika salah satu dari mereka mengandung koma atau karakter end-of-line itu mungkin mengacaukan file Anda. Saya biasanya hanya menggunakan lib pihak ketiga untuk hal-hal csv.
Matthijs Wessels
@MatthijsWessels Ada saran perpustakaan?
Arash Motamedi

Jawaban:

311

MEMPERBARUI

Kembali di hari-hari naif saya, saya menyarankan melakukan ini secara manual (itu adalah solusi sederhana untuk pertanyaan sederhana), namun karena ini menjadi lebih dan lebih populer, saya akan merekomendasikan menggunakan perpustakaan CsvHelper yang melakukan semua pemeriksaan keamanan, dll.

CSV jauh lebih rumit daripada yang disarankan pertanyaan / jawaban.

Jawaban Asli

Karena Anda sudah memiliki perulangan, pertimbangkan untuk melakukannya seperti ini:

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

Atau sesuatu untuk efek ini. Alasan saya adalah: Anda tidak perlu menulis ke file untuk setiap item, Anda hanya akan membuka aliran sekali dan kemudian menulis untuk itu.

Anda bisa mengganti

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

dengan

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

jika Anda ingin menyimpan versi csv sebelumnya di file yang sama

C # 6

Jika Anda menggunakan c # 6.0 maka Anda dapat melakukan hal berikut

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

EDIT

Berikut ini tautan ke pertanyaan yang menjelaskan apa yang Environment.NewLinedilakukan.

Johan
sumber
1
@Rajat no, karena 2 adalah baris baru
Johan
4
Anda juga dapat menyingkirkan {2}dan Environment.NewLinedan menggunakan AppendLinebukannyaAppend
KyleMit
37
Dan apa yang terjadi ketika konten CSV Anda memiliki koma yang perlu diloloskan? Anda butuh penawaran. Dan apa yang terjadi ketika sebuah kutipan perlu diloloskan? Membangun file CSV dengan benar jauh lebih kompleks daripada yang disiratkan oleh jawaban ini.
MgSam
1
Saya mendapat "exceptionType": "System.UnauthorizedAccessException",
Chit Khine
3
Saya sudah mencoba solusi Anda, tetapi bagi saya (budaya Perancis) ini berfungsi lebih baik dengan menggunakan System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparatoralih-alih koma.
A.Pissicat
89

Saya akan sangat menyarankan Anda untuk pergi ke rute yang lebih membosankan. Apalagi jika ukuran file Anda besar.

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()membuka file baru, menulis konten dan kemudian menutup file. Membuka file adalah operasi yang banyak sumber daya, daripada menulis data ke aliran terbuka. Membuka \ menutup file di dalam satu loop akan menyebabkan penurunan kinerja.

Pendekatan yang disarankan oleh Johan memecahkan masalah itu dengan menyimpan semua output dalam memori dan kemudian menulisnya sekali. Namun (dalam kasus file besar) program Anda akan mengkonsumsi sejumlah besar RAM dan bahkan crashOutOfMemoryException

Keuntungan lain dari solusi saya adalah Anda dapat menerapkan jeda \ melanjutkan dengan menyimpan posisi saat ini dalam data input.

upd. Ditempatkan menggunakan di tempat yang tepat

Pavel Murygin
sumber
1
Anda harus meletakkan for for loop di dalam usingpernyataan. Kalau tidak, Anda akan membuka kembali file sepanjang waktu berulang-ulang.
Oliver
2
Jawaban yang bagus. Jawaban yang benar jika Anda menghapus "{2}" dari string format dan ganti "image" dengan "second" di baris yang sama. Juga, jangan lupa untuk menutup penulis dengan w.Close (); w.Flush () tidak diperlukan.
movAX13h
6
Jawaban ini mengabaikan kebutuhan untuk melarikan diri karakter.
MgSam
Bisakah kami juga menambahkan bahwa itu membantu untuk mengatur penulis Anda seperti ini: StreamWriter baru (path, false, Encoding.UTF8)
Charkins12
jika Anda memiliki beberapa ratus juta baris ... haruskah Anda menyiram setiap baris?
Ardianto Suhendar
30

Menulis file csv dengan tangan bisa jadi sulit karena data Anda mungkin mengandung koma dan baris baru. Saya sarankan Anda menggunakan perpustakaan yang ada sebagai gantinya.

Pertanyaan ini menyebutkan beberapa opsi.

Apakah ada perpustakaan CSV pembaca / penulis di C #?

Jeppe Andreasen
sumber
4
Ini adalah satu-satunya jawaban yang benar di sini. Sayang sekali jawaban buruk lainnya yang mencoba melakukannya dengan tangan memiliki begitu banyak upvotes.
MgSam
17

Saya menggunakan solusi dua parse karena sangat mudah dipelihara

// 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());
pengguna3495843
sumber
1
Anda mungkin menemukan Anda mengalami tekanan memori dengan pendekatan ini ketika membangun file besar. Jika Anda menghapus .ToList maka allLines akan menjadi IEnumerbale <objek []>. Anda kemudian dapat memilih itu alih-alih "untuk masing-masing" yaitu alin, Pilih (baris => csv.AppendLine (string.Join (",", line))) yang akan memberi Anda <nomor> IEnumerable <string>. Ini sekarang dapat diteruskan ke File tetapi metode WriteAllLines. Ini sekarang berarti semuanya malas dan Anda tidak perlu menarik semuanya ke dalam memori, tetapi Anda masih mendapatkan sintaks yang Anda sukai.
RhysC
Saya menggunakan metode Anda untuk menulis ke file csv. Itu bagus! Implementasi terbaru saya mengharuskan saya untuk menggunakan File.AppendAllLines(filePath, lines, Encoding.ASCII);bukan File.WriteAllText(filePath, csv.ToString()); Jadi, saya melakukan sesuatu yang sangat kikuk. Setelah membuat csv menggunakan StringBuilder, saya mengonversinya menjadi Daftar <string> untuk mendapatkan IEnumerable untuk panggilan ke AppendAllLines, yang tidak akan menerima csv.ToString () sebagai parameter: List<string> lines = new List<string>(); lines.Add(csv.ToString(0, csv.Length));Apakah Anda memiliki cara yang lebih baik untuk melakukan ini?
Patricia
12

Alih-alih menelepon setiap kali AppendAllText()Anda bisa berpikir tentang membuka file sekali dan kemudian menulis seluruh konten sekali:

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);
    }
}
Oliver
sumber
@ aMazing: Maaf, salah ketik. Sudah diperbaiki.
Oliver
Jawaban ini mencakup ekstensi harus csv. Jika xls, semua data akan muncul dalam satu kolom ... csv itu akan muncul dengan benar di setiap kolom.
GM
10

Cukup gunakan AppendAllText sebagai gantinya:

File.AppendAllText(filePath, csv);

Satu-satunya downside dari AppendAllText adalah ia akan membuang kesalahan ketika file tidak ada, jadi ini harus diperiksa

Maaf, momen pirang sebelum membaca dokumentasi . Bagaimanapun, metode WriteAllText menimpa apa pun yang sebelumnya ditulis dalam file, jika file itu ada.

Perhatikan bahwa kode Anda saat ini tidak menggunakan baris baru yang tepat, misalnya di Notepad Anda akan melihat semuanya sebagai satu baris panjang. Ubah kode ini menjadi baris baru yang benar:

string csv = string.Format("{0},{1}{2}", first, image, Environment.NewLine);
Shadow Wizard adalah Telinga Untuk Anda
sumber
2
@Rajat tidak, ini akan menulis karakter baris baru tepat setelah data gambar Anda.
Shadow Wizard adalah Ear For You
7

Alih-alih menciptakan kembali roda perpustakaan dapat digunakan. CsvHelpersangat bagus untuk membuat dan membaca file csv. Operasi baca dan tulisnya berbasis aliran dan karenanya mendukung operasi dengan sejumlah besar data.


Anda dapat menulis csv Anda seperti berikut ini.

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

Karena perpustakaan menggunakan refleksi, ia akan mengambil jenis apa pun dan menguraikannya langsung.

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

value1, benar

value2, salah


Jika Anda ingin membaca lebih lanjut tentang konfigurasi dan kemungkinan perpustakaan, Anda dapat melakukannya di sini .

NtFreX
sumber
Anda mungkin harus meneruskan CultureInfo ke StreamWriter agar ini berfungsi.
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();
                    }
                }
            }
        }
    }
}
Alejandro Haro
sumber
mengapa tidak ada pembuat string di sini?
Seabizkit
1
RFC mana yang Anda gunakan untuk menulis ini .Replace(",", ";") + ',' :?
vt100
Cara ini terlalu lama jika Anda memiliki lebih dari 20.000 baris dan 30 kolom
Vikrant
3

Menangani Koma

Untuk menangani koma di dalam nilai saat menggunakan string.Format(...), yang berikut ini telah berfungsi untuk saya:

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

Jadi untuk menggabungkannya dengan jawaban Johan, akan terlihat seperti ini:

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

Mengembalikan File CSV

Jika Anda hanya ingin mengembalikan file alih-alih menulisnya ke lokasi, ini adalah contoh bagaimana saya menyelesaikannya:

Dari Prosedur yang Disimpan

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

Dari Daftar

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

Semoga ini bermanfaat.

Trevor Nestman
sumber
2

Ini adalah tutorial sederhana tentang cara membuat file csv menggunakan C # yang dapat Anda edit dan perluas sesuai kebutuhan Anda.

Pertama, Anda harus membuat aplikasi konsol Visual Studio C # baru, ada langkah-langkah yang harus diikuti untuk melakukan ini.

Contoh kode akan membuat file csv bernama MyTest.csv di lokasi yang Anda tentukan. Isi file harus 3 kolom bernama dengan teks di 3 baris pertama.

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);
        }
    }
}
Bloggins
sumber
2
Tautan ke suatu solusi disambut baik, tetapi harap pastikan jawaban Anda bermanfaat tanpanya: tambahkan konteks di sekitar tautan sehingga sesama pengguna Anda akan mengetahui apa itu dan mengapa ada, lalu kutip bagian yang paling relevan dari halaman yang Anda tuju. menautkan kembali jika seandainya halaman target tidak tersedia. Jawaban yang sedikit lebih dari sebuah tautan dapat dihapus.
Shree
Ah saya mengerti. Terima kasih untuk umpan baliknya. Saya telah membuat perubahan yang disorot. Harap pilih.
Bloggins
0

Anda mungkin hanya perlu menambahkan umpan baris "\n\r".

OneWileyDog
sumber
0

Berikut ini adalah pustaka sumber terbuka lainnya untuk membuat file CSV dengan mudah, 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);
}

Untuk informasi lebih lanjut, silakan baca artikel CodeProject tentang penggunaan.

RajN
sumber
0

Salah satu cara sederhana untuk menyingkirkan masalah penimpaan adalah dengan menggunakan File.AppendTextuntuk menambahkan baris pada akhir file sebagai

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);
    }
} 
Vinod Srivastav
sumber
0
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();
dasmon
sumber
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);
}

Ini akan memungkinkan Anda untuk menulis file csv dengan cukup sederhana. Pemakaian:

StreamWriter writer = new StreamWriter("myfile.csv");
writer.WriteCSVLine(new[]{"A", "B"});
trinalbadger587
sumber