Datei weiterlesen nach dem Ende

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von Haudruferzappeltnoch.

    Datei weiterlesen nach dem Ende

    Hallo,

    ich versuche aus einer Datei nur die neuen Einträge zu lesen. Dazu setze ich den Filestream des StreamReader zu Beginn ganz ans Ende,

    VB.NET-Quellcode

    1. If jumpToEnd Then
    2. Reader.BaseStream.Seek(0, SeekOrigin.End)
    3. Reader.DiscardBufferedData()
    4. End If

    beim Aufruf von StreamReader.ReadLine wird dann Nothing zurückgegeben
    Ich warte also darauf, dass es weitergeht.
    Wenn dies passiert, dann gibt StreamReader.ReadLine auch einen empty String zurück. Und zwar so oft wie Zeilen hinzugefügt wurde.
    Wie kriege ich denn den tatsächlichen Inhalt dieser Zeilen?

    Viele Grüße
    @Haudruferzappeltnoch Merke Dir beim Auslesen die Länge der Datei.
    Beim erneuten Öffnen machst Du ein Seek auf diese Position:

    VB.NET-Quellcode

    1. Reader.BaseStream.Seek(LAST_POSITION, SeekOrigin.Begin)
    Feddich.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich konnte dein besagtes Problem nicht reproduzieren. Gelesen hab ich die Zeilen so:

    C#-Quellcode

    1. string line;
    2. while ((line = sr.ReadLine()) != null)
    3. Console.WriteLine($" > {line}");

    TestProgram

    C#-Quellcode

    1. static void Main(string[] args)
    2. {
    3. const string FILE_NAME = "srTest.txt";
    4. FileStream fs = new FileStream(FILE_NAME, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    5. StreamReader sr = new StreamReader(fs);
    6. if (!sr.BaseStream.CanSeek)
    7. {
    8. Console.WriteLine("StreamReader can't seek...");
    9. Console.ReadLine();
    10. return;
    11. }
    12. sr.BaseStream.Seek(0, SeekOrigin.End);
    13. FileSystemWatcher watcher = new FileSystemWatcher(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
    14. {
    15. NotifyFilter = NotifyFilters.LastWrite
    16. };
    17. watcher.Changed += (s, e) =>
    18. {
    19. if (e.ChangeType != WatcherChangeTypes.Changed || e.Name != FILE_NAME)
    20. return;
    21. string line;
    22. while ((line = sr.ReadLine()) != null)
    23. Console.WriteLine($" > {line}");
    24. };
    25. watcher.EnableRaisingEvents = true;
    26. Console.ReadKey();
    27. sr.Dispose();
    28. fs.Dispose();
    29. watcher.Dispose();
    30. }

    Bilder
    • sr-example.png

      5,12 kB, 209×128, 187 mal angesehen
    @RodFromGermany
    Das Verhalten hat sich geändert, allerdings weiterhin nicht zum Guten.

    @Fakiz
    Hm, ja ich mache es eigentlich genauso, nur nicht mit FileSystemWatcher, sonder auf gut Glück, zeitkritisch ist das nicht.

    Hier der volle Testprozess:
    Button1 -> EOF erscheint
    Datei öffnen -> abc, neue Zeile, cde, neue Zeile, efg, neue Zeile -> speichern
    Button1 -> a erscheint dann dreimal String.Empty, danach EOF

    Also ich bekomme jetzt ein Zeichen.

    VB.NET-Quellcode

    1. Friend Class Form1
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. Using FA As New FileAdapter("pfad", True)
    4. RichTextBox1.AppendText(If(FA.Read, "EOF"))
    5. RichTextBox1.AppendText(vbCrLf)
    6. End Using
    7. End Sub
    8. End Class
    9. Friend Class FileAdapter
    10. Implements IDisposable
    11. Private strm As FileStream
    12. Private Reader As StreamReader
    13. Private start As Long
    14. Private disposedValue As Boolean
    15. Friend Property File As FileInfo
    16. Sub New(path As String, jumpToEnd As Boolean)
    17. _File = New FileInfo(path)
    18. strm = New FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
    19. Reader = New StreamReader(strm)
    20. If jumpToEnd Then
    21. start = Reader.BaseStream.Length
    22. Reader.BaseStream.Seek(start, SeekOrigin.Begin)
    23. End If
    24. End Sub
    25. Friend Function Read() As String
    26. Return Reader.ReadLine
    27. End Function
    28. Protected Overridable Sub Dispose(disposing As Boolean)
    29. If Not disposedValue Then
    30. If disposing Then
    31. Reader.Close()
    32. strm.Dispose()
    33. Reader.Dispose()
    34. End If
    35. disposedValue = True
    36. End If
    37. End Sub
    38. Public Sub Dispose() Implements IDisposable.Dispose
    39. Dispose(disposing:=True)
    40. GC.SuppressFinalize(Me)
    41. End Sub
    42. End Class


    New FileAdapater("pfad", False) funktioniert außerdem wie erwartet. Also das Verschieben der BaseStream Position funktioniert, aber er kommt nicht damit zurecht, dass da jetzt neue Information reingekommen ist.

    Was auch funktioniert ist, Zeile 24 und 25 ersetzen durch Reader.ReadToEnd. Das müllt mir aber den Arbeitsspeicher mit einigen hundert MB zu.

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Wenn da neue Zeilen reinkommen ist die erste Zeile die ab der letzten Position gelesen wird CrLf das wird als Empty.String ausgegeben. Wenn du das so machen willst müßtest du erst Reader.BaseStream.Position += 2 und dann Return Reader.ReadLine. Das wird aber problematisch wenn kein Zeilenumbruch stattgefunden hat.
    Also das letzte Zeichen in der Datei ist immer ein Zeilenumbruch.
    Ich habe abc eingefügt und ausgegeben wird a
    Weitere Zeilen entsprechend gar nix mehr.

    Hab es mit += 2 ausprobiert, kriege nun überhaupt nichts mehr gelesen.
    Wirkt also so als wäre nach dem a irgendwas kaputt.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Im Normalfall sollten Zeilen immer mit einem NullByte enden und bei einem Zeilenumbruch beginnt die Zeile dann mit \r, \n zumindest bei Windows.

    Erster Buchstabe:


    Zweiter Buchstabe:


    Das Bild zeigt einen Ausschnitt aus der StreamReader.ReadLine() Methode. Hier kann mann sehen das Zeilenumbrüche mit einem leeren String quitiert werden. Wie gesagt du solltest ReadLine() in einer While-Schleife aufrufen und solange lesen bis keine Daten mehr gelesen werden können.

    PS: Habe ein H geschrieben gespeichert dann ein A geschrieben und gespeichert.
    Ja das ist halt eine 7 stellige Zeilenanzahl, wenn man die Möglichkeit hat die zu überspringen ist das denke ich sehr sinnvoll.

    Wie eine Zeile endet weiß ich nicht unbedingt. Ich sagte ja die Datei endet zu jedem Zeitpunkt in einem Zeilenumbruch. Also mit einer leeren Zeile.
    Da springe ich hin. Dann schreibe ich etwas in die Zeile und füge weitere Zeilen hinzu. Und dass ich den ersten neuen Buchstaben dann lesen, kann passt da auch nicht zu.
    @Haudruferzappeltnoch Mach doch mal ein kleines Demo-Projektund lade es hoch.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ja das ist halt eine 7 stellige Zeilenanzahl, wenn man die Möglichkeit hat die zu überspringen ist das denke ich sehr sinnvoll.

    Das kannst du ja. Spring zum ende der Datei und wenn du den neuen inhalt lesen willst liest du diesen in einer Schleife. Das sollten dann genau 2 Zeilen sein eine leere Zeile und der neue Inhalt.
    Alternativ kannst du auch mal ganz einfach:

    VB.NET-Quellcode

    1. Friend Function Read() As String
    2. Return Reader.ReadLine & Reader.ReadLine;
    3. End Function

    ausprobieren.

    PS: Das gilt natürlich nur wenn auch nur eine neue Zeile geschrieben wurde.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Fakiz“ ()

    Ich weiß nicht ob ihr mal in Code in Post 4 geguckt habt, zum Einen @RodFromGermany das ist der Vollständige Code.
    Zum Anderen @Fakiz calle ich das Readline in einem Button, mehrfach aufrufen tue ich also bereits, mit geschildertem Ausgang, das zu Concatenieren kann das Ergebnis nicht ändern.

    Hier Beispieltext für eine Beispieldatei:
    "Ich bin die erste Zeile
    "
    Anwendung starten

    Neuer Text
    "Ich bin die erste Zeile
    abc
    def
    ghi
    "
    Resultat nach 4 mal Button drücken: a, empty, empty, empty, EOF

    Haudruferzappeltnoch schrieb:

    das ist der Vollständige Code.
    Bei mir wird bei jedem Mal Drücken des Buttons eine "EOF"-Zeile in der RTB angehängt.
    Sonst passieret bei mir nix. :S
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hängst du denn zwischen den Button Klicks Inhalt an die Datei an die du liest?
    Falls nicht ist das auch so gewünscht mit dem EOF^^

    Wenn du deine Form1.vb Datei als Test liest, dann könntest du das vermutlich direkt im Code durch Einfügen von Kommentaren am Ende erreichen, wobei ich weiß nicht ob man bei laufendem Code speichern kann, vermutlich nicht.


    Tschuldige, ich habe unbedacht den FileAdapter in einen Using Block gepackt.
    Der muss natürlich eine langlebige externe Variable bleiben.

    Korrektur

    VB.NET-Quellcode

    1. Private FA As New FileAdapter("pfad", True)
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. RichTextBox1.AppendText(If(FA.Read, "EOF"))
    4. RichTextBox1.AppendText(vbCrLf)
    5. End Sub

    Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Haudruferzappeltnoch schrieb:

    Tschuldige, ich habe unbedacht den FileAdapter in einen Using Block gepackt.
    Der muss natürlich eine langlebige externe Variable bleiben.

    Das ist ja komisch. Dann konntest du Reader.Readline aufrufen, obwohl der Reader disposed war?
    Also das wäre nicht gut - da sollte eine InvalidOperationException kommen.
    Hm, nene, in dem kaputten Szenario wurde ReadLine dennoch nur aufgerufen wenn auch der Reader da war. Der Punkt is, er wurde immer wieder neu erzeugt, was auch bedeutet, dass er immer nach jeder Änderung startet, was bedeutet, dass er immer beim End Of File steht und Nothing zurückgibt.

    Habe mittlerweile auch soweit erfasst, dass DiscardBufferedData keinen Unterschied macht, obwohl man das wohl aufrufen soll wenn man die Position des BaseStreams ändert.
    Ich habe das Gefühl BaseStream ändern und FileAccess.ReadWrite verträgt sich nicht.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()