Laden...

File.ReadAllLine und Text mit List<T> ersetzen

Letzter Beitrag vor 6 Monaten 14 Posts 522 Views
File.ReadAllLine und Text mit List<T> ersetzen

Hallo.

Leider ging wohl mein letzter Beitrag im Nirvana unter oder wurde schlicht nicht Veröffentlicht. Dann versuche ich es mal in einem anderen Bereich 😉

Also: ich lese eine relativ große Textdatei in einen string[] ein und durchlaufe diesen mit einer foreach Schleife.

Dann suche ich nach einem bestimmten Vorkommen und lese danach die Zeilen bis zu nächsten }-Zeichen aus. Diese Daten werden in einer List<T> gespeichert, geändert und mit einem StringBuilder wieder ausgegeben. So weit, so Schlecht. Nun mein Problem:

Ich müsste diese Zeilen, die ich vorher kopiert habe, entfernen und exakt an dieser Stelle die neuen Daten einfügen. Zeilennummer wo die Sachen standen, habe ich und die Anzahl der List.Count was dann die Endzeile ergibt.

Wie mache ich das ?

Ich war so Stolz auf die Lösung, leider passte Sie nicht zum Problem 😃

Wenn du den Content der gesamten Datei in der Liste hast, ist es am einfachsten die Datei komplett neu zuschreiben.
Dann kannst du einfach mit File.WriteAllLines arbeiten und die Liste dann schreiben lassen.
Wäre die einfachere Lösung als selbst die passenden Zeilen ermitteln und zu schreiben.

Doku:
https://learn.microsoft.com/de-de/dotnet/api/system.io.file.writealllines?view=net-8.0#system-io-file-writealllines(system-string-system-collections-generic-ienumerable((system-string)))

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

Re...

Klar, das war auch mein Plan, leider klappt das nicht.

Ich hänge mal bisschen Code an...

Ich kommentiere die relevanten Zeilen

    public static void ADD_JOB(string STARTFIRMA, string STARTORT)
    {
        SII_Utilities.Load_LAST_GAME_SII();
        var _input = File.ReadAllLines(MainWindow.Global.TL_ETS_GAME_DRIVE_ENC_PATH);
        var filePos_start = 0;
        var filePos_end = 0;
        
        
        foreach (var match in _input.Select((text, index) => new { text, lineNumber = index + 1 }).Where(x => x.text.Contains("company : company.volatile." + STARTFIRMA + "." + STARTORT)))
        {
// ich suche in der Datei nach einer bestimmten Zeile, wenn er die gefunden hat, wird die Zeilennummer gespeichert
            filePos_start = match.lineNumber;
            
            var job = GetLines(match.lineNumber - 1);
            filePos_end = job.Count; // die Länge der gefundenen Zeilen wird gespeichert
            
            string tagLine, dataLine, _nameless = "_nameless.";
            foreach (var row1 in job)
            {
                
                if (row1.Contains(':'))
                {
                    string[] splittedLine = row1.Split(new char[] { ':' }, 2);
                    tagLine = splittedLine[0].Trim();
                    dataLine = splittedLine[1].Trim();
                }
                else
                {
                    tagLine = row1.Trim();
                    dataLine = "";
                }
/// ab hier werden die Daten ab der gefundenen Zeile eingelesen:
                switch (tagLine)
                {
                    case "":
                    {
                        break;
                    }
                    
                    case "company":
                    {
                        JOB_CLASS.COMPANY = dataLine;
                        break;
                    }
                    
                    case "permanent_data":
                    {
                        JOB_CLASS.PERMANENT_DATA = dataLine;
                        break;
                    }
                    
                    case "delivered_trailer":
                    {
                        JOB_CLASS.DELIVERED_TRAILER = dataLine;
                        break;
                    }
                    
                    case "delivered_pos":
                    {
                        JOB_CLASS.DELIVERED_POS = int.Parse(dataLine);
                        break;
                    }
                    
                    case var s when s.StartsWith("delivered_pos["):
                    {
                        JOB_CLASS.DELIVERY_POS.Add(new Vector_3f(dataLine));
                        break;
                    }
                    
                    case "job_offer":
                    {
                        JOB_CLASS.JOB_OFFER_ANZ = int.Parse(dataLine);
                        break;
                    }
                    
                    case var s when s.StartsWith("job_offer["):
                    {
                        JOB_CLASS.JOB_OFFERS.Add(dataLine);
                        break;
                    }
                    
                    case "cargo_offer_seeds:":
                    {
                        JOB_CLASS.CARGO_OFFER_SEED = int.Parse(dataLine);
                        break;
                    }
                    
                    case var s when s.StartsWith("cargo_offer_seeds["):
                    {
                        JOB_CLASS.CARGO_OFFER_SEED_LIST.Add(uint.Parse(dataLine));
                        break;
                    }

                    case "discovered":
                    {
                        JOB_CLASS.DISCOVERED = bool.Parse(dataLine);
                        break;
                    }
                    
                    case "reserved_trailer_slot":
                    {
                        JOB_CLASS.RESERVED_TRAILER_SLOT = dataLine;
                        break;
                    }
                    
                    case "state":
                    {
                        JOB_CLASS.STATE = dataLine;
                        break;
                    }
                    
                    case "state_change_time":
                    {
                        JOB_CLASS.STATE_CHANGE_TIME = dataLine;
                        break;
                    }
                }
            }
            
            try
            {
                SII_Utilities.Load_LAST_GAME_SII(); // hier werden alle Zeilen nochmal eingelesen um dann die entsprechenden Zeilen zu entfernen
                _input = File.ReadAllLines(MainWindow.Global.TL_ETS_GAME_DRIVE_ENC_PATH);
                List<string> remove = new List<string>(_input);
                Console.WriteLine(@"Remove Range : " + filePos_start + " - " + filePos_end);
                remove.RemoveRange(filePos_start, (filePos_start+filePos_end));
            }
            
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + @" " + ex.StackTrace);
            }
        }

