Wie erstelle ich eine CSV Excel Datei C #? [geschlossen]

132

Ich suche eine Klasse zum Erstellen von CSV-Excel-Dateien.

Erwartete Funktionen:

  • Extrem einfach zu bedienen
  • Entkommt Kommas und Anführungszeichen, damit Excel sie gut handhabt
  • Exportiert Datum und Uhrzeit im zeitzonensicheren Format

Kennen Sie eine Klasse, die dazu in der Lage ist?

Chris
quelle
12
Stellen Sie die Frage besser im Teil FRAGE und veröffentlichen Sie dann Ihre eigene Antwort im Teil ANTWORT. Stellen Sie sicher, dass Sie der Frage Tags und Schlüsselwörter hinzufügen, damit sie durchsuchbar ist.
Cheeso
WICHTIG: Sie sollten auch Anführungszeichen hinzufügen, wenn der "Wert" CARRIAGE RETURNS enthält.
Alex
Danke @Chris, ein Vorschlag, wenn ich darf, dieser Code kann eine KeyNotFoundException auslösen, siehe meine Antwort.
Joseph
Das beste Beispiel ... aber wie kann ich zwei Tabellen in einer einzigen Datei hinzufügen? Das bedeutet, dass ich eine Tabelle mit zwei Zeilen habe und die andere Tabelle 10 Zeilen und beide einen eindeutigen Spaltennamen haben. Ich möchte oben und danach eine Tabelle mit zwei Zeilen hinzufügen Lücke von zwei Zeilen Ich möchte zweite Tabelle hinzufügen.
Floki

Antworten:

92

Etwas andere Version, die ich mit Reflection für meine Bedürfnisse geschrieben habe. Ich musste eine Liste von Objekten nach CSV exportieren. Für den Fall, dass jemand es für die Zukunft nutzen möchte.

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;

        }
    }

Verwendungsbeispiel: (aktualisiert pro Kommentar)

CsvExport<BusinessObject> csv= new CsvExport<BusinessObject>(GetBusinessObjectList());
Response.Write(csv.Export());
Codierernetz
quelle
5
Es war eher so: List <BusinessObject> x = new List <BusinessObject> (); CsvExport <BusinessObject> x = neues CsvExport <BusinessObject> (MUsers);
versteckt am
5
Woher kommt Ihre INullable-Schnittstelle?
Kilhoffer
Das beste Beispiel ... aber wie kann ich zwei Tabellen in einer einzigen Datei hinzufügen? Das bedeutet, dass ich eine Tabelle mit zwei Zeilen habe und die andere Tabelle 10 Zeilen und beide einen eindeutigen Spaltennamen haben. Ich möchte oben und danach eine Tabelle mit zwei Zeilen hinzufügen Lücke von zwei Zeilen Ich möchte zweite Tabelle hinzufügen.
Floki
2
Ich weiß, dass der ursprüngliche Beitrag aus dem Jahr 2011 stammt, daher bin ich mir nicht sicher, ob dies in der damals verwendeten .NET-Version möglich war. Aber warum nicht die public string Export()Methode entfernen und die andere Methode in public string Export(bool includeHeaderLiner = true)(mit einem Standardparameterwert) ändern ? Auch hier bin ich mir nicht sicher, ob 2011 Standardparameter verfügbar waren, aber der aktuelle Code erscheint mir nur unorthodox.
Kevin Cruijssen
19

Bitte verzeih mir

Aber ich denke, ein öffentliches Open-Source-Repository ist eine bessere Möglichkeit, Code zu teilen und Beiträge, Korrekturen und Ergänzungen wie "Ich habe das behoben, ich habe das behoben" zu machen.

Also habe ich aus dem Code des Themenstarters und allen Ergänzungen ein einfaches Git-Repository erstellt:

https://github.com/jitbit/CsvExport

Ich habe auch selbst einige nützliche Korrekturen hinzugefügt. Jeder kann Vorschläge hinzufügen, sie aufteilen usw. usw. Senden Sie mir Ihre Gabeln, damit ich sie wieder in das Repo einbinde.

