Làm cách nào để tạo tệp CSV Excel C #? [đóng cửa]


132

Tôi đang tìm một lớp để tạo tệp CSV Excel.

Các tính năng dự kiến:

  • Sử dụng cực kỳ đơn giản
  • Thoát dấu phẩy và dấu ngoặc kép để excel xử lý chúng tốt
  • Xuất khẩu ngày và thời gian theo định dạng bằng múi giờ

Bạn có biết lớp nào có khả năng này không?


12
tốt hơn để đặt câu hỏi trong phần CÂU HỎI, sau đó đăng câu trả lời của riêng bạn trong phần TRẢ LỜI. Hãy chắc chắn để thêm các thẻ và từ khóa trong câu hỏi để làm cho nó có thể tìm kiếm được.
Cheeso

QUAN TRỌNG: bạn cũng nên thêm dấu ngoặc kép khi có CARRIAGE TRẢ LẠI trong "giá trị".
Alex

Cảm ơn @Chris, một đề xuất nếu tôi có thể, mã này có thể ném KeyNotFoundException, vui lòng xem câu trả lời của tôi.
Joseph

Ví dụ tốt nhất của nó ... nhưng làm thế nào tôi có thể thêm hai bảng trong một tệp, có nghĩa là tôi có một bảng gồm hai hàng và bảng khác là 10 hàng và cả hai đều có tên cột duy nhất. Tôi muốn thêm hai bảng hàng trên và sau khoảng cách của hai dòng tôi muốn thêm bảng thứ hai.
Floki

Câu trả lời:


92

Phiên bản hơi khác tôi đã viết bằng cách sử dụng sự phản ánh cho nhu cầu của tôi. Tôi đã phải xuất một danh sách các đối tượng sang csv. Trong trường hợp ai đó muốn sử dụng nó cho tương lai.

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;

        }
    }

Mẫu sử dụng: (cập nhật trên mỗi bình luận)

CsvExport<BusinessObject> csv= new CsvExport<BusinessObject>(GetBusinessObjectList());
Response.Write(csv.Export());

5
Nó giống như thế này: Danh sách <BusinessObject> x = Danh sách mới <BusinessObject> (); CsvExport <BusinessObject> x = new CsvExport <BusinessObject> (MUsers);
ẩn

5
Giao diện INullable của bạn đến từ đâu?
Kilhoffer

Ví dụ tốt nhất của nó ... nhưng làm thế nào tôi có thể thêm hai bảng trong một tệp, có nghĩa là tôi có một bảng gồm hai hàng và bảng khác là 10 hàng và cả hai đều có tên cột duy nhất. Tôi muốn thêm hai bảng hàng trên và sau khoảng cách của hai dòng tôi muốn thêm bảng thứ hai.
Floki

2
Tôi biết bài viết gốc là từ năm 2011, vì vậy tôi không chắc liệu nó có khả thi trong phiên bản .NET đã được sử dụng trước đó không. Nhưng tại sao không loại bỏ public string Export()phương thức và thay đổi phương thức khác thành public string Export(bool includeHeaderLiner = true)(với giá trị tham số mặc định). Một lần nữa, tôi không chắc liệu các tham số mặc định có khả dụng trong năm 2011 hay không, nhưng mã hiện tại chỉ mang tính chất onorthodox đối với tôi.
Kevin Cruijssen

19

Xin hãy tha thứ cho tôi

Nhưng tôi nghĩ rằng một kho lưu trữ nguồn mở công khai là một cách tốt hơn để chia sẻ mã và đóng góp, sửa chữa và bổ sung như "Tôi đã sửa lỗi này, tôi đã sửa nó"

Vì vậy, tôi đã tạo một kho lưu trữ git đơn giản từ mã của trình khởi động chủ đề và tất cả các bổ sung:

https://github.com/jitbit/CsvExport

Tôi cũng đã thêm một vài sửa chữa hữu ích cho mình. Mọi người có thể thêm đề xuất, chia nó để đóng góp, v.v ... Gửi cho tôi dĩa của bạn để tôi hợp nhất chúng lại vào repo.

