Scrittura dei dati nel file CSV in C #


198

Sto provando a scrivere in un csvfile riga per riga usando il linguaggio C #. Ecco la mia funzione

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

L'intera funzione viene eseguita all'interno di un ciclo e ogni riga deve essere scritta nel csvfile. Nel mio caso, la riga successiva sovrascrive la riga esistente e alla fine ottengo un solo record nel file CSV che è l'ultimo. Come posso scrivere tutte le righe nel csvfile?


Piuttosto usa un StringBuildere poi fai un salvataggio?
Johan,

1
Se questo non è un compito che devi svolgere quotidianamente, ti consiglio di usare LinqPad, che viene fornito con una comoda funzione per scrivere i dati in un CSV:Util.WriteCsv (mydatacollection, @"c:\temp\data.csv");
Marco

3
Nota a margine, assicurati che i tuoi valori csv siano codificati. Vale a dire se uno di essi contiene una virgola o un carattere di fine riga, potrebbe rovinare il file. Di solito uso solo una libreria di terze parti per roba CSV.
Matthijs Wessels l'

@MatthijsWessels Qualche suggerimento sulla biblioteca?
Arash Motamedi,

Risposte:


311

AGGIORNARE

Ai miei tempi ingenui, ho suggerito di farlo manualmente (era una soluzione semplice a una semplice domanda), tuttavia a causa di questo sempre più popolare, consiglierei di utilizzare la libreria CsvHelper che fa tutti i controlli di sicurezza, ecc.

Il CSV è molto più complicato di quanto suggerisce la domanda / risposta.

Risposta originale

Dato che hai già un ciclo, considera di farlo in questo modo:

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

O qualcosa in tal senso. Il mio ragionamento è: non sarà necessario scrivere nel file per ogni elemento, aprirai il flusso una sola volta e poi scriverà su di esso.

È possibile sostituire

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

con

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

se si desidera mantenere le versioni precedenti di CSV nello stesso file

C # 6

Se si utilizza c # 6.0, è possibile effettuare le seguenti operazioni

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

MODIFICARE

Ecco un link a una domanda che spiega cosa Environment.NewLinefa.


1
@Rajat no, perché 2 è una nuova linea
Johan,

4
Si può anche sbarazzarsi di {2}e Environment.NewLineed utilizzare AppendLineal posto diAppend
KyleMit

37
E cosa succede quando il tuo contenuto CSV ha una virgola che deve essere salvata? Hai bisogno di preventivi. E cosa succede quando è necessario scappare un preventivo? La corretta costruzione dei file CSV è molto più complessa di quanto questa risposta implichi.
MgSam

1
Ho ricevuto un "exceptionType": "System.UnauthorizedAccessException",
Chit Khine,

3
Ho provato la tua soluzione, ma per me (cultura francese) funziona meglio usando al System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparatorposto della virgola.
A.Pissicat,

89

Consiglio vivamente di percorrere la strada più noiosa. Soprattutto se la dimensione del file è grande.

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()apre un nuovo file, scrive il contenuto e quindi chiude il file. L'apertura dei file è un'operazione che richiede molte risorse, rispetto alla scrittura di dati in open stream. L'apertura / chiusura di un file all'interno di un loop provocherà un calo delle prestazioni.

L'approccio suggerito da Johan risolve il problema archiviando tutto l'output in memoria e quindi scrivendolo una volta. Tuttavia (nel caso di file di grandi dimensioni) il programma consumerà una grande quantità di RAM e si blocca anche conOutOfMemoryException

Un altro vantaggio della mia soluzione è che è possibile implementare la pausa \ ripresa riprendendo la posizione corrente nei dati di input.

Agg. Posizionato usando nel posto giusto


1
Si dovrebbe mettere il vostro ciclo for all'interno della usingdichiarazione. Altrimenti riaprirai il file continuamente.
Oliver,

2
Buona risposta. Risposta corretta se si rimuove "{2}" dalla stringa di formato e si sostituisce "immagine" con "secondo" nella stessa riga. Inoltre, non dimenticare di chiudere lo scrittore con w.Close (); w.Flush () non è necessario.
movAX13h,

6
Questa risposta ignora la necessità di sfuggire ai personaggi.
MgSam

Possiamo anche aggiungere che aiuta a impostare il tuo scrittore in questo modo: nuovo StreamWriter (path, false, Encoding.UTF8)
Charkins12

se hai qualche centinaio di milioni di linee ... dovresti svuotare ogni linea?
Ardianto Suhendar

30

Scrivere file CSV a mano può essere difficile perché i tuoi dati potrebbero contenere virgole e newline. Suggerisco invece di utilizzare una libreria esistente.

Questa domanda menziona alcune opzioni.

Ci sono librerie di lettori / scrittori CSV in C #?


4
Questa è l'unica risposta ragionevolmente corretta qui. È un peccato che le altre cattive risposte che provano a farlo a mano abbiano così tanti voti positivi.
MgSam

17

Uso una soluzione a due analisi poiché è molto facile da mantenere

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

