Schreiben von Daten in eine CSV-Datei in C #

197

Ich versuche, csvZeile für Zeile mit der Sprache C # in eine Datei zu schreiben . Hier ist meine Funktion

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

Die gesamte Funktion wird in einer Schleife ausgeführt, und jede Zeile sollte in die csvDatei geschrieben werden. In meinem Fall überschreibt die nächste Zeile die vorhandene Zeile und am Ende erhalte ich einen einzigen Datensatz in der CSV-Datei, der letzte. Wie kann ich alle Zeilen in die csvDatei schreiben ?

rampuriyaaa
quelle
Verwenden Sie lieber ein StringBuilderund machen Sie dann einen Save?
Johan
1
Wenn dies keine Aufgabe ist, die Sie täglich erledigen müssen, empfehle ich die Verwendung von LinqPad, das mit einer praktischen Funktion zum Schreiben von Daten in eine CSV ausgestattet ist:Util.WriteCsv (mydatacollection, @"c:\temp\data.csv");
Marco
3
Stellen Sie nebenbei sicher, dass Ihre CSV-Werte codiert sind. Das heißt, wenn einer von ihnen ein Komma oder ein Zeilenendezeichen enthält, kann dies Ihre Datei durcheinander bringen. Normalerweise benutze ich nur eine Drittanbieter-Bibliothek für CSV-Inhalte.
Matthijs Wessels
@MatthijsWessels Irgendwelche Bibliotheksvorschläge?
Arash Motamedi

Antworten:

311

AKTUALISIEREN

In meinen naiven Tagen schlug ich vor, dies manuell zu tun (es war eine einfache Lösung für eine einfache Frage). Da dies jedoch immer beliebter wurde, würde ich empfehlen, die Bibliothek CsvHelper zu verwenden , die alle Sicherheitsüberprüfungen usw. durchführt.

CSV ist viel komplizierter als die Frage / Antwort vermuten lässt.

Ursprüngliche Antwort

Da Sie bereits eine Schleife haben, sollten Sie dies folgendermaßen tun:

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

Oder etwas in diesem Sinne. Meine Argumentation lautet: Sie müssen nicht für jedes Element in die Datei schreiben, sondern öffnen den Stream nur einmal und schreiben dann darauf.

Sie können ersetzen

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

mit

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

Wenn Sie frühere Versionen von CSV in derselben Datei behalten möchten

C # 6

Wenn Sie c # 6.0 verwenden, können Sie Folgendes tun

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

BEARBEITEN

Hier ist ein Link zu einer Frage, die erklärt, was Environment.NewLinefunktioniert.

Johan
quelle
1
@ Rajat nein, weil 2 eine neue Zeile ist
Johan
4
Sie können auch loswerden {2}und Environment.NewLineverwenden AppendLinestattAppend
KyleMit
37
Und was passiert, wenn Ihr CSV-Inhalt ein Komma enthält, das maskiert werden muss? Sie brauchen Angebote. Und was passiert, wenn ein Zitat entkommen muss? Das korrekte Erstellen von CSV-Dateien ist weitaus komplexer als diese Antwort impliziert.
MgSam
1
Ich habe einen "exceptionType": "System.UnauthorizedAccessException",
Chit Khine
3
Ich habe Ihre Lösung ausprobiert, aber für mich (französische Kultur) funktioniert es besser, wenn Sie System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparatoranstelle von Komma verwenden.
A. Pissicat
89

Ich würde Ihnen wärmstens empfehlen, den mühsameren Weg zu gehen. Vor allem, wenn Ihre Datei groß ist.

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()öffnet eine neue Datei, schreibt den Inhalt und schließt die Datei. Das Öffnen von Dateien ist viel ressourcenintensiver als das Schreiben von Daten in einen offenen Stream. Das Öffnen / Schließen einer Datei innerhalb einer Schleife führt zu Leistungseinbußen.

Der von Johan vorgeschlagene Ansatz löst dieses Problem, indem die gesamte Ausgabe im Speicher gespeichert und dann einmal geschrieben wird. Das Programm (bei großen Dateien) verbraucht jedoch viel RAM und stürzt sogar abOutOfMemoryException

Ein weiterer Vorteil meiner Lösung besteht darin, dass Sie das Anhalten und Fortsetzen implementieren können, indem Sie die aktuelle Position in den Eingabedaten speichern.

