Bagaimana cara membuat file CSV Excel C #? [Tutup]

132

Saya mencari kelas untuk membuat file CSV Excel.

Fitur yang diharapkan:

  • Sangat mudah digunakan
  • Mengosongkan koma dan mengutip jadi excel menanganinya dengan baik
  • Tanggal dan data ekspor dalam format zona waktu-bukti

Apakah Anda tahu ada kelas yang mampu melakukan ini?

Chris
sumber
12
lebih baik mengajukan pertanyaan di bagian PERTANYAAN, dan kemudian posting jawaban Anda sendiri di bagian JAWABAN. Pastikan untuk menambahkan tag dan kata kunci dalam pertanyaan agar dapat dicari.
Cheeso
PENTING: Anda juga harus menambahkan tanda kutip ketika ada PENGEMBALIAN CARRIAGE di "nilai".
Alex
Terima kasih @ Chris, saran jika saya bisa, kode ini dapat melempar KeyNotFoundException, silakan lihat jawaban saya.
Joseph
Contoh terbaiknya ... tetapi bagaimana saya bisa menambahkan dua tabel dalam file tunggal, berarti saya memiliki satu tabel dua baris dan tabel lainnya adalah 10 baris dan keduanya memiliki nama kolom yang unik. Saya ingin menambahkan dua baris tabel di atas dan setelah celah dua baris saya ingin menambahkan tabel kedua.
Floki

Jawaban:

92

Versi yang sedikit berbeda saya tulis menggunakan refleksi untuk kebutuhan saya. Saya harus mengekspor daftar objek ke csv. Jika seseorang ingin menggunakannya untuk masa depan.

public class CsvExport<T> where T: class
    {
        public List<T> Objects;

        public CsvExport(List<T> objects)
        {
            Objects = objects;
        }

        public string Export()
        {
            return Export(true);
        }

        public string Export(bool includeHeaderLine)
        {

            StringBuilder sb = new StringBuilder();
            //Get properties using reflection.
            IList<PropertyInfo> propertyInfos = typeof(T).GetProperties();

            if (includeHeaderLine)
            {
                //add header line.
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    sb.Append(propertyInfo.Name).Append(",");
                }
                sb.Remove(sb.Length - 1, 1).AppendLine();
            }

            //add value for each property.
            foreach (T obj in Objects)
            {               
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    sb.Append(MakeValueCsvFriendly(propertyInfo.GetValue(obj, null))).Append(",");
                }
                sb.Remove(sb.Length - 1, 1).AppendLine();
            }

            return sb.ToString();
        }

        //export to a file.
        public void ExportToFile(string path)
        {
            File.WriteAllText(path, Export());
        }

        //export as binary data.
        public byte[] ExportToBytes()
        {
            return Encoding.UTF8.GetBytes(Export());
        }

        //get the csv value for field.
        private string MakeValueCsvFriendly(object value)
        {
            if (value == null) return "";
            if (value is Nullable && ((INullable)value).IsNull) return "";

            if (value is DateTime)
            {
                if (((DateTime)value).TimeOfDay.TotalSeconds == 0)
                    return ((DateTime)value).ToString("yyyy-MM-dd");
                return ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss");
            }
            string output = value.ToString();

            if (output.Contains(",") || output.Contains("\""))
                output = '"' + output.Replace("\"", "\"\"") + '"';

            return output;

        }
    }

Contoh penggunaan: (diperbarui per komentar)

CsvExport<BusinessObject> csv= new CsvExport<BusinessObject>(GetBusinessObjectList());
Response.Write(csv.Export());
coder net
sumber
5
Lebih seperti ini: Daftar <BusinessObject> x = Daftar baru <BusinessObject> (); CsvExport <BusinessObject> x = CsvExport baru <BusinessObject> (MUsers);
disembunyikan
5
Dari mana antarmuka INullable Anda berasal?
Kilhoffer
Contoh terbaiknya ... tetapi bagaimana saya bisa menambahkan dua tabel dalam file tunggal, berarti saya memiliki satu tabel dua baris dan tabel lainnya adalah 10 baris dan keduanya memiliki nama kolom yang unik. Saya ingin menambahkan dua baris tabel di atas dan setelah celah dua baris saya ingin menambahkan tabel kedua.
Floki
2
Saya tahu posting asli berasal dari 2011, jadi saya tidak yakin apakah itu mungkin dalam versi .NET yang digunakan saat itu. Tetapi mengapa tidak menghapus public string Export()metode dan mengubah metode lain ke public string Export(bool includeHeaderLiner = true)(dengan nilai parameter default). Sekali lagi, saya tidak yakin apakah parameter default tersedia pada 2011, tetapi kode saat ini hanya terlihat seperti biasa bagi saya.
Kevin Cruijssen
19

Tolong maafkan saya