Und die gespeicherten Daten könnte ich theoretisch als text wieder einfügen aber wie?

 internal static string PrintOut(string _NAMELESS, string ZIELORT, string ZIELFIRMA, int STRECKE, string LADUNG)
    {
        string returnString = "";

        StringBuilder returnSB = new StringBuilder();
        returnSB.AppendLine("company : " + JOB_CLASS.COMPANY);
        returnSB.AppendLine(" permanent_data: " + JOB_CLASS.PERMANENT_DATA);
        returnSB.AppendLine(" delivered_trailer: " + JOB_CLASS.DELIVERED_TRAILER);
        returnSB.AppendLine(" delivered_pos: " + JOB_CLASS.DELIVERY_POS.Count);
        for (int i = 0; i < JOB_CLASS.DELIVERY_POS.Count; i++)
            returnSB.AppendLine(" delivered_pos[" + i + "]: " + JOB_CLASS.DELIVERY_POS[i]);
        returnSB.AppendLine(" job_offer: " + (JOB_CLASS.JOB_OFFER_ANZ+1));
        for (int i = 0; i < JOB_CLASS.JOB_OFFERS.Count; i++)
            returnSB.AppendLine(" job_offer[" + i + "]: " + JOB_CLASS.JOB_OFFERS[i]);
        returnSB.AppendLine(" job_offer[" + (JOB_CLASS.JOB_OFFERS.Count) + "]: " + "_nameless.TTT.TTTT.TTTT");
        returnSB.AppendLine(" cargo_offer_seeds: " + JOB_CLASS.CARGO_OFFER_SEED_LIST.Count);
        for (int i = 0; i < JOB_CLASS.CARGO_OFFER_SEED_LIST.Count; i++)
            returnSB.AppendLine(" cargo_offer_seeds[" + i + "]: " + JOB_CLASS.CARGO_OFFER_SEED_LIST[i].ToString());
        returnSB.AppendLine(" discovered: " + JOB_CLASS.DISCOVERED.ToString().ToLower());
        returnSB.AppendLine(" reserved_trailer_slot: " + (JOB_CLASS.RESERVED_TRAILER_SLOT == null ? "nil" : JOB_CLASS.RESERVED_TRAILER_SLOT));
        returnSB.AppendLine("}");
        returnSB.AppendLine();
        
        returnSB.AppendLine(" job_offer_data : " + _NAMELESS);
        returnSB.AppendLine(" target: \"" + ZIELFIRMA + "." + ZIELORT + "\"");
        returnSB.AppendLine(" expiration_time: 1500");
        returnSB.AppendLine(" urgency: 0");
        returnSB.AppendLine(" shortest_distance_km: " + STRECKE);
        returnSB.AppendLine(" ferry_time: 0");
        returnSB.AppendLine(" ferry_cost: 0");
        returnSB.AppendLine(" cargo: cargo." + LADUNG);
        returnSB.AppendLine(" cargo: cargo." + LADUNG);
        returnSB.AppendLine(" company_truck: renault_premium_4x2_a");
        returnSB.AppendLine(" trailer_variant: trailer.ffb_eut35");
        returnSB.AppendLine(" trailer_definition: trailer_def.feldbinder.eut.single_3_35.silo_35_3p");
        returnSB.AppendLine(" units_count: 35");
        returnSB.AppendLine(" fill_ratio: 1");
        returnSB.AppendLine("  trailer_place: 0");
        returnSB.AppendLine("}");
        
        returnString = returnSB.ToString();

        return returnString;
    }

