CSV / C#.NET: LINQ

C#.NET: LINQ

C#.NET: LINQ

➪ LINQ (Language Integrated Query) bietet leistungsfähige Standards, diverse Datenquellen (Objektlisten, SQL-DB-Tabellen, XML-Dateien) in einer SQL-ähnlichen Syntax auszuwerten. Dabei arbeitet LINQ mit Lambda-Ausdrücken, anonymen Typen, Objektinitialisierern und Typinferenz.

Auf dieser Seite:

Nehmen Sie eine Func als Lambda-Ausdruck, die einen Integerwert als Parameter übernimmt, diesen Wert quadriert und dessen Resultat zurückliefert.


Func<int, int> fquadriere = x => x * x;

Mit dieser lokalen Func ausgerüstet,


int[] werte = new int[10];
for (int i = 1; i <= 10; i++)
{
    werte[i - 1] = fquadriere(i);
}
// Auswahl der Werte mit LINQ
var teilbardurch9 = from y 
                    in werte 
                    where y % 9 == 0 
                    select y;
// Auswertung der Auswahl mit einer foreach-Schleife
foreach (var z in teilbardurch9) 
                    Console.WriteLine(z);

pic/cs_linq_intarray.png

Alternativ können Sie eine dynamische Integer-Werteliste füllen.


// deklariere eine Liste vom Typ Integer
List<int> werte = new List<int>();
// fülle die Liste mit den quadrierten Werten von 1 bis 10
for (int i = 1; i <= 10; i++) 
                    werte.Add(
                        fquadriere(i)
                    );
// Rest wie vorher

Vorstellbar ist auch dieser bereits vorher beschriebene Ansatz:


Func<int, int> fquadriere = x => x * x;
Func<int, int, List<int>> Zahlen = (start, ende) => {
    List<int> l = new List<int>();
    for (int i = start; i <= ende; i++)
        l.Add(fquadriere(i));
    return l;
};
Action<int> ausgabe = x => Console.WriteLine($"{x,9:N2}");
var teilbardurch9 = from y in Zahlen(1, 10)
                    where y % 9 == 0
                    orderby y descending
                    select y;
foreach (var z in teilbardurch9) ausgabe(z); 
//Alternative, Ausgabe in ursprünglicher Reihenfolge
//pteilbardurch9.ForAll(s => ausgabe(s));          

var teilbardurch9 kann dabei in LINQ auch so geschrieben werden:


var teilbardurch9 = Zahlen(1, 10)
                    .Where(y => y % 9 == 0)
                    .OrderByDescending(y => y)
                    .Select(y => y);

Der Unterschied zwischen LINQ und PLINQ (parallel LINQ) ist nun, dass die LINQ-Anweisungen parallel (.AsParallel()) durchgeführt werden. Die erhoffte erhöhte Geschwindigkeit mag eintreffen; ob auch die angestrebte Sortierreihenfolge eingehalten wird, bleibt abzuwarten.


var pteilbardurch9 = Zahlen(1, 10)
                    .AsParallel()
                    .Where(y => y % 9 == 0)
                    .OrderByDescending(y => y)
                    .Select(y => y);

Das jeweilige Resultat:


    81,00
    36,00
     9,00

Möglich ist auch der Aufruf, der nur bei .AsParallel() zur Verfügung steht.


pteilbardurch9.ForAll(s => ausgabe(s));

Wenn Sie beide Aufrufe nacheinander laufen lassen, können Sie feststellen, dass die Reihenfolge´der ausgegebenen Zahlen unterschiedlich ist.


pteilbardurch9.ForAll(s => ausgabe(s));
foreach (var z in pteilbardurch9) ausgabe(z);

Auswerten von CSV mit LINQ

Mit LINQ bietet C#.NET flexible Möglichkeiten, Textdateien (zum Beispiel CSV) in XML-Dokumente zu konvertieren. Auch Gruppierungen der XML-Struktur sind leicht möglich.

Alternativ zur CSV-XML-Konvertierung via unparsed-text-lines können Sie in C#.NET/VisualBasic.NET mit LINQ arbeiten. Nehmen Sie diese CSV-Struktur:


