Écriture de données dans un fichier CSV en C #

198

J'essaie d'écrire dans un csv fichier ligne par ligne en utilisant le langage C #. Voici ma fonction

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

La fonction entière s'exécute dans une boucle et chaque ligne doit être écrite dans le csvfichier. Dans mon cas, la ligne suivante écrase la ligne existante et à la fin, j'obtiens un seul enregistrement unique dans le fichier csv qui est le dernier. Comment puis-je écrire toutes les lignes du csvfichier?

rampuriyaaa
la source
Plutôt utiliser un StringBuilder, puis faire une sauvegarde?
Johan
1
Si ce n'est pas une tâche que vous devez accomplir quotidiennement, je recommande d'utiliser LinqPad, qui est livré avec une fonction pratique pour écrire des données dans un csv:Util.WriteCsv (mydatacollection, @"c:\temp\data.csv");
Marco
3
Sur une note latérale, assurez-vous que vos valeurs csv sont encodées. Autrement dit, si l'un d'eux contient une virgule ou un caractère de fin de ligne, il pourrait gâcher votre fichier. J'utilise généralement une bibliothèque tierce pour les trucs csv.
Matthijs Wessels
@MatthijsWessels Des suggestions de bibliothèque?
Arash Motamedi

Réponses:

311

METTRE À JOUR

À l'époque naïve, j'ai suggéré de le faire manuellement (c'était une solution simple à une question simple), mais en raison de cette popularité croissante , je recommanderais d'utiliser la bibliothèque CsvHelper qui effectue tous les contrôles de sécurité, etc.

Le CSV est beaucoup plus compliqué que ce que la question / réponse suggère.

Réponse originale

Comme vous avez déjà une boucle, envisagez de le faire comme ceci:

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

Ou quelque chose à cet effet. Mon raisonnement est le suivant: vous n'aurez pas besoin d'écrire dans le fichier pour chaque élément, vous n'ouvrirez le flux qu'une seule fois, puis vous y écrivez.

Vous pouvez remplacer

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

avec

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

si vous souhaitez conserver les versions précédentes de csv dans le même fichier

C # 6

Si vous utilisez c # 6.0, vous pouvez effectuer les opérations suivantes

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

ÉDITER

Voici un lien vers une question qui explique ce que cela Environment.NewLinefait.

Johan
la source
1
@Rajat non, car 2 est une nouvelle ligne
Johan
4
Vous pouvez également vous débarrasser de {2}et Environment.NewLineet utiliser à la AppendLineplace deAppend
KyleMit
37
Et que se passe-t-il lorsque votre contenu CSV a une virgule qui doit être échappée? Vous avez besoin de devis. Et que se passe-t-il lorsqu'une citation doit s'échapper? Construire correctement des fichiers CSV est beaucoup plus complexe que cette réponse ne l'implique.
MgSam
1
J'ai obtenu un "exceptionType": "System.UnauthorizedAccessException",
Chit Khine
3
J'ai essayé votre solution, mais pour moi (culture française) cela fonctionne mieux en utilisant System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparatorau lieu de virgule.
A.Pissicat
89

Je vous recommande fortement d'emprunter la voie la plus fastidieuse. Surtout si la taille de votre fichier est importante.

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()ouvre un nouveau fichier, écrit le contenu, puis ferme le fichier. L'ouverture de fichiers est une opération beaucoup plus gourmande en ressources que l'écriture de données dans un flux ouvert. L'ouverture / la fermeture d'un fichier dans une boucle entraînera une baisse des performances.

L'approche suggérée par Johan résout ce problème en stockant toutes les sorties en mémoire, puis en les écrivant une fois. Cependant (en cas de gros fichiers), votre programme consommera une grande quantité de RAM et même plantera avecOutOfMemoryException

Un autre avantage de ma solution est que vous pouvez implémenter une pause \ reprise en enregistrant la position actuelle dans les données d'entrée.

upd. Placé en utilisant au bon endroit

Pavel Murygin
la source
1
Vous devez placer votre boucle for dans l' usinginstruction. Sinon, vous allez rouvrir le fichier tout le temps encore et encore.
Oliver
2
Bonne réponse. Bonne réponse si vous supprimez "{2}" de la chaîne de formatage et remplacez "image" par "deuxième" sur la même ligne. N'oubliez pas non plus de fermer le rédacteur avec w.Close (); w.Flush () n'est pas nécessaire.
movAX13h
6
Cette réponse ignore la nécessité d'échapper aux personnages.
MgSam
Pouvons-nous également ajouter que cela aide à définir votre écrivain comme ceci: nouveau StreamWriter (chemin, faux, Encoding.UTF8)
Charkins12
si vous avez quelques centaines de millions de lignes ... devriez-vous vider chaque ligne?
Ardianto Suhendar
30

Écrire des fichiers csv à la main peut être difficile car vos données peuvent contenir des virgules et des retours à la ligne. Je vous suggère d'utiliser une bibliothèque existante à la place.

Cette question mentionne quelques options.

Existe-t-il des bibliothèques de lecture / écriture CSV en C #?

Jeppe Andreasen
la source
4
C'est la seule réponse raisonnablement correcte ici. C'est dommage que les autres mauvaises réponses qui essaient de le faire à la main aient autant de votes positifs.
MgSam
17

J'utilise une solution à deux analyses car elle est très facile à entretenir

// 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
la source
1
Vous pouvez constater que vous rencontrez une pression mémoire avec cette approche lors de la création de fichiers volumineux. Si vous supprimez le .ToList, alors allLines serait un IEnumerbale <objet []>. Vous pouvez alors sélectionner celui-ci au lieu de "pour chacun", c'est-à-dire les allines, Select (line => csv.AppendLine (string.Join (",", line))) qui vous donnera une IEnumerable <string>. Cela peut maintenant être simplement transmis au fichier mais à la méthode WriteAllLines. Cela signifie maintenant que tout est paresseux et que vous n'avez pas besoin de tout mettre en mémoire, mais vous obtenez toujours la syntaxe qui vous convient.
RhysC
J'utilise votre méthode pour écrire dans un fichier csv. C'est bien! Ma dernière implémentation nécessite que j'utilise à la File.AppendAllLines(filePath, lines, Encoding.ASCII);place de File.WriteAllText(filePath, csv.ToString()); Donc, je fais quelque chose de très maladroit. Après avoir construit le csv à l'aide de StringBuilder, je le convertis en List <string> pour obtenir un IEnumerable pour l'appel à AppendAllLines, qui n'acceptera pas csv.ToString () comme paramètre: List<string> lines = new List<string>(); lines.Add(csv.ToString(0, csv.Length));Avez-vous une meilleure façon de procéder?
Patricia
12

Au lieu d'appeler à chaque fois, AppendAllText()vous pourriez penser à ouvrir le fichier une fois, puis à écrire tout le contenu une fois:

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
la source
@aMazing: Désolé, une faute de frappe. Est maintenant corrigé.
Oliver
Cette réponse inclut que l'extension doit être csv. Si c'est xls, les données apparaîtront toutes dans une colonne ... csv elles apparaîtront correctement dans chaque colonne.
gman
10

Utilisez simplement AppendAllText à la place:

File.AppendAllText(filePath, csv);

Le seul inconvénient de AppendAllText est qu'il générera une erreur lorsque le fichier n'existe pas, donc cela doit être vérifié

Désolé, moment blond avant de lire la documentation . Quoi qu'il en soit, la méthode WriteAllText écrase tout ce qui a été précédemment écrit dans le fichier, si le fichier existe.

Notez que votre code actuel n'utilise pas de nouvelles lignes appropriées, par exemple dans le Bloc-notes, vous verrez tout cela comme une longue ligne. Changez le code en ceci pour avoir de nouvelles lignes appropriées:

string csv = string.Format("{0},{1}{2}", first, image, Environment.NewLine);
L'assistant de l'ombre est à votre écoute
la source
2
@Rajat non, cela écrira le nouveau caractère de ligne juste après vos données d'image.
Shadow Wizard est Ear For You
7

Au lieu de réinventer la roue, une bibliothèque pourrait être utilisée. CsvHelperest idéal pour créer et lire des fichiers csv. Ses opérations de lecture et d'écriture sont basées sur des flux et prennent donc également en charge les opérations avec une grande quantité de données.


Vous pouvez écrire votre csv comme suit.

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

Comme la bibliothèque utilise la réflexion, elle prendra n'importe quel type et l'analysera directement.

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

valeur1, vrai

valeur2, faux


Si vous souhaitez en savoir plus sur les configurations et les possibilités des bibliothèques, vous pouvez le faire ici .

NtFreX
la source
Vous devrez peut-être transmettre CultureInfo à StreamWriter pour que cela fonctionne.
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();
                    }
                }
            }
        }
    }
}
Alejandro Haro
la source
pourquoi pas de constructeur de cordes ici?
Seabizkit
1
Quel RFC avez-vous utilisé pour écrire ceci .Replace(",", ";") + ',' :?
vt100
Cette méthode prend trop de temps si vous avez plus de 20000 lignes et 30 colonnes
Vikrant
3