Ich war so Stolz auf die Lösung, leider passte Sie nicht zum Problem 😃

Der Code ist alles andere als lesbar und ohne Hintergrundwissen zum Aufbau der Datei nicht ganz klar.
Der Hintergrund scheint zu sein, dass es sich dabei um eine Job Liste für das Spiel Euro Truck Simulator 2 (ETS2) handelt.

Es wäre vermutlich einfacher, wenn du den Code umstrukturierst als am jetzigen festzuhalten.
Wenn der Aufbau der Job Einträge gleich ist, was dein StringBuilder Ansatz suggeriert, wäre es vermutlich sauberer eine eigene Klasse für die Jobs sowie zum lesen/schreiben der Liste jeweils eine Reader/Writer Klasse anzulegen.
Der Reader kann dir im einfachsten Fall aus der Datei die Jobs als Liste liefern, der Aufbau sollte im einfachsten Fall für alle Jobs gleich sein.
Der Writer kann diese Liste anch Anpassung dann sauber zurück schreiben.
Damit wäre dein Code sauber aufgeteilt und sollte nur gültige Jobs lesen/schreiben.

Wenn es sich nicht zufällig bei dem Format der Dateien um JSON handelt, könntest du sogar direkt über De/Serialisierung arbeiten z.B. mit System.Text.Json
Dann kannst du dir die Reader/Writer Klassen sparen, gibt es dann Quasi an Bord.

Nachtrag:
Könntest du eine Beispieldatei liefern bzw. eien Ausschnitt mit mehreren Jobs pasten?
Dann könnte man u.U. das Format ableiten, aber sieht sehr nach JSON aus.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

Hallo und willkommen,

du könntest einfach die zu löschenden Zeilen im Array _input auf String.Empty setzen und dann den mit dem StringBuilder erzeugten Text in _input[filePos_start]setzen.

Und dann mit File.WriteAllLines(...) wieder abspeichern (so brauchst du auch keine temp. List<string>- hast dann allerdings entsprechende Leerzeilen).

Oder alternativ, wie bisher, in der List<string> die Zeilen löschen und dann mit Insert(filePos_start, ...)den mit dem StringBuilder erzeugten Text einfügen - und dann ebenfalls mit File.WriteAllLines(...) wieder abspeichern.

PS: Warum liest du die Datei denn wieder neu ein (die Daten, d.h. _input, hat sich doch nicht verändert)?

Edit: Wenn es sich jedoch um eine Json-Datei handelt, dann solltest du auch einen Json-Parser verwenden.

Das mit dem String.Empty hört sich gut an. Das wäre dann nach GetLines denke ich mal ?

Ja das 2. einlesen ist totaler Quatsch, habe ich schon raus. Ist kein Json

Ich war so Stolz auf die Lösung, leider passte Sie nicht zum Problem 😃

Bin ja auch kein professioneller Programmierer 😃 Bei mir ist alles ein wenig Chaotisch. Manchmal muss ich andere Fragen was der Code eigentlich macht (Spaß)

Nein im Ernst, das ist für mich auch Neuland, muss mich da auch erst reinarbeiten.

Das Spiel ist wirklich ETS2 und die Originale Datei ist verschlüsselt.

In der SII_Utilities.Load_LAST_GAME_SII(); wird das letzte Savegame geladen, entschlüsselt und als normale Textdatei in TL_ETS_GAME_DRIVE_ENC_PATH gespeichert die dann eben ausgelesen werden kann.

Es geht im Moment weniger darum die Jobs auszulesen, eher einen eigenen Job einzufügen. Alles andere kommt später.

Die Job Daten, wenn man es als Key Value Pairs betrachtet, sind die Keys immer gleich nur die Values ändern sich eben in der StringBuilder Methode da wir ja eigene Daten einsetzen. Dazu muss in der JobListe der Firma diese Jobdaten die wir dort einfügen auch hinterlegt werden damit der Auftrag eben in der firma angezeigt wird.

