264 votes

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

J'essaie d'écrire dans un csv 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);

L'ensemble de la fonction s'exécute à l'intérieur d'une boucle, et chaque ligne doit être écrite dans le fichier csv fichier. Dans mon cas, la ligne suivante écrase la ligne existante et à la fin, je n'obtiens qu'un seul enregistrement dans le fichier csv qui est le dernier. Comment puis-je écrire toutes les lignes dans le fichier csv ? csv fichier ?

400voto

Johan Points 1371

UPDATE

À l'époque de ma naïveté, je suggérais de le faire manuellement (c'était une solution simple à une question simple), mais comme cela devient de plus en plus populaire, je recommande 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 la 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 de ce genre. 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 fois et n'y écrirez qu'ensuite.

Vous pouvez remplacer

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

avec

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

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

C# 6

Si vous utilisez c# 6.0, vous pouvez procéder comme suit

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

EDITAR

Voici un lien à une question qui explique ce que Environment.NewLine fait.

109voto

Pavel Murygin Points 2106

Je vous recommande vivement de suivre 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 et ferme ensuite le fichier. L'ouverture de fichiers est une opération plus gourmande en ressources que l'écriture de données dans un flux ouvert. Ouverture de \closing un fichier à l'intérieur d'une boucle entraînera une baisse des performances.

L'approche suggérée par Johan résout ce problème en stockant toute la sortie en mémoire et en l'écrivant une seule fois. Cependant (dans le cas de gros fichiers), votre programme consommera une grande quantité de RAM et pourra même planter avec OutOfMemoryException

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

upd. Placement de l'utilisation au bon endroit

43voto

Jeppe Andreasen Points 461

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

Cette question mentionne quelques options.

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

17voto

Oliver Points 19006

Au lieu d'appeler à chaque fois AppendAllText() vous pouvez envisager d'ouvrir le fichier une fois et d'é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);
    }
}

17voto

user3495843 Points 201

J'utilise une solution à deux parse car elle est très facile à maintenir.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X