upd. Platziert mit an der richtigen Stelle

Pavel Murygin
quelle
1
Sie sollten Ihre for-Schleife in die usingAnweisung einfügen. Andernfalls werden Sie die Datei immer wieder neu öffnen.
Oliver
2
Gute Antwort. Richtige Antwort, wenn Sie "{2}" aus der Formatzeichenfolge entfernen und "Bild" durch "Sekunde" in derselben Zeile ersetzen. Vergessen Sie auch nicht, den Writer mit w.Close () zu schließen. w.Flush () wird nicht benötigt.
movAX13h
6
Diese Antwort ignoriert die Notwendigkeit, Zeichen zu entkommen.
MgSam
Können wir auch hinzufügen, dass es hilfreich ist, Ihren Writer wie folgt einzustellen: neuer StreamWriter (Pfad, falsch, Encoding.UTF8)
Charkins12
Wenn Sie einige hundert Millionen Zeilen haben ... sollten Sie jede Zeile spülen?
Ardianto Suhendar
30

Das manuelle Schreiben von CSV-Dateien kann schwierig sein, da Ihre Daten möglicherweise Kommas und Zeilenumbrüche enthalten. Ich schlage vor, Sie verwenden stattdessen eine vorhandene Bibliothek.

Diese Frage erwähnt einige Optionen.

Gibt es CSV-Lese- / Schreibbibliotheken in C #?

Jeppe Andreasen
quelle
4
Dies ist hier die einzig vernünftige Antwort. Es ist eine Schande, dass die anderen schlechten Antworten, die versuchen, es von Hand zu tun, so viele positive Stimmen haben.
MgSam
17

Ich verwende eine Zwei-Analyse-Lösung, da diese sehr einfach zu warten ist

// 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());
user3495843
quelle
1
Beim Aufbau großer Dateien kann es bei diesem Ansatz zu Speicherdruck kommen. Wenn Sie die .ToList entfernen, ist allLines ein IEnumerbale <object []>. Sie können dann anstelle von "für jeden", dh Allines, Select (line => csv.AppendLine (string.Join (",", line))) auswählen, wodurch Sie einen IEnumerable <string> erhalten. Dies kann jetzt nur an die Datei, aber an die WriteAllLines-Methode übergeben werden. Dies bedeutet jetzt, dass das Ganze faul ist und Sie nicht alles in den Speicher ziehen müssen, aber Sie erhalten trotzdem die Syntax, mit der Sie zufrieden sind.
RhysC
Ich benutze Ihre Methode, um in eine CSV-Datei zu schreiben. Es ist toll! Meine neueste Implementierung erfordert, dass ich File.AppendAllLines(filePath, lines, Encoding.ASCII);anstelle von File.WriteAllText(filePath, csv.ToString()); Also mache ich etwas sehr Klobiges. Nachdem ich die CSV mit StringBuilder erstellt habe, konvertiere ich sie in eine List <string>, um eine IEnumerable für den Aufruf von AppendAllLines zu erhalten, die csv.ToString () nicht als Parameter akzeptiert: List<string> lines = new List<string>(); lines.Add(csv.ToString(0, csv.Length));Haben Sie eine bessere Möglichkeit, dies zu tun?
Patricia
12

Anstatt jedes Mal AppendAllText()anzurufen, könnten Sie darüber nachdenken, die Datei einmal zu öffnen und dann den gesamten Inhalt einmal zu schreiben:

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
quelle
@aMazing: Sorry, ein Tippfehler. Ist jetzt behoben.
Oliver
Diese Antwort beinhaltet, dass die Erweiterung csv sein muss. Wenn es xls ist, werden alle Daten in einer Spalte angezeigt ... csv wird in jeder Spalte korrekt angezeigt.
Gman
10

Verwenden Sie stattdessen einfach AppendAllText:

File.AppendAllText(filePath, csv);

Der einzige Nachteil von AppendAllText ist, dass es einen Fehler auslöst, wenn keine Datei vorhanden ist. Daher muss dies überprüft werden

Entschuldigung, blonder Moment vor dem Lesen der Dokumentation . Auf jeden Fall überschreibt die WriteAllText-Methode alles, was zuvor in die Datei geschrieben wurde, sofern die Datei vorhanden ist.