Ich war so Stolz auf die Lösung, leider passte Sie nicht zum Problem 😃

Zitat von Thommy1972de2

Das mit dem String.Empty hört sich gut an. Das wäre dann nach GetLines denke ich mal ?

Wo du bisher auch das "Remove Range"hast (denn das entspricht dem ja).

Trotzdem wäre es m.E. besser einen Job-Parser (bzw. Serializer) zu haben, der die gesamten Daten als Objekte einliest und wieder abspeichert - und nicht nur nach einer bestimmten sucht und diese abändert (bzw. löscht und einen neuen Job einfügt). Aber dazu müßten wir mal eine gesamte Datei (bzw. einen relevanten Ausschnitt daraus) sehen - kannst es ja mal als Anhang anfügen.

Gibt es denn auch eine SII_Utilities.Write_LAST_GAME_SII(), denn das Spiel wird doch sicherlich die verschlüsselte Save-Datei erwarten?

PS: Deine Variablenschreibweise (z.B. alles Großbuchstaben für Methodenparameter und Eigenschaften) ist äußerst ungewöhnlich und der Code ist damit schlecht(er) zu lesen.

Ich stelle dir mal die 2 relevanten Dateien hier rein.
Wenn die Datei neu geladen wird, wird Sie vom Game wieder verschlüsselt, man muss sie dann beim Ändern nur jedes Mal Encrypten

Ich war so Stolz auf die Lösung, leider passte Sie nicht zum Problem 😃

Ich, sowie T-Virus, meinen die Save-Datei (Textdatei).

Edit:

Ich habe mir mal gerade die "SII_Utilties.cs" angeschaut. Es wird dort fast in jeder Methode diese Datei immer wieder geladen:

Load_LAST_GAME_SII();
var lines = File.ReadAllLines(MainWindow.Global.TL_ETS_GAME_DRIVE_ENC_PATH);

Da wäre es wirklich eleganter und performanter, dies nur einmalig zu tun und dann die Daten vorzuhalten, so daß die Methoden nur auf diesen Daten operieren. C# ist ja eine objektorientierte Sprache, daher sollte man möglichst OOP benutzen (und statische Klassen bzw. Methoden nur für Hilfsfunktionalitäten einsetzen).

Und wenn du dann diese Funktionalität in einer Klasse hast, so hast du dann schon einen guten Ansatz für die Datenverwaltung (inkl. Jobs).

Ah, Ok, dann auch noch die. 
Das ist die Encodierte Version, die andere würde ja nichts bringen. Musste Sie verpacken.

Ich war so Stolz auf die Lösung, leider passte Sie nicht zum Problem 😃

OK, exaktes JSON ist dies nicht, aber ähnlich dazu.

Im ersten Schritt könntest du ja einfach mal alle Blöcke in eine Liste einlesen, d.h. zuerst den Header überlesen und dann jeweils bis zur schließenden Klammer } lesen (am besten du erzeugst dir für die Daten eine Klasse mit den Eigenschaften für Name, "_nameless..." sowie dem Block-Text).

So kannst du dann einfacher auf den Daten operieren (z.B. auch Suchfunktionen nach dem Namen z.B. "job_info"schreiben).

Und wenn du dann die Daten spezieller auswerten möchtest, dann kannst du immer noch spezialisierte Klassen dafür (z.B. Job_Info) erzeugen.

PS: Die zuletzt angefügten "job_info"-Blöcke sind aber noch fehlerhaft (dort fehlt z.B. die öffnende Klammer {), oder?

Außerdem muß ganz unten noch die schließende Klammer für die 1.+2. Zeile "SiiNunit" + "{" stehen (bisher in Zeile 161774).

Edit:

Außerdem scheint es zu jedem job_info-Block auch einen driver_ai-Block zu geben, in der auf diesen mittels driver_job: verwiesen wird.

Dazu mußt du dann aber auch wohl jeweils einzigartige Ids ("_nameless...") erzeugen (nicht die player-Id verwenden)!

Das wäre eine Idee. Habe das leider Coronabedingt noch nicht richtig Testen können. Letzter Infekt war irgendwie leichter.

Bin seit Samstag grade auf dem "KeinBockfürgarnix" Trip. Wenn es mir wieder besser geht hänge ich mich wieder dran

Ich war so Stolz auf die Lösung, leider passte Sie nicht zum Problem 😃