Laden...

Vorletzte Zeile in einer CSV-Datei löschen

Erstellt von Demokrit vor 5 Jahren Letzter Beitrag vor 5 Jahren 2.502 Views
D
Demokrit Themenstarter:in
10 Beiträge seit 2018
vor 5 Jahren
Vorletzte Zeile in einer CSV-Datei löschen

Hallo,

Ich habe ein Program mit dem ich Daten von einer WPF-App in einer .csv Datei einspeichere.
(Datum und dann ein paar int Werte).
Pro Tag sollte aber nur ein "Datensatz" in der Datei stehen das heißt wenn zb heute am 28.11.2018 der speichern button gedrückt wird, soll folgender datensatz hinzugefügt werden:


28:11:2018 00:00:00; 1;1;1;1;1

Das funktioniert perfekt.

Wenn aber heute schon ein Datensatz in der Datei abgespeichert wurde, sollen die Werte mit dem gleichen Datum addiert werden sodass nur noch eine einzelne Zeile da steht.

Das klappt auch sehr gut das einzige Problem ist das wenn ich zb:

28:11:2018 00:00:00; 1;1;1;1;1
28:11:2018 00:00:00; 1;1;1;1;1

Habe sollte

28:11:2018 00:00:00; 2;2;2;2;2

rauskommen.

Leider kommt logischerweise bei mir:

28:11:2018 00:00:00; 1;1;1;1;1
28:11:2018 00:00:00; 2;2;2;2;2

raus da ich den vorherigen Datensatz noch nicht gelöscht habe.

Da ich viel gegoogelt habe, weiß ich das es 2 Ansätze für die Lösung gibt:

  1. Jedes mal die .csv komplett neu schreiben statt zu "appenden" um dann den einen Datensatz auszulassen
  2. Jedes mal die 2 letzte Zeile löschen, da diese ja in dem hinzugefügten Datensatz steht

Problem:

  1. Ich verstehe das Prinzip dahinter und es scheint mir die "sauberere" Lösung zu sein, aber ich verstehe nicht wie ich in dem Fall immer die 2t letzte Zeile (Die immer einen Unterschiedlichen Wert hat) "rausfiltern" sollte.
  2. Ich verstehe bzw weiß nicht ob das möglich ist

