none
Textfile - Zeilenweise, Dictionary (Key, Value) RRS feed

  • Frage

  • Hallo,
    ich habe ein Textfile, das ich einlesen muss.
    Diese Files beinhaltet Kommentare und Wertepaare.
    Dabei will ich das in einen Dictionary haben, um später leichter darauf zugreifen zu können.
    Key ändert sich nicht.
    Wert ändert sich.
    Es funktioniert.
    Dennoch denke ich, dass man es eleganter lösen kann.
    A) Wie macht man am besten Kommentare raus, wenn z.B.
    // nicht vorne steht, sondern mit Leerzeichen
         //
    Wie kann ich sicherstellen, dass das Paar auch da ist.
    B) Gibt es Möglichkeiten das einfacher zu parsen?
      LinQ direkt in ein Dictionary.
     
     Danke für Tipps.
     
     Viele Grüße Sandra

    using (StreamReader sr = new StreamReader(@"Info.txt", Encoding.Default))
    {
    	StringBuilder content = new StringBuilder();
    	string line;
    	// schreibt Zeile für Zeile auf den StringBuilder bis das Ende der Datei erreicht ist
    	while (!sr.EndOfStream)
    	{
    		line = sr.ReadLine();
    
    		if (line.Length > 2)
    		{
    			if (line.Substring(0, 2) != "//")
    				content.AppendLine(line);
    		}
    	}
    
    	string toCheckIt = content.ToString();
    
    
    	Dictionary<string, string> dictionary = new Dictionary<string, string>();
    	string[] items = toCheckIt.TrimEnd(',').Split(',');
    
    	for (int i = 0; i < items.Length; i++)
    	{
    		if ( i % 2 !=0 )
    			dictionary.Add(items[i - 1], items[i]);
    	}
    
    	//Dictionary<string, string> dict = new Dictionary<string, string>();
    	//dict = toCheckIt.Split(',')
    	//      .Select(keyValuePair => keyValuePair.Split(','))
    	//      .ToDictionary(values => values[0], values => values[1]);
    }
    			
    			
    //  Informationen 
    //  01: comment 1
    //  34: comment 2
    
    POSITION1,0,POSITION2,0,POSITION3,0,POSITION4,0,POSITION5,0,POSITION6,0,POSITION7,0,POSITION8,0,POSITION9,0,POSITION10,0,POSITION11,0,POSITION12,0,POSITION13,0,POSITION14,0,POSITION15,0,POSITION16,0,POSITION17,0,POSITION18,0,POSITION19,0,POSITION20,0,POSITION21,0,POSITION22,0,POSITION23,0,POSITION24,0,POSITION25,0,POSITION26,0,POSITION27,0,POSITION28,0,POSITION29,1,POSITION30,1,POSITION31,1,POSITION32,1,POSITION33,1,POSITION34,1,POSITION35,1,POSITION36,1,POSITION37,1,POSITION38,1,POSITION39,1,POSITION40,1,POSITION41,1,POSITION42,1,POSITION43,1,POSITION44,1,POSITION45,1,POSITION46,1,POSITION47,1,POSITION48,1,POSITION49,1,POSITION50,1,POSITION51,1,POSITION52,1,POSITION53,1,POSITION54,1,POSITION55,1,POSITION56,1,POSITION57,1,POSITION58,1,POSITION59,1,POSITION60,1,POSITION61,1,POSITION62,1,POSITION63,1,POSITION64,1,POSITION65,1,POSITION66,1,POSITION67,1,POSITION68,1,POSITION69,1,POSITION70,1,POSITION71,1,POSITION72,1,POSITION73,1,POSITION74,1,POSITION75,1,POSITION76,1,POSITION77,1,POSITION78,1,POSITION79,1,POSITION80,1,POSITION81,1,POSITION82,1,POSITION83,1,POSITION84,1,POSITION85,1,POSITION86,1,POSITION87,1,POSITION88,1,POSITION89,1,POSITION90,1,POSITION91,1,POSITION92,1,POSITION93,1,POSITION94,1,POSITION95,1,POSITION96,1,POSITION97,1,POSITION98,1,POSITION99,1,POSITION100,1,POSITION101,1,POSITION102,1,POSITION103,1,POSITION104,
    

    Dienstag, 14. März 2017 17:51