Tapi saya pikir repositori open-source publik adalah cara yang lebih baik untuk berbagi kode dan memberikan kontribusi, dan koreksi, dan tambahan seperti "Saya memperbaiki ini, saya memperbaikinya"

Jadi saya membuat repositori git sederhana dari kode topik-pemula dan semua tambahan:

https://github.com/jitbit/CsvExport

Saya juga menambahkan beberapa perbaikan yang berguna. Semua orang dapat menambahkan saran, garpu untuk berkontribusi dll. Dll. Kirim saya garpu Anda jadi saya menggabungkan mereka kembali ke repo.

PS. Saya memposting semua pemberitahuan hak cipta untuk Chris. @ Chris jika Anda menentang ide ini - beri tahu saya, saya akan membunuhnya.

jitbit
sumber
11

Solusi lain yang baik untuk membaca dan menulis file CSV adalah filehelpers (open source).

Jelle
sumber
NB: Dukungan Excel hanya untuk skenario dasar : Dukungan Excel yang diterapkan saat ini hanya untuk skenario dasar. Jika Anda membutuhkan pemformatan khusus, bagan, dll. Anda harus mencari kode khusus. Sangat disarankan untuk langsung menggunakan perpustakaan NPOI
AK
6

Bagaimana kalau menggunakan string.Bergabung dengan semua loop foreach?

Hinek
sumber
String.Join hanya berfungsi pada string [], sedangkan saya menggunakan beberapa fitur Daftar <string>.
Chris
12
String.Join("," , List<string>)bekerja juga.
Demensik
6

Jika ada yang ingin saya mengonversikan ini ke metode ekstensi di IEnumerable:

public static class ListExtensions
{
    public static string ExportAsCSV<T>(this IEnumerable<T> listToExport, bool includeHeaderLine, string delimeter)
    {
        StringBuilder sb = new StringBuilder();

        IList<PropertyInfo> propertyInfos = typeof(T).GetProperties();

        if (includeHeaderLine)
        {
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                sb.Append(propertyInfo.Name).Append(",");
            }
            sb.Remove(sb.Length - 1, 1).AppendLine();
        }

        foreach (T obj in listToExport)
        {
            T localObject = obj;

            var line = String.Join(delimeter, propertyInfos.Select(x => SanitizeValuesForCSV(x.GetValue(localObject, null), delimeter)));

            sb.AppendLine(line);
        }

        return sb.ToString();
    }

    private static string SanitizeValuesForCSV(object value, string delimeter)
    {
        string output;

        if (value == null) return "";

        if (value is DateTime)
        {
            output = ((DateTime)value).ToLongDateString();
        }
        else
        {
            output = value.ToString();                
        }

        if (output.Contains(delimeter) || output.Contains("\""))
            output = '"' + output.Replace("\"", "\"\"") + '"';

        output = output.Replace("\n", " ");
        output = output.Replace("\r", "");

        return output;
    }
}
KeyboardCowboy
sumber
5

kerja bagus di kelas ini. Sederhana dan mudah digunakan. Saya memodifikasi kelas untuk memasukkan judul di baris pertama ekspor; pikir saya akan berbagi:

menggunakan:

CsvExport myExport = new CsvExport();
myExport.addTitle = String.Format("Name: {0},{1}", lastName, firstName));

kelas:

public class CsvExport
{
    List<string> fields = new List<string>();

    public string addTitle { get; set; } // string for the first row of the export

    List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
    Dictionary<string, object> currentRow
    {
        get
        {
            return rows[rows.Count - 1];
        }
    }

    public object this[string field]
    {
        set
        {
            if (!fields.Contains(field)) fields.Add(field);
            currentRow[field] = value;
        }
    }

    public void AddRow()
    {
        rows.Add(new Dictionary<string, object>());
    }

    string MakeValueCsvFriendly(object value)
    {
        if (value == null) return "";
        if (value is Nullable && ((INullable)value).IsNull) return "";
        if (value is DateTime)
        {
            if (((DateTime)value).TimeOfDay.TotalSeconds == 0)
                return ((DateTime)value).ToString("yyyy-MM-dd");
            return ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss");
        }
        string output = value.ToString();
        if (output.Contains(",") || output.Contains("\""))
            output = '"' + output.Replace("\"", "\"\"") + '"';
        return output;

    }

    public string Export()
    {
        StringBuilder sb = new StringBuilder();

        // if there is a title
        if (!string.IsNullOrEmpty(addTitle))
        {
            // escape chars that would otherwise break the row / export
            char[] csvTokens = new[] { '\"', ',', '\n', '\r' };

            if (addTitle.IndexOfAny(csvTokens) >= 0)
            {
                addTitle = "\"" + addTitle.Replace("\"", "\"\"") + "\"";
            }
            sb.Append(addTitle).Append(",");
            sb.AppendLine();
        }


        // The header
        foreach (string field in fields)
        sb.Append(field).Append(",");
        sb.AppendLine();

        // The rows
        foreach (Dictionary<string, object> row in rows)
        {
            foreach (string field in fields)
                sb.Append(MakeValueCsvFriendly(row[field])).Append(",");
            sb.AppendLine();
        }

        return sb.ToString();
    }