Beachten Sie, dass Ihr aktueller Code keine richtigen neuen Zeilen verwendet. In Notepad wird beispielsweise alles als eine lange Zeile angezeigt. Ändern Sie den Code in diesen, um korrekte neue Zeilen zu erhalten:

string csv = string.Format("{0},{1}{2}", first, image, Environment.NewLine);
Der Schatten-Assistent ist das Ohr für Sie
quelle
2
@ Rajat nein, dies schreibt das neue Zeilenzeichen direkt nach Ihren Bilddaten.
Schatten-Assistent ist Ohr für Sie
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
quelle
warum hier kein Stringbuilder?
Seabizkit
1
Mit welchem ​​RFC haben Sie das geschrieben : .Replace(",", ";") + ',' ?
vt100
Dieser Weg dauert zu lange, wenn Sie mehr als 20000 Zeilen und 30 Spalten haben
Vikrant
6

Anstatt das Rad neu zu erfinden, könnte eine Bibliothek verwendet werden. CsvHelpereignet sich hervorragend zum Erstellen und Lesen von CSV-Dateien. Die Lese- und Schreibvorgänge basieren auf Streams und unterstützen daher auch Vorgänge mit einer großen Datenmenge.


Sie können Ihre CSV wie folgt schreiben.

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

Da die Bibliothek Reflektion verwendet, wird jeder Typ verwendet und direkt analysiert.

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

Wert1, wahr

Wert2, falsch


Wenn Sie mehr über die Bibliothekskonfigurationen und -möglichkeiten erfahren möchten, können Sie dies hier tun .

NtFreX
quelle
Möglicherweise müssen Sie CultureInfo an StreamWriter übergeben, damit dies funktioniert.
Alif
3

Umgang mit Kommas

Für die Behandlung von Kommas innerhalb von Werten bei der Verwendung string.Format(...)hat Folgendes bei mir funktioniert:

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

Um es mit Johans Antwort zu kombinieren, würde es so aussehen:

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

CSV-Datei zurückgeben

Wenn Sie die Datei einfach zurückgeben möchten, anstatt sie an einen Speicherort zu schreiben, ist dies ein Beispiel dafür, wie ich dies erreicht habe:

Aus einer gespeicherten Prozedur

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

Aus einer Liste

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

Hoffentlich ist das hilfreich.

Trevor Nestman
quelle
2

Dies ist ein einfaches Tutorial zum Erstellen von CSV-Dateien mit C #, das Sie bearbeiten und erweitern können, um es Ihren eigenen Anforderungen anzupassen.

Zunächst müssen Sie eine neue Visual Studio C # -Konsolenanwendung erstellen. Dazu müssen Sie die folgenden Schritte ausführen.

Der Beispielcode erstellt eine CSV-Datei mit dem Namen MyTest.csv an dem von Ihnen angegebenen Speicherort. Der Inhalt der Datei sollte aus 3 benannten Spalten mit Text in den ersten 3 Zeilen bestehen.

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
quelle
2
Ein Link zu einer Lösung ist willkommen, aber stellen Sie sicher, dass Ihre Antwort ohne sie nützlich ist: Fügen Sie dem Link einen Kontext hinzu, damit Ihre Mitbenutzer eine Vorstellung davon haben, was es ist und warum es dort ist, und zitieren Sie dann den relevantesten Teil der Seite, die Sie verwenden. erneutes Verknüpfen mit, falls die Zielseite nicht verfügbar ist. Antworten, die kaum mehr als ein Link sind, können gelöscht werden.
Shree
Ah ich sehe. Danke für die Rückmeldung. Ich habe die Änderungen hervorgehoben. Bitte stimmen Sie ab.
Bloggins
0

Möglicherweise müssen Sie nur einen Zeilenvorschub hinzufügen "\n\r".

OneWileyDog
quelle
0

Hier ist eine weitere Open-Source-Bibliothek zum einfachen Erstellen von CSV-Dateien, 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);
}

Weitere Informationen finden Sie im CodeProject- Artikel zur Verwendung.

RajN
quelle
0

Eine einfache Möglichkeit, das Problem des Überschreibens File.AppendTextzu beseitigen, besteht darin, die Zeile am Ende der Datei als anzuhängen

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

Dies sollte es Ihnen ermöglichen, ganz einfach eine CSV-Datei zu schreiben. Verwendung:

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