1
È possibile che si verifichi una pressione della memoria con questo approccio durante la creazione di file di grandi dimensioni. Se hai rimosso .ToList, allora allLines sarebbe un IEnumerbale <oggetto []>. È quindi possibile selezionare quello anziché "per ognuno", ovvero alline, Select (line => csv.AppendLine (string.Join (",", line))) che ti darebbe un IEnumerable <string>. Questo ora può essere semplicemente passato al File ma con il metodo WriteAllLines. Questo ora significa che tutto è pigro e non è necessario inserire tutto nella memoria, ma ottieni ancora la sintassi di cui sei felice.
RhysC

Sto usando il tuo metodo per scrivere su un file CSV. È ottimo! La mia ultima implementazione richiede che io usi File.AppendAllLines(filePath, lines, Encoding.ASCII);invece di File.WriteAllText(filePath, csv.ToString()); So, sto facendo qualcosa di molto goffo. Dopo aver creato il CSV usando StringBuilder, lo converto in un Elenco <stringa> per ottenere un IEnumerable per la chiamata ad AppendAllLines, che non accetterà csv.ToString () come parametro: List<string> lines = new List<string>(); lines.Add(csv.ToString(0, csv.Length));hai un modo migliore per farlo?
Patricia,

12

Invece di chiamare ogni volta AppendAllText()che potresti pensare di aprire il file una volta e quindi scrivere l'intero contenuto una volta:

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

@aMazing: Siamo spiacenti, un errore di battitura. Ora è stato risolto.
Oliver,

Questa risposta include che l'estensione deve essere csv. Se è xls, i dati appariranno tutti in una colonna ... csv appariranno correttamente in ogni colonna.
Gman,

10

Basta usare invece AppendAllText:

File.AppendAllText(filePath, csv);

L'unico aspetto negativo di AppendAllText è che genererà un errore quando il file non esiste, quindi è necessario verificarlo

Scusa, momento biondo prima di leggere la documentazione . Ad ogni modo, il metodo WriteAllText sovrascrive tutto ciò che è stato precedentemente scritto nel file, se il file esiste.

Nota che il tuo codice attuale non utilizza nuove righe appropriate, ad esempio nel Blocco note vedrai tutto come una lunga riga. Modificare il codice in questo per avere nuove righe appropriate:

string csv = string.Format("{0},{1}{2}", first, image, Environment.NewLine);

2
@Rajat no, questo scriverà il nuovo carattere di riga subito dopo i dati dell'immagine.
Shadow Wizard è Ear For You,

7

Invece di reinventare la ruota si potrebbe usare una libreria. CsvHelperè ottimo per creare e leggere file CSV. Le sue operazioni di lettura e scrittura sono basate sul flusso e quindi supportano anche operazioni con una grande quantità di dati.


Puoi scrivere il tuo csv come segue.

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

Poiché la libreria utilizza la riflessione, prenderà qualsiasi tipo e la analizzerà direttamente.

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

valore1, vero

valore2, falso


Se vuoi leggere di più sulle configurazioni e le possibilità dei librarys puoi farlo qui .


Potrebbe essere necessario passare CultureInfo a StreamWriter affinché funzioni.
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();
                    }
                }
            }
        }
    }
}

perché nessun costruttore di stringhe qui?
Seabizkit,

1
Quale RFC hai usato per scrivere questo .Replace(",", ";") + ',' :?
vt100,

In questo modo impiega troppo tempo se hai più di 20000 righe e 30 colonne
Vikrant il

3

Gestione delle virgole

Per la gestione delle virgole all'interno dei valori durante l'utilizzo string.Format(...), per me ha funzionato:

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

Quindi per combinarlo con la risposta di Johan, sembrerebbe così:

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

Restituzione del file CSV

Se volevi semplicemente restituire il file invece di scriverlo in una posizione, questo è un esempio di come l'ho realizzato:

Da una procedura memorizzata

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

Da un elenco

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

Speriamo che sia utile


2

Questo è un semplice tutorial sulla creazione di file CSV utilizzando C # che potrai modificare ed espandere in base alle tue esigenze.

Per prima cosa devi creare una nuova applicazione console di Visual Studio C #, ci sono dei passaggi da seguire per farlo.

Il codice di esempio creerà un file CSV chiamato MyTest.csv nella posizione specificata. Il contenuto del file deve essere composto da 3 colonne con nome e testo nelle prime 3 righe.

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

2
Un link a una soluzione è il benvenuto, ma assicurati che la tua risposta sia utile senza di essa: aggiungi un contesto attorno al link in modo che i tuoi colleghi utenti abbiano qualche idea di cosa sia e perché sia ​​lì, quindi cita la parte più pertinente della pagina che ' re collegamento a nel caso in cui la pagina di destinazione non sia disponibile. Le risposte che sono poco più di un collegamento possono essere eliminate.
Shree

Ah capisco Grazie per il feedback. Ho apportato le modifiche evidenziate. Per favore, vota.
Bloggins,

0

Potrebbe essere necessario solo aggiungere un feed di riga "\n\r".


0

Ecco un'altra libreria open source per creare facilmente file CSV, 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);
}

Per ulteriori informazioni, leggi l' articolo di CodeProject sull'utilizzo.


0

Un modo semplice per sbarazzarsi del problema di sovrascrittura è utilizzare File.AppendTextper aggiungere la riga alla fine del file come

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

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

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

Ciò dovrebbe consentire di scrivere un file CSV in modo abbastanza semplice. Uso:

StreamWriter writer = new StreamWriter("myfile.csv");
writer.WriteCSVLine(new[]{"A", "B"});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.