    public void ExportToFile(string path)
    {
        File.WriteAllText(path, Export());
    }

    public byte[] ExportToBytes()
    {
        return Encoding.UTF8.GetBytes(Export());
    }
}
gnome
sumber
3

Saya menambahkan ExportToStream sehingga csv tidak harus menyimpan ke hard drive terlebih dahulu.

public Stream ExportToStream()
{
    MemoryStream stream = new MemoryStream();
    StreamWriter writer = new StreamWriter(stream);
    writer.Write(Export(true));
    writer.Flush();
    stream.Position = 0;
    return stream;
}
Jay Greene
sumber
3

aku sudah menambahkan

public void ExportToFile(string path, DataTable tabela)
{

     DataColumnCollection colunas = tabela.Columns;

     foreach (DataRow linha in tabela.Rows)
     {

           this.AddRow();

           foreach (DataColumn coluna in colunas)

           {

               this[coluna.ColumnName] = linha[coluna];

           }

      }
      this.ExportToFile(path);

}

Kode sebelumnya tidak berfungsi dengan versi .NET lama. Untuk versi 3.5 framework gunakan versi lain ini:

        public void ExportToFile(string path)
    {
        bool abort = false;
        bool exists = false;
        do
        {
            exists = File.Exists(path);
            if (!exists)
            {
                if( !Convert.ToBoolean( File.CreateText(path) ) )
                        abort = true;
            }
        } while (!exists || abort);

        if (!abort)
        {
            //File.OpenWrite(path);
            using (StreamWriter w = File.AppendText(path))
            {
                w.WriteLine("hello");
            }

        }

        //File.WriteAllText(path, Export());
    }
Rodrigo Araujo
sumber
2

Terima kasih banyak untuk itu! Saya memodifikasi kelas menjadi:

  • gunakan pembatas variabel, alih-alih dalam bentuk kode
  • mengganti semua baris baru (\ n \ r \ n \ r) di MakeValueCsvFriendly

Kode:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.SqlTypes;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

    public class CsvExport
    {

        public char delim = ';';
        /// <summary>
        /// To keep the ordered list of column names
        /// </summary>
        List<string> fields = new List<string>();

        /// <summary>
        /// The list of rows
        /// </summary>
        List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();

        /// <summary>
        /// The current row
        /// </summary>
        Dictionary<string, object> currentRow { get { return rows[rows.Count - 1]; } }

        /// <summary>
        /// Set a value on this column
        /// </summary>
        public object this[string field]
        {
            set
            {
                // Keep track of the field names, because the dictionary loses the ordering
                if (!fields.Contains(field)) fields.Add(field);
                currentRow[field] = value;
            }
        }

        /// <summary>
        /// Call this before setting any fields on a row
        /// </summary>
        public void AddRow()
        {
            rows.Add(new Dictionary<string, object>());
        }

        /// <summary>
        /// Converts a value to how it should output in a csv file
        /// If it has a comma, it needs surrounding with double quotes
        /// Eg Sydney, Australia -> "Sydney, Australia"
        /// Also if it contains any double quotes ("), then they need to be replaced with quad quotes[sic] ("")
        /// Eg "Dangerous Dan" McGrew -> """Dangerous Dan"" McGrew"
        /// </summary>
        string MakeValueCsvFriendly(object value)
        {
            if (value == null) return "";
            if (value is INullable && ((INullable)value).IsNull) return "";
            if (value is DateTime)
            {
                if (((DateTime)value).TimeOfDay.TotalSeconds == 0)
                    return ((DateTime)value).ToString("yyyy-MM-dd");
                return ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss");
            }
            string output = value.ToString();
            if (output.Contains(delim) || output.Contains("\""))
                output = '"' + output.Replace("\"", "\"\"") + '"';
            if (Regex.IsMatch(output,  @"(?:\r\n|\n|\r)"))
                output = string.Join(" ", Regex.Split(output, @"(?:\r\n|\n|\r)"));
            return output;
        }

        /// <summary>
        /// Output all rows as a CSV returning a string
        /// </summary>
        public string Export()
        {
            StringBuilder sb = new StringBuilder();

            // The header
            foreach (string field in fields)
                sb.Append(field).Append(delim);
            sb.AppendLine();

            // The rows
            foreach (Dictionary<string, object> row in rows)
            {
                foreach (string field in fields)
                    sb.Append(MakeValueCsvFriendly(row[field])).Append(delim);
                sb.AppendLine();
            }

            return sb.ToString();
        }

        /// <summary>
        /// Exports to a file
        /// </summary>
        public void ExportToFile(string path)
        {
            File.WriteAllText(path, Export());
        }

        /// <summary>
        /// Exports as raw UTF8 bytes
        /// </summary>
        public byte[] ExportToBytes()
        {
            return Encoding.UTF8.GetBytes(Export());

        }

    }
