Fehler abfangen, wenn Streamreader geöffnete csv öffnen soll

  • VB.NET
  • .NET (FX) 4.0

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von DerSmurf.

    Fehler abfangen, wenn Streamreader geöffnete csv öffnen soll

    Huhu
    Mittels Open File Dialog öffne ich eine csv Datei für den Import in ein DataSet.
    Das funktioniert alles, und die eventuellen Fehler habe ich soweit abgefangen.

    Einen Fehler jedoch bekomme ich nicht abgefangen.
    Wenn der Streamreader eine geöffnete Datei öffnen soll, schmeißt mir mein Programm ja eine Exception.
    Kann ich diesen Fehler irgendwie eleganter Abfangen, als in einem Try ... Catch Block?
    Nach einer kurzen Suche gebe ich dir folgendes Gedankenspiel:

    Nehmen wir an es gäbe einen Weg um zu testen, ob eine Datei geöffnet ist ohne Exceptions. Was ist nun, wenn zwischen der eigentlichen Prüfung, und dem tatsächlichen Öffnen sich doch noch ein anderes Programm dazwischenmogelt und die Datei bereits geöffnet hat?

    Ich denke du verstehst was ich meine.

    EaranMaleasi schrieb:

    Ich denke du verstehst was ich meine.

    Ich kann den Fehler nie mit 100%iger Sicherheit abfangen.
    Wenn ich aber vor Übergabe der Datei an den Stream Reader einmal test ob sie geöffnet ist, fange ich zumindest den Fehler ab der Auftritt, wenn ich manuell die Datei öffne und vor dem Import vergesse sie zu schließen.
    Das kommt in der Praxis ab und an vor.

    Allerdings verstehe ich

    EaranMaleasi schrieb:

    Nehmen wir an es gäbe einen Weg um zu testen, ob eine Datei geöffnet ist ohne Exceptions.

    so, dass eine Prüfung auf "offene Datei" nicht möglich ist, ich also Try ... Catch verwenden muss?
    @DerSmurf Aus diesem Grund verwendet man ja das Using-Konstrukt, dass immer hinter einem Aufräumt.

    DerSmurf schrieb:

    so, dass eine Prüfung auf "offene Datei" nicht möglich ist, ich also Try ... Catch verwenden muss?
    Exakt.

    @a.b_om Von welcher Klasse redest du? Weder File, FileInfo, FileStream noch StreamReader haben eine .IsOpen() Funktion

    Edit:
    @a.b_om ich bin soeben von .NET FW 2.0 bis .NET Core 3.1 durchgegangen, und bei keinem der besagten Streams findet sich eine .IsOpen() Funktion. Ja, du hast dich vollkommen getäuscht.

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

    @EaranMaleasi
    Stream Writer. Ich habe das mal gebraucht und habe es so abgefragt. Ich dachte Stream Reader oder anderes hätte das auch.
    Oder täusche ich mich vollkommen.

    Jedenfalls, hätte ich eine solche Abfrage in einer externen Methode gemacht. Es hat immernoch einen Try ... Catch, aber es hat dieses mal einen 100% wahrscheinlichkeit, dass es klappt.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub IsFileOpen(ByVal file As FileInfo)
    2. Dim stream As FileStream = Nothing
    3. Try
    4. stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None)
    5. stream.Close()
    6. Catch ex As Exception
    7. If TypeOf ex Is IOException AndAlso IsFileLocked(ex) Then
    8. ' do something here, either close the file if you have a handle, show a msgbox, retry or as a last resort terminate the process - which could cause corruption and lose data
    9. End If
    10. End Try
    11. End Sub

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „a.b_om“ ()

    Ich glaube da kommst du an Try...Catch nicht vorbei. Ich habe dafür eine Function gebastelt womit ich die Datei auf Zugriffrechte prüfe.

    Code

    VB.NET-Quellcode

    1. Private Function IsFileInUse(FullFilePath As String) As Boolean
    2. If IO.File.Exists(FullFilePath) Then
    3. Dim NewFile As Integer = FreeFile()
    4. Try
    5. FileOpen(NewFile, FullFilePath, OpenMode.Binary, OpenAccess.ReadWrite, OpenShare.LockReadWrite)
    6. Catch
    7. Return True
    8. Finally
    9. FileClose(NewFile)
    10. End Try
    11. Return False
    12. Else
    13. Return False
    14. End If
    15. End Function


    Ein Computer wird das tun, was du programmierst - nicht das, was du willst.

    Yanbel schrieb:

    Visual Basic-Quellcode

    1. Dim NewFile As Integer = FreeFile()
    What :?:
    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, der Code ist schon ein wenig älter... aber er funktioniert. ;)

    Ein aktuelles Beispiel würde vermutlich so aussehen:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Function IsFileInUse(FullFilePath As String) As Boolean
    2. If IO.File.Exists(FullFilePath) Then
    3. Try
    4. Using xStream As New IO.StreamReader(FullFilePath)
    5. xStream.Close()
    6. End Using
    7. Return False
    8. Catch ex As IO.IOException
    9. Return True
    10. Catch ex As Exception
    11. Return False
    12. End Try
    13. Else
    14. Return False
    15. End If
    16. End Function
    17. Private Sub btnRead_Click(sender As Object, e As EventArgs) Handles btnRead.Click
    18. If Not IsFileInUse("C:\Temp\Test.xml") Then
    19. 'Methode zum Lesen
    20. Else
    21. MsgBox("Die Datei wird aktuell von einem anderen Prozess verwendet", MsgBoxStyle.Critical, "Datei in Bearbeitung")
    22. End If
    23. End Sub


    Ein Computer wird das tun, was du programmierst - nicht das, was du willst.
    @Yanbel Sieht doch schon besser aus.
    Ich wäre geneigt, nur Exception und keine weitere abzufangen, da der Code absolut klar ist.
    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!
    Vollzitat entfernt ~VaporiZed

    Ja, ist halt jetzt speziell auf seine Anforderung zugeschnitten, da er ja wissen will, wann die Datei in Bearbeitung ist. Daher die Einschränkung auf die IO.IOException. Kann man natürlich auch grundsätzlich schreiben, aber da wir bisher kein bisschen Code von ihm gesehen haben, weiß ich halt nicht in wie weit er bereits Errorhandling implementiert hat.


    Ein Computer wird das tun, was du programmierst - nicht das, was du willst.

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

    Sorry, ich dachte nicht, dass wir Code brauchen.
    Aber hier gerne mehr Infos.

    Es gibt 5 Textboxen mit zugehörigen Buttons, in denen eben bis zu 5 Importdateien ausgewählt werden können.
    Die Textboxen sind für Benutzereingaben gesperrt und die Pfade werden in Modulweit gültigen String Variablen gespeichert.​Private csvFile1 As String
    Hier dann der Aufruf der Importfunktion:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. 'ButtonKlick zum starten des Imports
    2. Private Sub BTNImport_Click(sender As Object, e As EventArgs) Handles BTNImport.Click
    3. Dim Delete As Boolean
    4. Dim csvError As String
    5. Delete = CBDelFile.Checked
    6. If csvFile1 <> "" Then
    7. If StartImport(csvFile1, csvError) Then
    8. If Delete Then My.Computer.FileSystem.DeleteFile(csvFile1)
    9. csvFile1 = ""
    10. TBimport1.Text = ""
    11. LBLImport1.Text = "Import erfolgreich"
    12. Else
    13. LBLImport1.Text = csvError
    14. End If
    15. End If
    16. If csvFile2 <> "" Then
    17. If StartImport(csvFile2, csvError) Then
    18. If Delete Then My.Computer.FileSystem.DeleteFile(csvFile2)
    19. csvFile2 = ""
    20. TBImport2.Text = ""
    21. LBLImport2.Text = "Import erfolgreich"
    22. Else
    23. LBLImport2.Text = csvError
    24. End If
    25. End If
    26. If csvFile3 <> "" Then
    27. If StartImport(csvFile3, csvError) Then
    28. If Delete Then My.Computer.FileSystem.DeleteFile(csvFile3)
    29. csvFile3 = ""
    30. TBImport3.Text = ""
    31. LBLImport3.Text = "Import erfolgreich"
    32. Else
    33. LBLImport3.Text = csvError
    34. End If
    35. End If
    36. If csvFile4 <> "" Then
    37. If StartImport(csvFile4, csvError) Then
    38. If Delete Then My.Computer.FileSystem.DeleteFile(csvFile4)
    39. csvFile4 = ""
    40. TBImport4.Text = ""
    41. LBLImport4.Text = "Import erfolgreich"
    42. Else
    43. LBLImport4.Text = csvError
    44. End If
    45. End If
    46. If csvFile5 <> "" Then
    47. If StartImport(csvFile5, csvError) Then
    48. If Delete Then My.Computer.FileSystem.DeleteFile(csvFile5)
    49. csvFile5 = ""
    50. TBImport5.Text = ""
    51. LBLImport5.Text = "Import erfolgreich"
    52. Else
    53. LBLImport5.Text = csvError
    54. End If
    55. End If
    56. End Sub​


    und die eigentliche Importfunktion:

    VB.NET-Quellcode

    1. 'Funktion zum importieren der csv Dateien
    2. Private Function StartImport(csvFile As String, ByRef csvError As String) As Boolean
    3. 'Prüfen ob Datei existiert
    4. If Not System.IO.File.Exists(csvFile) Then
    5. csvError = "Datei nicht gefunden"
    6. Return False
    7. End If
    8. Dim TheReader As New StreamReader(csvFile, Encoding.Default)
    9. Dim sline As String = ""
    10. Dim ShippingDate As Date
    11. Do
    12. 'Splitten der einzelnen csv Zeilen
    13. sline = TheReader.ReadLine
    14. If sline Is Nothing Then Exit Do
    15. Dim words() As String = sline.Split(";"c)
    16. 'überspringen der Überschriften
    17. If words(0) = "PARCELNO" Then Continue Do
    18. 'abbrechen, wenn zu wenig Einträge vorhanden sind
    19. If words.Length < 125 Then
    20. csvError = "Falsche Datei ausgewählt - zu wenig Einträge"
    21. Return False
    22. End If
    23. 'neue Reihe im DataTable anlegen
    24. Dim NewParcel As DSParcels.DTParcelRow
    25. NewParcel = DSParcels.DTParcel.NewDTParcelRow()
    26. 'Daten in die DataTable schreiben
    27. With NewParcel
    28. .ShippingNumber = words(0)
    29. .Protocol = words(3)
    30. .OrderID = words(4)
    31. .ebayName = words(5)
    32. .ArticleNumber = words(6)
    33. .Name1 = words(8)
    34. .Name2 = words(9)
    35. .Street = words(10)
    36. .Number = words(11)
    37. .ZipCode = words(15)
    38. .Location = words(16)
    39. If Date.TryParse(words(125), ShippingDate) Then .ShippingDate = ShippingDate
    40. End With
    41. DSParcels.DTParcel.Rows.Add(NewParcel)
    42. Loop
    43. TheReader.Close()
    44. Return True
    45. End Function


    Bisher prüfe ich ob die Datei wirklich existiert und ob die Spaltenmenge passt.
    Meine Importdateien haben immer exakt den gleichen Aufbau.

    DerSmurf schrieb:

    Sorry, ich dachte nicht, dass wir Code brauchen.
    Mach einen Projektrumpf aus Deinem Projekt, der Deinen Effekt reproduziert.
    Bereinige (ohne bin- und obj-Verzeichnisse) und zippe das Projekt und hänge es an Deinen Post.
    Beschfreibe, was wir tun müssen, um den Effekt bei uns zu reproduzieren.
    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!
    Na aber, sind wir nicht schon fertig mit dem Thema?
    Meine Eingangsfrage war, ob ich den Fehler, den mir der Streamreader schmeißt, wenn er eine geöffnete csv öffnen soll, anders als mit Try ... Catch abfangen kann.
    Die Antwort habe ich bekommen und @Yanbel hat mir sogar Code gepostet, den ich fast 1 zu 1 verwenden kann.

    Edit: also ich habe natürlich kein Problem damit meine Solution hochzuladen (auch wenn das so wirken mag) -
    nur macht das natürlich keinen Sinn, wenn keine Fragen mehr offen sind - weder für euch (Helfer), noch für mich
    Dast ist ja kurios.
    Ich haben ur Excel ausprobiert, da es als Standrt csv App hinterlegt ist.
    Soll der Streamreader eine in Excel geöffnete csv Datei öffnen, quittiert er seinen Dienst mit dem Fehler:
    Exception: Der Prozess kann nicht auf die Datei "C:\Users\flori\Desktop\Neues Textdokument.csv" zugreifen, da sie von einem anderen Prozess verwendet wird.
    Ist die Datei in Wordpad oder im Editor geöffnet läufts ohne murren.

    Aber sollte ja wurscht sein, warum mein Programm keinen Zugriff auf die Datei hat - Hauptsache ich bekomme diesen Fehler (für eventuelle Fälle) abgefangen.
    Ja, aber das mit Excel und den meisten anderen Anwendungen kannst du umgehen, indem du einen Filestream mit den richtigen Parametern öffnest und darauf den StreamReader legst. Nur ganz selten blockieren die Anwendungen eine Datei so, dass man diese nicht lesen kann.

    VB.NET-Quellcode

    1. Using TheStream As New FileStream(csvFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
    2. Using TheReader As New StreamReader(TheStream, Encoding.Default)
    3. End Using
    4. End Using
    Ah perfekt.
    Das werde ich heute Abend testen und berichten.
    Vielen Dank erstmal :o)
    Dann komm ich ja scheinbar doch um try catch herum

    @Bluespide du hattest vollkommen recht. Vielen Dank!
    Durch deinen StreamReader Aufruf funktioniert der Import mit in Excel geöffneter csv ansandslos!

    Aber mir ist ein anderes Problemchen aufgefallen. Mein VS meldet eine Warnung die ich nicht nachvollziehen kann.
    Also schon verstehe was sie bedeutet - aber für mich ist, was VS meldet, nicht der Fall.
    Schweregrad Code Beschreibung Projekt Datei Zeile Unterdrückungszustand Warnung BC42030 Die csvError-Variable wird als Verweis übergeben, bevor ihr ein Wert zugewiesen wird. Zur Laufzeit kann eine Nullverweisausnahme auftreten.
    Für mich hat diese Variable immer einen Wert - warum die Warnung ausgesprochen wird, kann ich mir nicht erklären.
    Die Stelle der Warnung habe ich im Code gekennzeichnet.
    Die Variablen csvFile1 bis 5 sind modulweit gültig und werden mit durch Button gestartete Subs per OpenFileDialog ausgewählt.

    VB.NET-Quellcode

    1. 'ButtonKlick zum starten des Imports
    2. Private Sub BTNImport_Click(sender As Object, e As EventArgs) Handles BTNImport.Click
    3. Dim Delete As Boolean
    4. Dim csvError As String
    5. Delete = CBDelFile.Checked
    6. If csvFile1 <> "" Then
    7. If StartImport(csvFile1, csvError) Then ''''''HIER
    8. If Delete Then My.Computer.FileSystem.DeleteFile(csvFile1)
    9. csvFile1 = ""
    10. TBimport1.Text = ""
    11. LBLImport1.Text = "Import erfolgreich"
    12. Else
    13. LBLImport1.Text = csvError
    14. End If
    15. End If
    16. If csvFile2 <> "" Then
    17. If StartImport(csvFile2, csvError) Then
    18. If Delete Then My.Computer.FileSystem.DeleteFile(csvFile2)
    19. csvFile2 = ""
    20. TBImport2.Text = ""
    21. LBLImport2.Text = "Import erfolgreich"
    22. Else
    23. LBLImport2.Text = csvError
    24. End If
    25. End If
    26. If csvFile3 <> "" Then
    27. If StartImport(csvFile3, csvError) Then
    28. If Delete Then My.Computer.FileSystem.DeleteFile(csvFile3)
    29. csvFile3 = ""
    30. TBImport3.Text = ""
    31. LBLImport3.Text = "Import erfolgreich"
    32. Else
    33. LBLImport3.Text = csvError
    34. End If
    35. End If
    36. If csvFile4 <> "" Then
    37. If StartImport(csvFile4, csvError) Then
    38. If Delete Then My.Computer.FileSystem.DeleteFile(csvFile4)
    39. csvFile4 = ""
    40. TBImport4.Text = ""
    41. LBLImport4.Text = "Import erfolgreich"
    42. Else
    43. LBLImport4.Text = csvError
    44. End If
    45. End If
    46. If csvFile5 <> "" Then
    47. If StartImport(csvFile5, csvError) Then
    48. If Delete Then My.Computer.FileSystem.DeleteFile(csvFile5)
    49. csvFile5 = ""
    50. TBImport5.Text = ""
    51. LBLImport5.Text = "Import erfolgreich"
    52. Else
    53. LBLImport5.Text = csvError
    54. End If
    55. End If
    56. End Sub

    VB.NET-Quellcode

    1. 'Funktion zum importieren der csv Dateien
    2. Private Function StartImport(csvFile As String, ByRef csvError As String) As Boolean
    3. 'Prüfen ob Datei existiert
    4. If Not System.IO.File.Exists(csvFile) Then
    5. csvError = "Datei nicht gefunden"
    6. Return False
    7. End If
    8. Using TheStream As New FileStream(csvFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
    9. Using TheReader As New StreamReader(TheStream, Encoding.Default)
    10. 'Dim TheReader As New StreamReader(csvFile, Encoding.Default)
    11. Dim sline As String = ""
    12. Dim ShippingDate As Date
    13. Do
    14. 'Splitten der einzelnen csv Zeilen
    15. sline = TheReader.ReadLine
    16. If sline Is Nothing Then Exit Do
    17. Dim words() As String = sline.Split(";"c)
    18. 'überspringen der Überschriften
    19. If words(0) = "PARCELNO" Then Continue Do
    20. 'abbrechen, wenn zu wenig Einträge vorhanden sind
    21. If words.Length < 125 Then
    22. csvError = "Falsche Datei ausgewählt - zu wenig Einträge"
    23. Return False
    24. End If
    25. 'neue Reihe im DataTable anlegen
    26. Dim NewParcel As DSParcels.DTParcelRow
    27. NewParcel = DSParcels.DTParcel.NewDTParcelRow()
    28. 'Daten in die DataTable schreiben
    29. With NewParcel
    30. .ShippingNumber = words(0)
    31. .Protocol = words(3)
    32. .OrderID = words(4)
    33. .ebayName = words(5)
    34. .ArticleNumber = words(6)
    35. .Name1 = words(8)
    36. .Name2 = words(9)
    37. .Street = words(10)
    38. .Number = words(11)
    39. .ZipCode = words(15)
    40. .Location = words(16)
    41. If Date.TryParse(words(125), ShippingDate) Then .ShippingDate = ShippingDate
    42. End With
    43. DSParcels.DTParcel.Rows.Add(NewParcel)
    44. Loop
    45. End Using
    46. End Using
    47. csvError = ""
    48. Return True
    49. End Function


    Falls der Code oben nicht reicht, um den Code nachzuvollziehen, habe ich mal die Solution und eine csv Datei angehängt.
    Im Programm habe ich den Verweis auf nUpdate deaktiviert und den Code dazu auskommentiert.
    Rest ist original.
    Die csv enthält natürlich keine Daten, ich hab hier nur eine Zeile mit dem Wert "4" vollgeschrieben.
    Dateien
    • ParcelTracker.zip

      (48,44 kB, 71 mal heruntergeladen, zuletzt: )
    • csv.zip

      (154 Byte, 58 mal heruntergeladen, zuletzt: )

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