LINQ to CSV: Schreiben einer CSV Datei mittels LINQ

Das sich CSV Dateien mittels LINQ lesen lassen, habe ich Ihnen in einem vorhergehenden Artikel bereits gezeigt. Jetzt wäre es natürlich wünschenswert die CSV Dateien ebenfalls mittels LINQ zu erzeugen. In diesem Artikel möchte ich Ihnen daher eine Erweiterungsmethode vorstellen, mit der Sie die Ergebnisse einer LINQ Abfrage direkt in das CSV Format umwandeln können.

 
Format einer CSV Datei

Wen Sie mehrere Entwickler danach fragen wie eine CSV Datei aufgebaut ist werden Sie sicher unterschiedliche Antworten erhalten. Das Format ist nicht exakt festgelegt. Folgende Punkte sollten daher beachtet werden:

  • Die erste Zeile der Datei kann optional als Kopfzeile mit Spaltennamen genutzt werden.
  • Als Trennzeichen wird standardmässig ein Komma verwendet.
  • Andere Trennzeichen sind ebenfalls erlaubt.
  • Felder, wie zum Beispiel ein Barcode der in der Datei gespeichert werden soll, könnten das definierte Trennzeichen beinhalten. Daher werden Felder standardmüssig in Hochkommas eingeschlossen.
  • Ein Hochkomma in einem Feld wird dabei mit einem doppelten Hochkomma maskiert.
  • Die Hochkommas sind optional und können daher auch entfallen.

 
Erweiterungsmethode

Um den obigen Vorgaben gerecht zu werden, wird die Erweiterungsmethode in zwei Varianten angeboten.

  • Variante 1: Alle Parameter sind einstellbar. Der Anwender kann entscheiden ob eine Kopfzeile eingefügt werden soll, welches Trennzeichen zum Einsatz kommt und ob Felder mit Hochkommas maskiert werden sollen.
  • Variante 2: Der Anwender kann keine Parameter angeben. Es werden die Standardwerte verwendet. Es wird keine Kopfzeile eingefügt, als Trennzeichen wird ein Komma verwendet und Felder werden mit Hochkomas maskiert.

 
Der nachfolgende Quellcode zeigt die Erweiterungsmethode.

    1 public static class Extensions

    2 {

    3     /// <summary>

    4     /// convert the objet list into a string in csv format

    5     /// – delimiter: comma

    6     /// – field quotes: quotes will be used

    7     /// </summary>

    8     /// <typeparam name="T"></typeparam>

    9     /// <param name="items"></param>

   10     /// <returns></returns>

   11     public static string AsCsv<T>(this IEnumerable<T> items)

   12     {

   13         return AsCsv(items, false, ",", true);

   14     }

   15 

   16     /// <summary>

   17     /// convert the objet list into a string in csv format

   18     /// </summary>

   19     /// <typeparam name="T"></typeparam>

   20     /// <param name="items"></param>

   21     /// <param name="withHeader"></param>

   22     /// <param name="delimiter"></param>

   23     /// <param name="useQuotesForFields"></param>

   24     /// <returns></returns>

   25     public static string AsCsv<T>(this IEnumerable<T> items,

   26         bool withHeader, string delimiter, bool useQuotesForFields)

   27     {

   28         var csvBuilder = new StringBuilder();

   29         var properties = typeof(T).GetProperties();

   30 

   31         if (withHeader)

   32         {

   33             csvBuilder.AppendLine(string.Join(delimiter,

   34                 (from p in properties select p.Name).ToArray()));

   35         }

   36 

   37         foreach (T item in items)

   38         {

   39             var values = from p in properties

   40                          select p.GetValue(item, null).ToCsvValue(useQuotesForFields);

   41 

   42             csvBuilder.AppendLine(string.Join(delimiter, values.ToArray()));

   43         }

   44         return csvBuilder.ToString();

   45     }

   46 

   47     /// <summary>

   48     /// convert a single item into a scv value

   49     /// </summary>

   50     /// <typeparam name="T"></typeparam>

   51     /// <param name="item"></param>

   52     /// <param name="useQuotesForFields"></param>

   53     /// <returns></returns>

   54     private static string ToCsvValue<T>(this T item, bool useQuotesForFields)

   55     {

   56         if (item is string)

   57         {

   58             if (useQuotesForFields == true)

   59             {

   60                 return string.Format("\"{0}\"", item.ToString().Replace("\"", "\"\""));

   61             }

   62             else

   63             {

   64                 return item.ToString();

   65             }

   66         }

   67 

   68         if (item is DateTime)

   69         {

   70             return string.Format("{0:u}", item);    //format: 2013-01-20 12:49:56Z

   71         }

   72 

   73         double dummy;

   74         if (item == null)

   75             return "";

   76 

   77         if (double.TryParse(item.ToString(), out dummy))

   78             return string.Format("{0}", item);

   79 

   80         return string.Format("{0}", item);

   81     }

   82 }

 
In der Funktion ToCsvValue werden die einzelnen Werte je nach Datentyp formatiert.

 
Anwenden in einer LINQ Abfrage

Jetzt soll die Erweiterungsmethode natürlich auch angewandt werden. Dazu wird eine Liste mit Personen erstellt. Die Datenklasse sieht folgendermassen aus.

    1 public class Person

    2 {       

    3     public string FirstName{get;set;}       

    4     public string LastName{get;set;}       

    5     public int Age{get;set;} 

    6 }

 
In einem Testprogramm werden drei Personen erzeugt. Danach sollen alle Personen die maximal 40 Jahre alt sind ermittelt und in einer CSV Datei gespeichert werden. In der Anwendung wird dazu die entsprechende LINQ Abfrage erstellt. Diese wird durch den Aufruf der neuen Erweiterungsmethode ergänzt. Dabei wird zum einen die parameterlose Funktion aufgerufen und in einem zweiten Test die parametrisierte Funktion. Der nachfolgende Quellcode zeigt die Testanwendung.

    1 static void Main(string[] args)

    2 {

    3     List<Person> persons;

    4     persons = new List<Person>()

    5     {

    6         new Person() { FirstName = "Hans", LastName = "Mueller", Age = 35},

    7         new Person() { FirstName = "Sabine", LastName = "Schmidt", Age = 37},

    8         new Person() { FirstName = "Uwe", LastName = "Meier", Age = 74}

    9     };

   10 

   11     var csvContent = persons.Where(person => person.Age < 40).AsCsv();

   12 

   13     using (StreamWriter writer = new StreamWriter("test1.csv"))

   14     {

   15         writer.Write(csvContent);

   16     }

   17 

   18     csvContent = persons.Where(person => person.Age < 40).AsCsv(true, ";", false);

   19 

   20     using (StreamWriter writer = new StreamWriter("test2.csv"))

   21     {

   22         writer.Write(csvContent);

   23     }

   24 }

 
Die Datei test1.csv hat nach Programmausführung den folgenden Inhalt:

„Hans“,“Mueller“,35

„Sabine“,“Schmidt“,37

 
Die Datei test2.csv hingegen sieht folgendermassen aus:

FirstName;LastName;Age

Hans;Mueller;35

Sabine;Schmidt;37

 
Fazit

Die Umwandlung von Abfrageergebnissen in das CSV Format lässt sich mittels einer Erweiterungsmethode in LINQ integrieren. Die so erstellte Erweiterungsmethode kann wie eine Standard LINQ Funktion genutzt werden und lässt sich dadurch  nahtlos in die entsprechenden Abfragen einfügen.

Werbung
Dieser Beitrag wurde unter .NET, C#, LINQ abgelegt und mit , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit deinem WordPress.com-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s