ORTID;ORTNAME;ID;NAME;VORNAME;GEHALT;IDORT;AUSGABEN
1;Neustadt;1;Holzflos;Hugo;234.56;1;3563.1400000000003
1;Neustadt;4;Nixlos;Nicole;1234.56;1;961.6300000000001
1;Neustadt;9;Sprachlos;Stefan;5430;1;4288.02
1;Neustadt;2;Sagblos;Stefan;321.45;1;2753.9399999999996
1;Neustadt;3;Sorglos;Siggi;987.58;1;2610.96
1;Neustadt;7;Herzlos;Heini;654.21;1;415.76
2;Darmstadt;8;Rhodos;Rudi;333.33;2;740.6999999999999
2;Darmstadt;15;Kolos;Karl;456;2;181.92
2;Darmstadt;16;Sinnlos;Simone;876.54;2;155.94
2;Darmstadt;17;Hirnlos;Horst;546.77;2;38.97
2;Darmstadt;18;Wertlos;Werner;777.77;2;103.96
2;Darmstadt;19;Lustlos;Ludwig;357;2;0
3;Kapstadt;5;Wasistlos;Willi;6789;3;831.44
3;Kapstadt;10;Ruhelos;Rita;234;3;203.19
3;Kapstadt;11;Schlaflos;Susi;321;3;808.13
3;Kapstadt;12;Rielos;Lotte;456;3;1555.98
3;Kapstadt;6;Bodenlos;Betty;3450;3;324.81999999999994
3;Kapstadt;13;Muehelos;Martin;222;3;142.89000000000001
3;Kapstadt;14;Leinenlos;Liane;135;3;964.0699999999999

In C#.NET können Sie sehr einfach ein XDocument mit mehreren Teilinformationen (XDeclaration, XComment, XElement, ) generieren. Die Teilinformationen erhalten Sie unschwer über File.ReadAllLines.


var vcsv2XML = new System.Xml.Linq.XDocument(
  new System.Xml.Linq.XDeclaration("1.0", "ISO-8859-1", "yes"),
  new System.Xml.Linq.XComment("Leute Von Heute"),
  new System.Xml.Linq.XElement("ERG",
    from zeile in File.ReadAllLines(@"C:\wg\Mensch.csv")
    where (!zeile.Contains("ORTID;ORTNAME;ID;NAME;VORNAME;GEHALT;IDORT;AUSGABEN"))
    let spalte = zeile.Split(';')
    select new System.Xml.Linq.XElement("DS",
        new System.Xml.Linq.XAttribute("ortid", spalte[0]),
        new System.Xml.Linq.XElement("ortname", spalte[1]),
        new System.Xml.Linq.XElement("Menschid", spalte[2]),
        new System.Xml.Linq.XElement("NACHNAME", spalte[3]),
        new System.Xml.Linq.XElement("VORNAME", spalte[4]),
        new System.Xml.Linq.XElement("Gehalt", spalte[5]),
        new System.Xml.Linq.XElement("idort", spalte[6]),
        new System.Xml.Linq.XElement("Ausgaben", spalte[7])
      )
    )
  );
Console.WriteLine(vcsv2XML.ToString());

Das Ergebnis der Konvertierung lautet dann (verkürzte Darstellung):


<!--Leute Von Heute-->
<ERG>
  <DS ortid="1">
    <ortname>Neustadt</ortname>
    <Menschid>1</Menschid>
    <NACHNAME>Holzflos</NACHNAME>
    <VORNAME>Hugo</VORNAME>
    <Gehalt>234.56</Gehalt>
    <idort>1</idort>
    <Ausgaben>3563.1400000000003</Ausgaben>
  </DS>
  <!-- weitere Ergebnisse -->
  <DS ortid="2">
    <ortname>Darmstadt</ortname>
    <Menschid>8</Menschid>
    <NACHNAME>Rhodos</NACHNAME>
    <VORNAME>Rudi</VORNAME>
    <Gehalt>333.33</Gehalt>
    <idort>2</idort>
    <Ausgaben>740.6999999999999</Ausgaben>
  </DS>
  <!-- weitere Ergebnisse -->
  <DS ortid="3">
    <ortname>Kapstadt</ortname>
    <Menschid>5</Menschid>
    <NACHNAME>Wasistlos</NACHNAME>
    <VORNAME>Willi</VORNAME>
    <Gehalt>6789</Gehalt>
    <idort>3</idort>
    <Ausgaben>831.44</Ausgaben>
  </DS>
  <!-- weitere Ergebnisse -->