Antworten

  • Hallo Sandra,

    woher soll nun gewusst werden, die static class zu nutzen.

    Wie bereits geschrieben ist das ein Sprachkonstrukt von C#. Wenn du mehr wissen willst lese die verlinkten Beiträge über Erweiterungsmethoden oder direkt in der Sprachspezifikation nach. Es wird global nach passenden statischen Methoden gesucht die als Erweiterungsmethode dienen könnn.

    So würde es gehen.

    Gibt es irgendwelche Zusätzlichen Informationen in der Exception? Denn wenn es ein Problem mit dem Methodenaufruf gäbe, würde der Compiler dir das bereits sagen. Und in meinem Testprojekt läuft ja alles.

    genau wie es eingelesen wird, muss es zurückgeschrieben werden.

    Das könnte in meinem Beispielprojekt zum Problem werden. Die Implementierung von Dictionary<,> sollte zwar keine Probleme machen, aber man kann die richtige Reihenfolge nicht garantieren. Das müsste man nochmal in einer List<KeyValuePair<,>> umbauen.

    bekommt dann ein Icon mit Pfeil nach unten. Was soll das mir sagen?

    An dem geänderten Icon siehst du, dass du die Methode wie eine Instanzmethode aufrufen kannst, obwohl es gar keine ist.

    D.h. ich übergebe der Funktion ein Objekt, diese dann einfach das Objekt weiter bearbeitet.
    Würde da ein Cast evtl. fehlen.

    Wenn der Compiler nicht meckert passt das alles so wie es ist.

    Du machst selbst für eine Zeile (CommentRow, DataRow) eine Schnittstelle.
    Gibt es hierfür Gründe bzw. wann machst Du so was.

    Irgendwie muss ich die Zeilen ja einzeln verwalten, wenn ich diese wieder 1 zu 1 zurück schreiben will. Um nun alle sicher typisiert verwalten zu können benutzen diese alle eine gemeinsame Schnittstelle. Diese schreibt nur vor, dass man aus dem jeweiligen Objekt wieder einen String für eine Zeile in der Datei machen kann. (Auch wenn ToString von Object geerbt immer da ist, was das ganze etwas sinnlos macht. Ist aber ordentlicher mit einer Schnittstelle).
    Ganz ähnlich arbeitet auch Roslyn, wo auch der neue C# Compiler dazu gehört. Dieser ordnet jedem Sprachkonstrukt eigene Klassen zu und verarbeitet dann diese anstelle des eigentlichen Codes.

    Zu der Sache mit dem Komma am Ende einer Zeile:

    Da müsste man den Code nochmal etwas umbauen, ist keine große Sache mehr.


    Viele Grüße, Tom Lambert - MVP, MCC und MSP
    Wozu Antworten markieren und Posts bewerten? Klicke hier
    Nützliche Links: .NET Quellcode | C#/VB.NET Konverter | GitHub Forum Samples | Account bestätigen (Verify Your Account)
    Ich: Webseite | Facebook | Twitter | Code Snippets | GitHub

    • Als Antwort markiert Sandra Bauer Mittwoch, 29. März 2017 16:58
    Donnerstag, 16. März 2017 20:11
    Moderator