Ich sitze jetzt schon seit einer Woche an dem Problem (bin neu in c#) und würde gerne mal weiterkommen.
Wenn es geht würde ich eine Lösung ohne Linq bevorzugen aber zur Not nehme ich das dann ohne es zu verstehen und mache einfach weiter

Code:

    class AddVariables
    {
        public static List<string[]> ReadCsvMethod()
        {
            List<string[]> oReadCsvList = new List<string[]>();
            string filePath = (@"C: \Users\---\Desktop\---l\---\SaveDatei.csv");
            using (StreamReader sr = new StreamReader(filePath))
            {
                while (!sr.EndOfStream)
                {
                    string[] Line = sr.ReadLine().Split(';');
                    oReadCsvList.Add(Line);
                }
            }
            return oReadCsvList;
        }
        public static Dictionary<DateTime, int[]> CompareDatimeTimeMethod(List<string[]> oReadCsvList, Dictionary<DateTime, int[]> oNewDataDictionary) 
        {
            Dictionary<DateTime, int[]> oAddedOldAndNewDataDictionary = new Dictionary<DateTime, int[]>(); 
            DateTime OldDateTime;                       

            for (int i = 0; i < oReadCsvList.Count; i++)
            {
                if (DateTime.TryParse(oReadCsvList[i][0], out OldDateTime))
                {
                    int[] iValuesToAdd = ConvertArrayMethod(oReadCsvList[i]);

                    if (OldDateTime.Date == DateTime.Now.Date)
                    {
                        for (int j = 0; j < iValuesToAdd.Length; j++)
                        {
                            oNewDataDictionary[OldDateTime][j] += iValuesToAdd[j];
                        }
                    }
                }
            }
            return oNewDataDictionary;
        }
        private static int[] ConvertArrayMethod(string[] oZeileStringArray)                                         
        {
            List<int> oConvertedArrayValuesList = new List<int>();
            for (int i = 1; i < oZeileStringArray.Length; i++)                                                
            {
                int iValue;
                int.TryParse(oZeileStringArray[i], out iValue);
                oConvertedArrayValuesList.Add(iValue);
            }
            return oConvertedArrayValuesList.ToArray();                                                              
        }

Button zum speichern (In einer anderen Klasse):

 
private void btnSpeichern(object sender, RoutedEventArgs e)
       {
            Dictionary<DateTime, int[]> oNewDataDictionary = new Dictionary<DateTime, int[]>();  
                                
            int[] iNewDataArray = new int[] { iVariable1, iVariable2, iVariable3, iVariable4, iVariable5};
            List<string[]> oReadCsvList = AddVariables.ReadCsvMethod();

            oNewDataDictionary.Add(DateTime.Now.Date, iNewDataArray);

            using (FileStream fileStream = new FileStream(@"C: \Users\---\Desktop\---l\---\SaveDatei.csv", FileMode.Append, FileAccess.Write))
            using (StreamWriter streamWriter = new StreamWriter(fileStream))
            {
                foreach (KeyValuePair<DateTime, int[]> kvp in AddVariables.CompareDatimeTimeMethod(oReadCsvList, oNewDataDictionary))
                {
                    streamWriter.WriteLine("{0}; {1}", kvp.Key, string.Join(";", kvp.Value));
                }
            }
        }
4.938 Beiträge seit 2008
vor 5 Jahren

Mittels FileStream.Position kannst du auch gezielt zu einer Position in der Datei springen und dann anschließend die Werte überschreiben:


fileStream.Position = FileLength - csv[csv.Length-1].Length - Environment.NewLine.Length;

Oder besser noch mit FileStream.Seek vom Ende der Datei rückwärts springen.

D
Demokrit Themenstarter:in
10 Beiträge seit 2018
vor 5 Jahren

Mittels
>
kannst du auch gezielt zu einer Position in der Datei springen und dann anschließend die Werte überschreiben:

  
fileStream.Position = FileLength - csv[csv.Length-1].Length - Environment.NewLine.Length;  
  

Oder besser noch mit
>
vom Ende der Datei rückwärts springen.

Okay das macht Sinn ich probiere gerade mit einem neuen Projekt und csv datei die "syntax" falls man das so sagt zu verstehen.

Ich versuche das Alphabet rückwärts zu lesen und den zweitletzten Buchstaben auszugeben.

Es hängt aber gerade an der ausgabe vielleicht kannst du mir kurz helfen:

using System;
using System.IO;

public class FSSeek
{
    public static void Main()
    {
        // Alphabet.csv beeinhaltet "abcdefghijklmnopqrstuvwxyz"
        using (FileStream fs = new FileStream(@"C:\Users\---\Desktop\---\Csvsave\Alphabet.csv", FileMode.Open, FileAccess.Read))
        {
            fs.Seek(fs.Length -2, SeekOrigin.Begin);
            long test = fs.Length - 2;
            {
                Console.Write(test);
            }

            Console.ReadLine();
        }
    }
}
4.938 Beiträge seit 2008
vor 5 Jahren

Wenn du aus der Datei lesen möchtest, dann mußt du selbstverständlich eine der Lese-Methoden aufrufen, z.B. FileStream.ReadByte.
Mit


long test = fs.Length - 2;
{
    Console.Write(test);
}

gibst du ja nur die Position als Zahl aus (d.h. bei 26 Buchstaben in der Datei sollte 24 rauskommen).

D
Demokrit Themenstarter:in
10 Beiträge seit 2018
vor 5 Jahren

Wenn du aus der Datei lesen möchtest, dann mußt du selbstverständlich eine der Lese-Methoden aufrufen, z.B.
>
.

Genau bei mir kam vorhin 23 raus (Habe 25 Buchstaben).
Mein Problem ist ja das ich den Wert der Position von 23 (also y) nicht ausgeben kann bzw nicht weiß wie ich das machen würde.

Eigentlich hätte ich es mit foreach probiert aber das hat auch nicht wirklich funktioniert

4.938 Beiträge seit 2008
vor 5 Jahren

Was paßt denn nicht, wenn du fileStream.ReadByte() aufrufst?
Du mußt es nur noch für die Ausgabe in einen char casten:


int data = fileStream.ReadByte();

Console.Write((char)data);

D
Demokrit Themenstarter:in
10 Beiträge seit 2018
vor 5 Jahren

Vielen Dank hat geklappt.
Kannst du eventuell noch erklären warum man das (char) an der Stelle braucht ?

4.938 Beiträge seit 2008
vor 5 Jahren

Da wegen dem Datentyp int (als Rückgabewert der Methode) ja sonst das gelesene Byte als (ASCII bzw. Unicode)-Zahl ausgegeben würde, anstatt als Zeichen (char).

Kannst es ja mal ohne den 'cast' probieren, dann sollte statt 'y' 121 ausgegeben werden, s.a. ASCII.

PS: Und Console.Write hat entsprechend der Datentypen unterschiedliche Überladungen, welche jeweils die Daten anders interpretieren und ausgeben. Generell wird dabei einfach deren ToString()-Wert ausgegeben.

D
152 Beiträge seit 2013
vor 5 Jahren

Da wegen dem Datentyp int (als Rückgabewert der Methode) ja sonst das gelesene Byte als (ASCII bzw. Unicode)-Zahl ausgegeben würde, anstatt als Zeichen (char).

Der Rückgabetyp int hat primär nichts ASCII bzw. Unicode zu tun, denn -1 kennzeichnet das Ende eines Streams und dieser passt halt nicht in ein byte.

Siehe auch FileStream.ReadByte Method.

4.938 Beiträge seit 2008
vor 5 Jahren

Ja, genau wegen der -1 (für den Fehlerfall) wird eben nicht ein byte zurückgegeben, sondern eben der größere Datentyp int. Und wenn nun ein Byte gelesen wurde (z.B. y), wird eben bei der Ausgabe dieser Zahl dessen ASCII-Code ausgegeben.

Es ist nur ein wenig eigenartig, daß eine Methode namens ReadByte ein int anstatt byte zurückgibt - und daher habe ich erklärt, was diese Zahl bedeutet:*-1: Fehler *0 - 255: ASCII-Code des gelesenen Zeichens

Sonst würde ja auch der 'cast' nach char nicht korrekt funktionieren (bei -1 kommt natürlich kein sinnvolles Zeichen raus, aber diesen Fall habe ich auch gar nicht berücksichtigt).

3.003 Beiträge seit 2006
vor 5 Jahren

Hinweis: ReadByte auf char zu casten funktioniert nur für 255 Zeichen von etwas über eine Million in Unicode darstellbaren. Zugegeben, für die mit Abstand häufigsten, dennoch ist diese Vorgehensweise beim Arbeiten mit Text etwas, was einem potenziell das Genick bricht.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

4.938 Beiträge seit 2008
vor 5 Jahren

Es ist schon klar, daß man Unicode-Texte mittels StreamReader lesen sollte.

@LaTino: ReadByte liefert ja nur Werte im Bereich 0-255 (oder eben zusätzlich noch -1), aber niemals größere Werte (denn ein Byte sind nunmal nur 8 Bit 😉.

Es ging ja nur um die Frage:

Kannst du eventuell noch erklären warum man das (char) an der Stelle braucht ?

Und die habe ich beantwortet.

@Demokrit: Hast du es denn jetzt verstanden?

3.003 Beiträge seit 2006
vor 5 Jahren

Es ist schon klar, daß man Unicode-Texte mittels
>
lesen sollte.

Dir, ja 😉. Im Thread steht aber sinngemäß "man kann ein Zeichen per (char)ReadByte() auslesen." (Was in diesem Fall geht, ja.)
Deshalb der Hinweis, dass man da ganz schnell in Teufels Küche kommen kann.

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

D
Demokrit Themenstarter:in
10 Beiträge seit 2018
vor 5 Jahren

Ja vielen dank für die Antworten 👍

Hinweis von Abt vor 5 Jahren

Bitte keine Full Quotes: [Hinweis] Wie poste ich richtig?