Munchies
sumber
1

Kelas asli memiliki masalah, dan itu adalah jika Anda ingin menambahkan kolom baru, Anda akan menerima KeyNotFoundException pada metode Ekspor. Sebagai contoh:

static void Main(string[] args)
{
    var export = new CsvExport();

    export.AddRow();
    export["Region"] = "New York, USA";
    export["Sales"] = 100000;
    export["Date Opened"] = new DateTime(2003, 12, 31);

    export.AddRow();
    export["Region"] = "Sydney \"in\" Australia";
    export["Sales"] = 50000;
    export["Date Opened"] = new DateTime(2005, 1, 1, 9, 30, 0);
    export["Balance"] = 3.45f;  //Exception is throwed for this new column

    export.ExportToFile("Somefile.csv");
}

Untuk mengatasi ini, dan menggunakan ide @KeyboardCowboy menggunakan refleksi, saya memodifikasi kode untuk memungkinkan menambahkan baris yang tidak memiliki kolom yang sama. Anda dapat menggunakan instance dari kelas anonim. Sebagai contoh:

static void Main(string[] args)
{
    var export = new CsvExporter();

    export.AddRow(new {A = 12, B = "Empty"});
    export.AddRow(new {A = 34.5f, D = false});

    export.ExportToFile("File.csv");
}

Anda dapat mengunduh kode sumber di sini CsvExporter . Jangan ragu untuk menggunakan dan memodifikasi.

Sekarang, jika semua baris yang ingin Anda tulis adalah dari kelas yang sama, saya membuat kelas generik CsvWriter.cs , yang memiliki penggunaan RAM kinerja yang lebih baik dan ideal untuk menulis file besar. Ditambah itu memungkinkan Anda menambahkan pemformat ke tipe data yang Anda inginkan . Contoh penggunaan:

class Program
{
    static void Main(string[] args)
    {
        var writer = new CsvWriter<Person>("Persons.csv");

        writer.AddFormatter<DateTime>(d => d.ToString("MM/dd/yyyy"));

        writer.WriteHeaders();
        writer.WriteRows(GetPersons());

        writer.Flush();
        writer.Close();
    }

    private static IEnumerable<Person> GetPersons()
    {
        yield return new Person
            {
                FirstName = "Jhon", 
                LastName = "Doe", 
                Sex = 'M'
            };

        yield return new Person
            {
                FirstName = "Jhane", 
                LastName = "Doe",
                Sex = 'F',
                BirthDate = DateTime.Now
            };
        }
    }


    class Person
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }

        public char Sex  { get; set; }

        public DateTime BirthDate { get; set; }
    }
Joseph
sumber
0

Anda hanya perlu 1 fungsi untuk melakukan ini. Yang harus Anda lakukan adalah membuat folder di explorer solusi Anda dan menyimpan file csv di sana lalu mengekspor file itu ke pengguna.

Seperti dalam kasus saya, saya memiliki folder yang diunduh. Pertama saya mengekspor semua konten saya ke direktori itu dan kemudian mengekspornya ke pengguna. Untuk penanganan response.end, saya menggunakan ThreadAbortException. Jadi ini adalah 100% fungsi asli dan berfungsi dalam solusi saya.

protected void lnkExport_OnClick(object sender, EventArgs e)
{

    string filename = strFileName = "Export.csv";

    DataTable dt = obj.GetData();  

// call the content and load it into the datatable

    strFileName = Server.MapPath("Downloads") + "\\" + strFileName;

// creating a file in the downloads folder in your solution explorer

    TextWriter tw = new StreamWriter(strFileName);

// using the built in class textwriter for writing your content in the exporting file

    string strData = "Username,Password,City";

// above line is the header for your exported file. So add headings for your coloumns in excel(.csv) file and seperate them with ","

    strData += Environment.NewLine;

// setting the environment to the new line

    foreach (DataRow dr in dt.Rows)
    {
       strData += dr["Username"].ToString() + "," + dr["Password"].ToString() + "," +      dr["City"].ToString();
       strData += Environment.NewLine;
    }

// everytime when loop execute, it adds a line into the file
    tw.Write(strData);

// writing the contents in file
    tw.Close();

// closing the file
    Response.Redirect("Downloads/" + filename);

// exporting the file to the user as a popup to save as....
}
Suhaib Janjua
sumber