PS. Ich habe alle Copyright-Hinweise für Chris veröffentlicht. @ Chris, wenn Sie gegen diese Idee sind - lassen Sie es mich wissen, ich werde es töten.

Jitbit
quelle
11

Eine weitere gute Lösung zum Lesen und Schreiben von CSV-Dateien sind Dateihelfer (Open Source).

Jelle
quelle
Hinweis: Die Excel-Unterstützung gilt nur für Basisszenarien : Die derzeit implementierte Excel-Unterstützung gilt nur für Basisszenarien. Wenn Sie benutzerdefinierte Formatierungen, Diagramme usw. benötigen, müssen Sie sich für einen benutzerdefinierten Code entscheiden. Es wird dringend empfohlen, die NPOI-Bibliothek direkt zu verwenden
AK
6

Wie wäre es mit string.Join anstelle aller foreach-Schleifen?

Hinek
quelle
String.Join funktioniert nur mit string [], während ich einige der Funktionen von List <string> verwende.
Chris
12
String.Join("," , List<string>)funktioniert auch.
Dementic
6

Wenn jemand möchte, habe ich dies in eine Erweiterungsmethode auf IEnumerable konvertiert:

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
quelle
5

großartige Arbeit an dieser Klasse. Einfach und leicht zu bedienen. Ich habe die Klasse so geändert, dass sie einen Titel in die erste Zeile des Exports einfügt. Ich dachte, ich würde teilen:

verwenden:

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

Klasse:

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

Ich habe ExportToStream hinzugefügt, damit die CSV nicht zuerst auf der Festplatte gespeichert werden musste.

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
quelle
3

Ich habe hinzugefügt

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

}

Vorheriger Code funktioniert nicht mit alten .NET-Versionen. Verwenden Sie für die Version 3.5 des Frameworks diese andere Version:

        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
quelle
2

Vielen Dank dafür! Ich habe die Klasse geändert zu:

  • Verwenden Sie ein variables Trennzeichen anstelle eines im Code fest codierten Codes
  • Ersetzen aller neuen Zeilen (\ n \ r \ n \ r) in MakeValueCsvFriendly

Code:

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

        }

    }
Knabbereien
quelle
1

Die ursprüngliche Klasse hat ein Problem. Wenn Sie also eine neue Spalte hinzufügen möchten, erhalten Sie die KeyNotFoundException on Export-Methode. Beispielsweise:

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

Um dies zu lösen und die @ KeyboardCowboy-Idee der Verwendung von Reflection zu verwenden, habe ich den Code so geändert, dass Zeilen hinzugefügt werden können, die nicht dieselben Spalten haben. Sie können Instanzen anonymer Klassen verwenden. Beispielsweise:

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

Sie können den Quellcode hier herunterladen . CsvExporter . Fühlen Sie sich frei zu verwenden und zu ändern.

Wenn nun alle Zeilen, die Sie schreiben möchten, derselben Klasse angehören, habe ich die generische Klasse CsvWriter.cs erstellt , die eine bessere RAM-Auslastung aufweist und sich ideal zum Schreiben großer Dateien eignet. Außerdem können Sie dem gewünschten Datentyp Formatierer hinzufügen . Ein Anwendungsbeispiel:

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
quelle
0

Sie benötigen dazu nur eine Funktion. Sie müssen lediglich einen Ordner in Ihrem Lösungs-Explorer erstellen, die CSV-Datei dort speichern und diese Datei dann an den Benutzer exportieren.

Wie in meinem Fall habe ich einen Ordner heruntergeladen. Zuerst exportiere ich meinen gesamten Inhalt in dieses Verzeichnis und exportiere ihn dann in den Benutzer. Für die Behandlung von response.end habe ich die ThreadAbortException verwendet. Es ist also eine 100% echte und funktionierende Funktion in meiner Lösung.

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
quelle