Gestion des virgules

Pour gérer les virgules à l'intérieur des valeurs lors de l'utilisation string.Format(...), ce qui suit a fonctionné pour moi:

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

Donc, pour le combiner avec la réponse de Johan, cela ressemblerait à ceci:

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

Retour du fichier CSV

Si vous vouliez simplement retourner le fichier au lieu de l'écrire dans un emplacement, voici un exemple de la façon dont je l'ai accompli:

À partir d'une procédure stockée

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

À partir d'une 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");
}

J'espère que cela vous sera utile.

Trevor Nestman
la source
2

Il s'agit d'un didacticiel simple sur la création de fichiers csv à l'aide de C # que vous pourrez modifier et développer pour répondre à vos propres besoins.

Vous devez d'abord créer une nouvelle application console Visual Studio C #, il existe des étapes à suivre pour ce faire.

L'exemple de code créera un fichier csv appelé MyTest.csv à l'emplacement que vous spécifiez. Le contenu du fichier doit être composé de 3 colonnes nommées avec du texte dans les 3 premières lignes.

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
la source
2
Un lien vers une solution est le bienvenu, mais assurez-vous que votre réponse est utile sans elle: ajoutez du contexte autour du lien pour que vos collègues utilisateurs aient une idée de ce que c'est et pourquoi il est là, puis citez la partie la plus pertinente de la page que vous '' relier à au cas où la page cible n'est pas disponible. Les réponses qui ne sont guère plus qu'un lien peuvent être supprimées.
Shree le
Ah, je vois. Merci pour les commentaires. J'ai fait les changements mis en évidence. Veuillez voter.
Bloggins
0

Vous devrez peut-être simplement ajouter un saut de ligne "\n\r".

OneWileyDog
la source
0

Voici une autre bibliothèque open source pour créer facilement un fichier 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);
}

Pour plus d'informations, veuillez lire l' article CodeProject sur l'utilisation.

RajN
la source
0

Un moyen simple de se débarrasser du problème d'écrasement consiste File.AppendTextà ajouter une ligne à la fin du fichier

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
la source
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();
démon
la source
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);
}

Cela devrait vous permettre d'écrire un fichier csv tout simplement. Usage:

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