</ERG>

Gruppierungen mit LINQ

LINQ bietet auch interessante Alternativen zum Gruppieren von XML-Strukturen (vgl. , , ).

Das soeben aus CSV generierte Resultat können Sie umgehend weiter verwenden, indem Sie die mehrfach auftretenden ortname mit LINQ gruppieren, sodass jeder Ort nur einmal genannt wird.


var output = new System.Xml.Linq.XElement("Orte",
    from grort in vcsv2XML.Descendants("ortname")
    group grort by grort.Value into g
    select new System.Xml.Linq.XElement("Ort",
        g.First().Parent.Descendants("idort"),
    new System.Xml.Linq.XElement("name", g.Key)));
Console.WriteLine(output.ToString());

Das Ergebnis sieht dann so aus:


<Orte>
  <Ort>
    <idort>1</idort>
    <name>Neustadt</name>
  </Ort>
  <Ort>
    <idort>2</idort>
    <name>Darmstadt</name>
  </Ort>
  <Ort>
    <idort>3</idort>
    <name>Kapstadt</name>
  </Ort>
</Orte>

Um weitere Inhalte anzusprechen, die sich aus der Gruppierung ergeben, können Sie so vorgehen:


var output = new System.Xml.Linq.XElement("Orte",
    from grort in vcsv2XML.Descendants("ortname")
    group grort by grort.Value into g
    select new System.Xml.Linq.XElement("Ort",
    g.First().Parent.Descendants("idort"),
    new System.Xml.Linq.XElement("name", g.Key),
    from mm in g
    select (
            new System.Xml.Linq.XElement("Einwohner",
            mm.Parent.Descendants("NACHNAME"),
            mm.Parent.Descendants("VORNAME"),
            mm.Parent.Descendants("Gehalt"),
            mm.Parent.Descendants("Ausgaben")
        ))));
Console.WriteLine(output.ToString());

Das (verkürzt dargestellte) Ergebnis dieser LINQ-basierten Transformation sehen Sie hier:


<Orte>
  <Ort>
    <idort>1</idort>
    <name>Neustadt</name>
    <Einwohner>
      <NACHNAME>Holzflos</NACHNAME>
      <VORNAME>Hugo</VORNAME>
      <Gehalt>234.56</Gehalt>
      <Ausgaben>3563.1400000000003</Ausgaben>
    </Einwohner>
    <!-- weitere Ergebnisse -->
  </Ort>
  <Ort>
    <idort>2</idort>
    <name>Darmstadt</name>
    <Einwohner>
      <NACHNAME>Rhodos</NACHNAME>
      <VORNAME>Rudi</VORNAME>
      <Gehalt>333.33</Gehalt>
      <Ausgaben>740.6999999999999</Ausgaben>
    </Einwohner>
    <!-- weitere Ergebnisse -->
  </Ort>
  <Ort>
    <idort>3</idort>
    <name>Kapstadt</name>
    <Einwohner>
      <NACHNAME>Wasistlos</NACHNAME>
      <VORNAME>Willi</VORNAME>
      <Gehalt>6789</Gehalt>
      <Ausgaben>831.44</Ausgaben>
    </Einwohner>
    <!-- weitere Ergebnisse -->
  </Ort>
</Orte>

wg / 29. September 2018



Fragen? Anmerkungen? Tipps?

Bitte nehmen Sie Kontakt zu mir auf.






Vielen Dank für Ihr Interesse an meiner Arbeit.


V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn

☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de

www.wilfried-grupe.de/cs_LINQdemo2.html