outofmemory - File.ReadAllText

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

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von markus.obi.

    outofmemory - File.ReadAllText

    Hallo,

    VB.NET-Quellcode

    1. Dim strContent As String = System.IO.File.ReadAllText(fileInfo.FullName, Encoding.[Default])
    2. strContent += System.IO.Path.GetFileNameWithoutExtension(fileInfo.FullName)

    mit dem wird eine Datei eingelesen und danach ein MD5Hash ermittelt, leider gibt es bei großen Dateien ein outofMemory ab ca. 180/200MB Dateien

    Gibt es eine Möglichkeit das nicht so speicherlastig zu machen?

    Gruß
    Hugoo00
    Noch 2 Gründe warum dein Ansatz nicht so dolle ist:

    1. Du ließt den content als String ein - soweit ich weiß brauchste aber die bytes.
    2. Du fügst an einen großen String was dran -> nicht gut. Dabei wird mal eben der String rumkopiert, was Zeit und kurzzeitig doppelt so viel Speicher als nötig frisst. (und wenn Strings, dann &= und nicht +=)

    @Artentus:
    Welchen Ansatz meinst du? algorithm.TransformBlock() oder MD5CryptoServiceProvider.ComputeHash(Stream)?
    Bei letzterem müsste man erst nen passenden Stream aus Dateiinhalt + Dateiname erstellen. Geht das überhaupt auf eine performante Art und Weise? Ich meine ohne die bytes der Datei in einem Rutsch einzulesen und ohne vermeidbares rumkopieren.

    Hier wäre ein Beispiel für die TransformBlock-Methode.
    Ich meinte eigentlich das zweite. Hätte man sich im Notfall halt ne eigene Stream-Klasse schreiben müssen, wäre nicht allzu schwer gewesen.
    Aber dass es sowas wie TransformBlock gibt, wusste ich gar nicht, ist hier auf jeden Fall vorzuziehen (ohne das Anhängen des Dateinamens wäre es mit dem Stream einfacher).
    hier mal die komplette funktion, nicht das es zu irgend welchen missverständnissen kommt

    VB.NET-Quellcode

    1. Private Shared Function GetMD5Hash(fileInfo As System.IO.FileInfo) As String
    2. Dim strContent As String = System.IO.File.ReadAllText(fileInfo.FullName, Encoding.[Default])
    3. strContent &= System.IO.Path.GetFileNameWithoutExtension(fileInfo.FullName)
    4. Using md5 As System.Security.Cryptography.MD5 = System.Security.Cryptography.MD5.Create()
    5. Return BitConverter.ToString(md5.ComputeHash(System.Text.Encoding.[Default].GetBytes(strContent))).Replace("-", "")
    6. End Using
    7. End Function

    Das mit den Stream hab ich mir auch schon gedacht aber irgend wie bekomm ick da den Dateinamen nicht dran und der muss unbedingt halt mit dran.
    Bei deiner Lösung wird dreimal ein Speicherplatz alloziiert, der der Größe der Datei entspricht.
    Außerdem kann diese Lösung auch nur Dateien bis zu einer bestimmten Größe verarbeiten.
    Die in Post#3 verlinkte Lösung hat diese Nachteile nicht.
    Es gibt übrigens C# to VB.Net Converter.

    Über MD5CryptoServiceProvider.ComputeHash(Stream) könnte es auch gehen und wäre wahrscheinlich sogar eleganter.

    VB.NET-Quellcode

    1. Function GetMd5Hash4(fileInfo As System.IO.FileInfo) As String
    2. Dim md5Hasher As New MD5CryptoServiceProvider()
    3. Dim data As Byte() = md5Hasher.ComputeHash(New FileStream(fileInfo.FullName, FileMode.Open))
    4. Dim sBuilder As New StringBuilder()
    5. Dim i As Integer
    6. For i = 0 To data.Length - 1
    7. sBuilder.Append(data(i).ToString("x2"))
    8. Next i
    9. Return sBuilder.ToString()
    10. End Function

    hab jetzt mal umgebaut mit ServiceProvider und die Datei über Stream drin sofern das so richtig ist zumindest wird mir nen md5 augeben wie bekommt man jetzt noch den dateinamen in den stream rein? muss man erst Write über nen StreamWriter benutzen ?
    Man könnte über .write() die Dateibytes und den Dateinamen in nen MemoryStream schreiben. Das wäre dann schön kurz aber es würde glaub ich wieder unnötig was rumkopiert. Die Methode von Artentus wäre denk ich eine Klasse schreiben die von Stream erbt und beim lesen den Dateicontent + Dateinamen zurückgibt. Weiß nicht genau wie das geht, ist aber etwas aufwendig.

    Es gäbe noch eine andere Methode:
    1. Bufferarray erstellen, länge = dateilänge + filenamebyteslänge
    1. Über Stream.read() den content direkt in das bytearray reinschreiben lassen.
    2. Via Array.Copy die filenamebytes hinten in den buffer reinkopieren.

    Bei zu großen Dateien (1 oder 2 GB) hilft aber nur TransformBlock!

    Spoiler anzeigen

    Visual Basic-Quellcode

    1. Private Sub Form1_Shown(sender As System.Object, e As System.EventArgs) Handles MyBase.Shown
    2. Dim finfo = New FileInfo("dadada")
    3. Using algorithm As MD5 = MD5.Create()
    4. Dim md5Hash As Byte() = CalculateHash(finfo, algorithm)
    5. Dim md5HashHex As String = String.Join(String.Empty, md5Hash.Select(Function(b) b.ToString("x2")))
    6. Debug.WriteLine(md5HashHex)
    7. End Using
    8. End Sub
    9. Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
    10. target = value
    11. Return value
    12. End Function
    13. Private Shared Function CalculateHash(finfo As FileInfo, algorithm As HashAlgorithm) As Byte()
    14. Dim BufferSize = 1 << 12
    15. Dim buffer As Byte() = New Byte(BufferSize - 1) {}
    16. Using inputStream As New FileStream(finfo.FullName, FileMode.Open)
    17. Dim readCount As Integer
    18. While (InlineAssignHelper(readCount, inputStream.Read(buffer, 0, BufferSize))) > 0
    19. algorithm.TransformBlock(buffer, 0, readCount, buffer, 0)
    20. End While
    21. End Using
    22. Dim fname = System.IO.Path.GetFileNameWithoutExtension(finfo.FullName)
    23. Dim fnamebytes = System.Text.Encoding.Default.GetBytes(fname)
    24. algorithm.TransformFinalBlock(fnamebytes, 0, fnamebytes.Length)
    25. Return algorithm.Hash
    26. End Function

    @SAR-71 es muss genau der MD5 raus kommen der aus dem datei inhalt + angefügtem dateiname raus kommt, sonnst ist der MD5 anders als der originale MD5. die funktion oben von mir hab ick hier auch ausm Forum aus einem anderen Thema von mir und die macht genau das was meine PHP funktion auch macht aber halt ziemlich Speicherlastig.

    @markus.obi ich versuch das mal mit der Funktion

    Tante Edit: @markus ich würde dich jetzt umarmen und drücken wenn du neben mir stehen würdest es funktioniert wirklich SUPER vielen lieben DANKE :thumbsup:

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

    Das mit den 1-2GB bezieht auf eine der Methoden, die ReadAllBytes() oder ReadAllText(). Wenn die Datei so groß ist, dass sie gar nicht mehr in den RAM passt, dann muss man eben ne andere Möglichkeit nehmen. Ob das jetzt die 32bit Begrenzung oder der verfügbare RAM-Speicher ist, ist ja egal.