Alle Antworten

  • Hallo Sandra,

    ohne zu wissen, wie deine Eingangsdatei aussieht, kann man da nicht viel sagen.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community


    Dienstag, 14. März 2017 19:22
    Moderator
  • Hallo Stefan,

     

    ups, das habe ich vergessen konkret zu schreiben.

     

    Ausgangsdatei.

     

    •  //  Informationen
    •  //  01: comment 1
    •  //  34: comment 2
    •   

    POSITION1,0,POSITION2,0,POSITION3,0,POSITION4,0,POSITION5,0,POSITION6,0,POSITION7,0,POSITION8,1,  usw.

     

    Viele Grüße Sandra

     

     

    Dienstag, 14. März 2017 20:24
  • Hallo Sandra,

    da stehen Aufzählungspunkte vor den Infomationen?

    Und was soll "POSITION1,0,POSITION2,0,..." bedeuten?

    Soll das ein Wertepaar sein? Falls ja, warum steht das in einer Zeile?

    Es wäre sinnvoller, wenn jedes Paar in einer separaten Zeile stehen würde. Falls das nicht möglich ist, welches eindeutige Merkmal hat der Schlüssel? Steht dort immer "POSITION" vornedran?

    Was soll passieren, wenn es eben kein Paar gibt?

    Was soll passieren, wenn es Lücken gibt?

     


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community



    Dienstag, 14. März 2017 20:26
    Moderator
  • Hallo Sandra,

    zu A) Ich würde dafür ein Regex benutzen. Damit können beliebig viele Leerzeichen vor dem "//" sein.

    zu B) Ich hab einen anderen Ansatz gewählt, jedoch ob der einfacher ist musst du selbst entscheiden. Nut mit Linq oder etwa in einer Zeile etc. ist mir jetzt spontan keine Version eingefallen.

    Hier meine Version:

    using (StreamReader sr = new StreamReader(@"Info.txt", Encoding.Default))
    {
        string line = string.Empty;
        while ((line = sr.ReadLine()) != null)
        {
            var commentRegex = new Regex(@"^\s*//");
            if (String.IsNullOrEmpty(line) || commentRegex.IsMatch(line))
            {
                continue; // Empty line or comment
            }
    
            var elements = line.TrimEnd(',').Split(',');
            var dictionary = new Dictionary<string, string>();
            for (int i = 0; i + 1 != elements.Length; i=i+2)
            {
                dictionary.Add(elements[i], elements[i + 1]);
            }
        }
    }

    Gruß,

    David

    Dienstag, 14. März 2017 20:36
  • Hallo Stefan,

    so sieht die Datei aus.

    // Informationen
    // 01: comment 1
    // 34: comment 2

    POSITION1,0,POSITION2,0,POSITION3,0,POSITION4,0,POSITION5,0,POSITION6,0,POSI
    TION7,0,POSITION8,1 usw.

    Es sind Wertepaare, die ich so bekomme, kann ich nicht ändern.

    Ich muss das einlesen.

    POSITION1,0
    POSITION2,0
    ...
    POSITIONN,1

    Dann habe ich eine Liste, Dictionary.

    Dieses muss ich analysieren, was machen.
    Das Ergebnis ist dann z.B.
    Ergebnisdatei, die ich schreiben muss.
    Wie Eingang, also mit den Kommentaren.

    POSITION1,1
    POSITION2,4
    POSITION3,3
    POSITION4,4
    ...
    POSITIONN,1

    Jede Position kann einen Wert zwischen 0 und 9 bekommen.

    Hoffentlich klar nun.

    Danke jetzt schon.
    Grüße Sandra
    Dienstag, 14. März 2017 20:50
  • Hallo zusammen,

    Regex kann zwar toll sein, aber auch sehr langsam. Daher würde ich hier wohl auf etwas derartiges zurück greifen:

    if (String.IsNullOrEmpty(line) || line.TrimStart().Startswith("//"))
    {
        continue; // Empty line or comment
    }

    Oder wenn es um komplizierter Ausdrücke geht und Regex doch einfacher ist: Dann bitte nur einmal das Regex-Objekt (ggf. als compiled) erzeugen und wieder verwenden. 

    @Sandra

    Wenn ich dich richtig verstehe, dann kann die Datei 2 Arten von Zeilen enthalten:

    1. Kommentarzeilen
      Diese beginnen immer mit optionalen Leerzeichen, gefolgt von //. Hinter den // kann beliebiger Text stehen der ignoriert wird.
    2. Datenzeilen
      Diese Enthalten noch dem Schema Key1,Value1,Key2,Value2,... die Daten.

    Kann es von 2. mehrere vorkommen in einer Datei geben? Dein Beispiel sieht für mich nicht danach aus.

    Hast du vielleicht eine BNF-Darstellung der Datei o.ä.? Das wäre vielleicht am einfachsten, damit wir uns nicht falsch verstehen.


    Viele Grüße, Tom Lambert - MVP, MCC und MSP
    Wozu Antworten markieren und Posts bewerten? Klicke hier
    Nützliche Links: .NET Quellcode | C#/VB.NET Konverter | GitHub Forum Samples | Account bestätigen (Verify Your Account)
    Ich: Webseite | Facebook | Twitter | Code Snippets | GitHub

    Dienstag, 14. März 2017 21:05
    Moderator
  • Kann es von 2. mehrere vorkommen in einer Datei geben? Dein Beispiel sieht für mich nicht danach aus.

    Hast du vielleicht eine BNF-Darstellung der Datei o.ä.? Das wäre vielleicht am einfachsten, damit wir uns nicht falsch verstehen.


    Viele 

    Hallo Tom,

    Danke, alles richtig erkannt.

    Key ist eindeutig. Nichts doppelt.

    BNF nein habe ich nicht, für was steht es denn?

    Eine Art Schema?

    Bzgl. Schreiben der Ergebnisse, einfach das dictionary durchlaufen und dann eine Zeile schreiben.

    Vorher optional noch die Kommentare einfügen.

    Viele Grüße Sandra

    Dienstag, 14. März 2017 21:19
  • Hallo Sandra,

    und nochmal die Fragen:

    Welches eindeutige Merkmal hat der Schlüssel? Steht dort immer "POSITION" vornedran?

    Was soll passieren, wenn es eben kein Paar gibt?

    Was soll passieren, wenn es Lücken gibt?

    Zusatzfragen:

    Kann es also maximal 10 Positionen (0-9) geben?

    Kommt eine Zeile in der Form "POSITION1,1,POSITION2,1,..." nur ein einziges mal in der Datei vor? Oder könnte es auch mehrere Zeilen solcher Art geben und falls ja, wie sehen die aus?


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community


    Dienstag, 14. März 2017 21:39
    Moderator
  • Hallo Sandra,

    BNF steht für Backus-Naur-Form. Damit kann man Texte syntaktisch beschreiben. So beispielsweise auch komplette Programmiersprachen. Sich so etwas anzufertigen lohnt sich IMO sehr bei eigenen Formaten, um Fehler zu vermeiden. Außerdem können so andere leichter nachvollziehen wie bestimmte Details aufgebaut sein sollen.

    Ich würde das nun in etwa so implementieren:

    internal class Program
    {
        static void Main(string[] args)
        {
            var dict = System.IO.File.ReadLines(@"test.txt")       // Alle Zeilen einlesen
                .Where(line => !string.IsNullOrWhiteSpace(line))   // Zeilen die nur Leerzeichen enthalten aussortieren
                .Where(line => !line.TrimStart().StartsWith("//")) // Zeilen die mit // (und ggf. Leerzeichen davor) anfangen aussortieren
                .SelectMany(SplitIntoKeyValuePairEnumeration)      // Die restlichen Zeilen an jedem 2. Komma aufteilen und alles in eine Liste packen
                .ToDictionary();                                   // Die selbst geschriebene Erweiterungsmethode nutzen
        }
    
        public static IEnumerable<KeyValuePair<string, string>> SplitIntoKeyValuePairEnumeration(string source)
        {
            var arr = source.Split(',');
            if (arr.Length % 2 != 0)
            {
                throw new InvalidOperationException();
            }
            for (var i = 0; i < arr.Length; i += 2)
            {
                yield return new KeyValuePair<string, string>(arr[i], arr[i + 1]);
            }
        }
    }
    
    public static class LinqExtensions
    {
        //Ist in vielen Faellen ganz hilfreich, daher ausgelagert
        public static IDictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source)
        {
            return source.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
        }
    }

    Der wichtige Code ist dadurch nun relativ kurz gehalten und das etwas komplexere wurde in eine extra Methode ausgelagert.

    Viele Grüße, Tom Lambert - MVP, MCC und MSP
    Wozu Antworten markieren und Posts bewerten? Klicke hier
    Nützliche Links: .NET Quellcode | C#/VB.NET Konverter | GitHub Forum Samples | Account bestätigen (Verify Your Account)
    Ich: Webseite | Facebook | Twitter | Code Snippets | GitHub


    Dienstag, 14. März 2017 22:09
    Moderator
  • Hallo Stefan,

     

    Welches eindeutige Merkmal hat der Schlüssel? Steht dort immer "POSITION" vornedran?

    Ja richtig.

    Was soll passieren, wenn es eben kein Paar gibt?

    Exception, Fehlermeldung.

    Was soll passieren, wenn es Lücken gibt?

    Exception, Fehlermeldung.

     

    Zusatzfragen:

    Kann es also maximal 10 Positionen (0-9) geben?

    0 bis N typischerweise, max. 500

    Kommt eine Zeile in der Form "POSITION1,1,POSITION2,1,..." nur ein einziges mal in der Datei vor? Oder könnte es auch mehrere Zeilen solcher Art geben und falls ja, wie sehen die aus?

    Eindeutig, ja. Eine Zeile.

     

    Dann bearbeiten und abspeichern.

     

    Grüße Sandra

     

    Mittwoch, 15. März 2017 05:08

  • Der wichtige Code ist dadurch nun relativ kurz gehalten und das etwas komplexere wurde in eine extra Methode ausgelagert.

    Hallo Tom,
    prinzipiell habe ich so etwas gesucht.
    Leider funktioniert es nicht.
    Fehlermeldung
       Der Vorgang ist aufgrund des aktuellen Zustands des Objekts ungültig.
    a) wie löst man es ohne static Klasse - LinqExtensions
    b) wie löst man es mit static Klasse - LinqExtensions
    Kannst Du es noch detaillierter erläutern.
    Danke.
    Kommentar, Leerzeilen aussortieren, ja, aber ich muss das Ergebnis zwischenspeichern,
    da ich ja die Values der Wertepaare verändern muss und neu in eine Datei schreiben muss.
    Dann bräuchte ich 2 Abfragen.

    Viele Grüße Sandra

    private void frmTest_Load(object sender, EventArgs e)
    {
    var dict = (System.IO.File.ReadLines(@"Info.txt")       // Alle Zeilen einlesen
    .Where(line => !string.IsNullOrWhiteSpace(line))   // Zeilen die nur Leerzeichen enthalten aussortieren
    .Where(line => !line.TrimStart().StartsWith("//")) // Zeilen die mit // (und ggf. Leerzeichen davor) anfangen aussortieren
    .SelectMany(SplitIntoKeyValuePairEnumeration))      // Die restlichen Zeilen an jedem 2. Komma aufteilen und alles in eine Liste packen
    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);   // Die selbst geschriebene Erweiterungsmethode nutzen
    .ToDictionary();   // Erweiterungsmethode nutzen bekannt geben, wie?
    }
    
    public static IEnumerable<KeyValuePair<string, string>> SplitIntoKeyValuePairEnumeration(string source)
    {
    	var arr = source.Split(',');
    	if (arr.Length % 2 != 0)
    	{
    		throw new InvalidOperationException();
    	}
    	for (var i = 0; i < arr.Length; i += 2)
    	{
    		yield return new KeyValuePair<string, string>(arr[i], arr[i + 1]);
    	}
    }
    
    public static class LinqExtensions
    {
    	//Ist in vielen Faellen ganz hilfreich, daher ausgelagert
    	public static IDictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source)
    	{
    		return source.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
    	}
    }
    


    Mittwoch, 15. März 2017 17:42
  • zu A) Ich würde dafür ein Regex benutzen. Damit können beliebig viele Leerzeichen vor dem "//" sein.

    zu B) Ich hab einen anderen Ansatz gewählt, jedoch ob der einfacher ist musst du selbst entscheiden. Nut mit Linq oder etwa in einer Zeile etc. ist mir jetzt spontan keine Version eingefallen.


    Hallo David,
    Deine Lösung mit der RegEx gefällt mir auch.
    Hast Du einen Tipp, wie man auf den Ausdruck kommt?
    Hilfstools etc. Sieht zumindest etwas komplex aus.
    Beispiel, was wäre zu tun, wenn ich alle Zeilen mit diesen Zeichen "###" incl. Leerzeichen am Anfang ignorieren möchte.
    new Regex(@"^\s*//");
    Kommentar ist das //
    Leerzeichen, das *
    ^\s  für..?
    Könnte man über die RegEx auch dann mein Dictionary befüllen, ohne Split?
    Wie würdest da in meinem Fall suchen, ohne ein 500 Seiten Buch zu lesen.
    Man brauch ja meist nicht viel.
    Nachteil, offensichtlich Performance, sprich langsam, lt. Forenmitglieder.
    Grüße Sandra



    • Bearbeitet Sandra Bauer Mittwoch, 15. März 2017 17:48 Format
    Mittwoch, 15. März 2017 17:44
  • Hallo Sandra,

    für den sehr einfachen Ausdruck brauchst du keine 500 Seiten zu lesen :)

    Der Ausdruck besteht aus den folgenden Teilen:

    ^ - Beginne am Anfange des strings / am Anfang der gelesenen Zeile

    \s - Erkenne ein Whitespace (Whitespace ist hier Leerzeichen, Tabulator, ...)

    \s* - Erkenne beliebig viele Whitespace also kein Whitespace oder mehr

    Das heißt der * bedeutet beliebige Anzahl der Zeichen. Das ist Gleichbedeutend mit dem Ausdruck \s{0,}. Das heist kein Whitespace oder eine beliebige Anzahl von Whitespaces.

    Und dann kommen die // die den Kommentar einleiten.

    Zusammen heißt das wenn am Anfang der Zeile nach beliebig vielen Whitespaces ein "//" kommt ist das ein Kommentar.

    > Beispiel, was wäre zu tun, wenn ich alle Zeilen mit diesen Zeichen "###"

    > incl. Leerzeichen am Anfang ignorieren möchte. 

    Kannst du jetzt alleine lösen, oder? :)

    Der Ausdruck dafür wäre dann: ^\s*###

    Wenn du mehr lesen willst schau mal auf Wikipedia vorbei: https://de.wikipedia.org/wiki/Regul%C3%A4rer_Ausdruck

    Dort wird unter anderem erklärt was die einzelnen Zeichen wie z.B. "\s" bedeuten. Sieh einfach mal im Teil "Reguläre Ausdrücke in der Praxis" nach.

    > Könnte man über die RegEx auch dann mein Dictionary befüllen, ohne Split?

    Split kann man auch mit Regex machen, ja. Jedoch ist der Split so einfach, dass es hier nicht sinnvoll ist. Wenn du z.B. an jedem Komma wenn davor und danach noch beliebige Leerzeichen sind, dann würde das schon mehr sinn machen ein Regex für das Split zu verwenden. Oder wenn du nur am Komma splitten willst, wenn danach eine bestimmte Zeichenfolge kommt. Aber wenn du einfach nur an einem Komma splitten willst ist ein Regex nicht sinnvoll.

    Um ein Beispiel zu machen:

    Wenn du nur am Komma splitten wollen würdest wenn danach Leerzeichen und dann eine Zahl kommt.

    D.h. string: "POSITION1,0" = splitten am Komma

    string: "POSITION1,A" = nicht splitten am Komma

    Dann könnte man beim splitten über die Verwendung eines Regex nachdenken.

    Input: POSITION1,A,POSITION2,0,POSITION3

    Code:

    using (StreamReader sr = new StreamReader(@"Info.txt", Encoding.Default))
    {
        string line = string.Empty;
        while ((line = sr.ReadLine()) != null)
        {
            var commentRegex = new Regex(@"^\s*//");
            if (String.IsNullOrEmpty(line) || commentRegex.IsMatch(line))
            {
                continue; // Empty line or comment
            }
    
            var splitRegex = new Regex(@",\s*(?=\d)");
            var elements = splitRegex.Split(line.TrimEnd(','));
        }
    }
    Das Beispiel macht vielleicht für deinen Anwendungsfall nicht soviel Sinn, aber war ja auch nur um das Prinzip zu zeigen :)

    Gruß,

    David


    Mittwoch, 15. März 2017 18:28
  • Hi Sandra,

    nur noch kurz zum Punkt Performance.

    Ja es ist richtig dass die Perfomance ein Nachteil ist, deswegen sollte man sich immer genau überlegen ob man ein Regex verwendet.

    Bei komplexen String Operationen finde ich sie aber echt gut zu verwenden. Ein Beispiel aus meiner Praxis wäre z.B. das Erkennen von Autonummernschildern. Wenn du eine Datenbank von Schildern hast und Erkennen willst ob das Schild z.B. zu einem Auto aus Deutschland, Frankreich oder Italien gehört. Dann ist es viel einfacher ein Regex für jeden Typ zu schreiben, wie eine größerer Methode die den Typ ohne Regex parst.

    Im Endeffekt musst du selber entscheiden, wie sehr die Perfomance wichtig ist. Kommt ja auch immer ein bisschen auf den Anwendungsfall und die Art dem Software an, die du erstellen möchtest.

    Gruß,

    David

     

    Mittwoch, 15. März 2017 18:43
  • Hallo Sandra,

    wo erhältst du denn die Exception? Für mich sieht das nach einer InvalidOperationException aus. Das könnte entweder an einer fehlerhaften Linq-Methode liegen (sehe aber keine) oder an meiner SplitIntoKeyValuePairEnumeration-Methode liegen. Denn diese wirft eine solche Exception, wenn eine gerade Anzahl an Kommas im String vorhanden sind; wenn also beispielsweise zu einem Key der Wert fehlt.

    zu a und b:
    Kennst du das Konzept der Erweiterungsmethoden? Man deklariert statische Methoden und der erste Parameter bekommt "this" als Schlüsselwort vor den Typnamen. Dadurch kannst du dann die Methode auf ebendiesem Typ wie eine Instanzmethode aufrufen. Das ist eines der Grundkonzepte von Linq. So gut wie alle Listen-Typen implementieren IEnumerable weswegen die Linq-Erweiterungsmethoden und mein ToDictionary so gut wie überall funktionieren.

    Mein a.ToDictionary-Aufruf ohne Parameter entspricht also einem LinqExtensions.ToDictionary(a) aufruf. (Wobei a eine Auflistung ist, hier die aussortierte Liste.)

    Wenn du die Werte auch schreiben musst, dann würde ich das ganze wohl sogar in eine eigene Klasse auslagern.

    Spielt die Reihenfolge der Werte in der Datenzeile eine Rolle? Oder ist nur die Key,Value Zuordnung wichtig?
    Müssen beim zurück schreiben die Kommentare erhalten bleiben? Das würde es nämlich etwas komplizierter machen, aber trotzdem noch recht einfach machbar.

    PS: Wenn du mal einen Ansatz einer ordentlichen Implementierung mit Klassen haben willst, die alle Daten erhalten und diese auch wieder zurück schrieben können, dann findest du hier ein von mir erstelltes Beispielprojekt. Das alles ist nicht vollständig getestet, das überlasse ich dir.


    Viele Grüße, Tom Lambert - MVP, MCC und MSP
    Wozu Antworten markieren und Posts bewerten? Klicke hier
    Nützliche Links: .NET Quellcode | C#/VB.NET Konverter | GitHub Forum Samples | Account bestätigen (Verify Your Account)
    Ich: Webseite | Facebook | Twitter | Code Snippets | GitHub


    Mittwoch, 15. März 2017 20:16
    Moderator
  • Hallo,

    public static class LinqExtensions
    {

    Woher soll ToDictionary wissen, dass es die static Klasse verwenden soll.
    Woher kennt die Abfrage den Typ?
    Ausgang muss wie die Eingangsdatei sein.

    Ich schaue es mir an. Ich verwende noch vs2010.

    Grüße Sandra



    Donnerstag, 16. März 2017 07:14
  • Hallo Sandra,

    der C# Compiler guckt global nach passenden Methoden und hat dabei bestimmte Präferenzen. Das gibt es IMO seit .NET 3.5 und C# 3.0 und somit auch schon in VS 2010. 

    Siehe auch:


    Viele Grüße, Tom Lambert - MVP, MCC und MSP
    Wozu Antworten markieren und Posts bewerten? Klicke hier
    Nützliche Links: .NET Quellcode | C#/VB.NET Konverter | GitHub Forum Samples | Account bestätigen (Verify Your Account)
    Ich: Webseite | Facebook | Twitter | Code Snippets | GitHub

    Donnerstag, 16. März 2017 17:13
    Moderator
  • Hallo Tom,

    anbei die Zusammenfassung, Tests.

    var dict = (System.IO.File.ReadLines(@"Info.txt")       // Alle Zeilen einlesen
    .Where(line => !string.IsNullOrWhiteSpace(line))   // Zeilen die nur Leerzeichen enthalten aussortieren
    .Where(line => !line.TrimStart().StartsWith("//")) // Zeilen die mit // (und ggf. Leerzeichen davor) anfangen aussortieren
    .SelectMany(SplitIntoKeyValuePairEnumeration))      // Die restlichen Zeilen an jedem 2. Komma aufteilen und alles in eine Liste packen
    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);   // Die selbst geschriebene Erweiterungsmethode nutzen
    .ToDictionary();   // Erweiterungsmethode nutzen bekannt geben, wie?

    Da bekomme ich die Exception, weil ich meine, woher soll nun gewusst werden, die static class zu nutzen.
    So würde es gehen.
    try
    {
     var dict = (System.IO.File.ReadLines(@"Info.txt")       // Alle Zeilen einlesen
      .Where(line => !string.IsNullOrWhiteSpace(line))   // Zeilen die nur Leerzeichen enthalten aussortieren
      .Where(line => !line.TrimStart().StartsWith("//")) // Zeilen die mit // (und ggf. Leerzeichen davor) anfangen aussortieren
      .SelectMany(SplitIntoKeyValuePairEnumeration));     // Die restlichen Zeilen an jedem 2. Komma aufteilen und alles in eine Liste packen
     // .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);   // Die selbst geschriebene Erweiterungsmethode nutzen
      var dict2 = LinqExtensions.ToDictionary(dict);
    }
    catch (Exception ex)
    { 
    }
    Es ist immer ein Komma am Ende.
    //  Informationen 
    //  1: comment 1
    //  0: comment 2
    POSITION1,0,POSITION2,0,POSITION3,0,....1,POSITION247,1,POSITION248,1,POSITION249,1,POSITION250,1
    wenn das Komma weg wäre. Ist aber dran.
    Es müsste so gehen.
    POSITION1,0,POSITION2,0,POSITION3,0,....1,POSITION247,1,POSITION248,1,POSITION249,1,POSITION250,1,
    >Spielt die Reihenfolge der Werte in der Datenzeile eine Rolle?
    ja, genau wie es eingelesen wird, muss es zurückgeschrieben werden. Mit den values 0..9, je nach Prozess.
    >Oder ist nur die Key,Value Zuordnung wichtig?
    Müssen beim zurück schreiben die Kommentare erhalten bleiben?
    Kommentare, Leerzeilen müssen bleiben.
    >Kennst du das Konzept der Erweiterungsmethoden?
    >Man deklariert statische Methoden und der erste Parameter bekommt "this" als Schlüsselwort vor den Typnamen
    bekommt dann ein Icon mit Pfeil nach unten. Was soll das mir sagen?
    >Mein a.ToDictionary-Aufruf ohne Parameter entspricht also einem LinqExtensions.ToDictionary(a) aufruf.
    >(Wobei a eine Auflistung ist, hier die aussortierte Liste.)
    D.h. ich übergebe der Funktion ein Objekt, diese dann einfach das Objekt weiter bearbeitet.
    Würde da ein Cast evtl. fehlen.
    Dein Beispielprojekt habe ich angeschaut.
    Du machst selbst für eine Zeile (CommentRow, DataRow) eine Schnittstelle.
    Gibt es hierfür Gründe bzw. wann machst Du so was.
    Wenn ich es richtig sehe, kannst Du somit den Typ abfragen und nur diesen Typen
    in das Dictionary werfen. Ja stimmt, guter Ansatz.
       public IList<ILine> Lines { get { return new List<ILine>(); }}
      Du hast eine Liste mit dem Interface 'Line'
      Jetzt gehst Du die Liste durch, wenn der Type DataRow ist,  tust Du splitten.
    >Wenn du mal einen Ansatz einer ordentlichen Implementierung mit Klassen haben willst,
    Viele Fragen, falls Du antworten willst, bedanke ich mich natürlich.
    Grüße Sandra
    Donnerstag, 16. März 2017 17:59
  • Hallo Sandra,

    woher soll nun gewusst werden, die static class zu nutzen.

    Wie bereits geschrieben ist das ein Sprachkonstrukt von C#. Wenn du mehr wissen willst lese die verlinkten Beiträge über Erweiterungsmethoden oder direkt in der Sprachspezifikation nach. Es wird global nach passenden statischen Methoden gesucht die als Erweiterungsmethode dienen könnn.

    So würde es gehen.

    Gibt es irgendwelche Zusätzlichen Informationen in der Exception? Denn wenn es ein Problem mit dem Methodenaufruf gäbe, würde der Compiler dir das bereits sagen. Und in meinem Testprojekt läuft ja alles.

    genau wie es eingelesen wird, muss es zurückgeschrieben werden.

    Das könnte in meinem Beispielprojekt zum Problem werden. Die Implementierung von Dictionary<,> sollte zwar keine Probleme machen, aber man kann die richtige Reihenfolge nicht garantieren. Das müsste man nochmal in einer List<KeyValuePair<,>> umbauen.

    bekommt dann ein Icon mit Pfeil nach unten. Was soll das mir sagen?

    An dem geänderten Icon siehst du, dass du die Methode wie eine Instanzmethode aufrufen kannst, obwohl es gar keine ist.

    D.h. ich übergebe der Funktion ein Objekt, diese dann einfach das Objekt weiter bearbeitet.
    Würde da ein Cast evtl. fehlen.

    Wenn der Compiler nicht meckert passt das alles so wie es ist.

    Du machst selbst für eine Zeile (CommentRow, DataRow) eine Schnittstelle.
    Gibt es hierfür Gründe bzw. wann machst Du so was.

    Irgendwie muss ich die Zeilen ja einzeln verwalten, wenn ich diese wieder 1 zu 1 zurück schreiben will. Um nun alle sicher typisiert verwalten zu können benutzen diese alle eine gemeinsame Schnittstelle. Diese schreibt nur vor, dass man aus dem jeweiligen Objekt wieder einen String für eine Zeile in der Datei machen kann. (Auch wenn ToString von Object geerbt immer da ist, was das ganze etwas sinnlos macht. Ist aber ordentlicher mit einer Schnittstelle).
    Ganz ähnlich arbeitet auch Roslyn, wo auch der neue C# Compiler dazu gehört. Dieser ordnet jedem Sprachkonstrukt eigene Klassen zu und verarbeitet dann diese anstelle des eigentlichen Codes.

    Zu der Sache mit dem Komma am Ende einer Zeile:

    Da müsste man den Code nochmal etwas umbauen, ist keine große Sache mehr.


    Viele Grüße, Tom Lambert - MVP, MCC und MSP
    Wozu Antworten markieren und Posts bewerten? Klicke hier
    Nützliche Links: .NET Quellcode | C#/VB.NET Konverter | GitHub Forum Samples | Account bestätigen (Verify Your Account)
    Ich: Webseite | Facebook | Twitter | Code Snippets | GitHub

    • Als Antwort markiert Sandra Bauer Mittwoch, 29. März 2017 16:58
    Donnerstag, 16. März 2017 20:11
    Moderator