Tái bút Tôi đã đăng tất cả các thông báo bản quyền cho Chris. @Chris nếu bạn phản đối ý tưởng này - hãy cho tôi biết, tôi sẽ giết nó.


11

Một giải pháp tốt khác để đọc và ghi tệp CSV là trình trợ giúp tệp (nguồn mở).


Lưu ý: Hỗ trợ Excel chỉ dành cho các kịch bản cơ bản : Hỗ trợ Excel được triển khai hiện tại chỉ dành cho các kịch bản cơ bản. Nếu bạn cần định dạng tùy chỉnh, biểu đồ, v.v. bạn phải tìm mã tùy chỉnh. Chúng tôi khuyên bạn nên sử dụng trực tiếp thư viện NPOI
AK

6

Làm thế nào về việc sử dụng chuỗi.Join thay vì tất cả các vòng lặp foreach?


String.Join chỉ hoạt động trên chuỗi [], trong khi tôi đang sử dụng một số tính năng của Danh sách <chuỗi>.
Chris

12
String.Join("," , List<string>)cũng hoạt động.
Mất trí nhớ

6

Nếu bất cứ ai muốn tôi chuyển đổi nó thành một phương thức mở rộng trên 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;
    }
}

5

công việc tuyệt vời trên lớp này Đơn giản và dễ sử dụng. Tôi đã sửa đổi lớp để bao gồm một tiêu đề trong hàng đầu tiên của xuất khẩu; Hình tôi sẽ chia sẻ:

sử dụng:

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

lớp học:

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


3

Tôi đã thêm ExportToStream để csv không phải lưu vào ổ cứng trước.

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

3

Tôi đã thêm

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

}

Mã trước không hoạt động với các phiên bản .NET cũ. Đối với phiên bản 3.5 của khung, hãy sử dụng phiên bản khác này:

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

2

Cảm ơn rất nhiều vì điều đó! Tôi đã sửa đổi lớp thành:

  • sử dụng một dấu phân cách biến, thay vì mã hóa cứng
  • thay thế tất cả các Lines mới (\ n \ r \ n \ r) trong MakeValueCsvFriendly

Mã số:

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

        }

    }


1

Lớp ban đầu có một vấn đề và đó là nếu bạn muốn thêm một cột mới, bạn sẽ nhận được KeyNotFoundException về phương thức Xuất. Ví dụ:

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

Để giải quyết vấn đề này và sử dụng ý tưởng @PalCowboy sử dụng sự phản chiếu, tôi đã sửa đổi mã để cho phép thêm các hàng không có cùng cột. Bạn có thể sử dụng các thể hiện của các lớp ẩn danh. Ví dụ:

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

Bạn có thể tải mã nguồn tại đây CsvExporter . Hãy sử dụng và sửa đổi.

Bây giờ, nếu tất cả các hàng bạn muốn viết đều thuộc cùng một lớp, tôi đã tạo lớp chung CsvWriter.cs , có mức sử dụng RAM hiệu suất tốt hơn và lý tưởng để ghi các tệp lớn. Thêm vào đó, nó cho phép bạn thêm trình định dạng vào loại dữ liệu bạn muốn . Một ví dụ về sử dụng:

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

0

Bạn chỉ cần 1 chức năng để làm điều này. Chỉ có bạn phải làm là tạo một thư mục trong trình khám phá giải pháp của bạn và lưu trữ tệp csv ở đó và sau đó xuất tệp đó cho người dùng.

Như trong trường hợp của tôi, tôi có một thư mục tải về. Đầu tiên tôi xuất tất cả nội dung của mình vào thư mục đó và sau đó xuất nó cho người dùng. Để xử lý answer.end, tôi đã sử dụng ThreadAdortException. Vì vậy, nó là một chức năng chính hãng và làm việc 100% trong giải pháp của tôi.

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....
}
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.