4 votes

Définir la position/Index de FileStream.Seek pour récupérer des "blocs" de données VB.NET

Je travaille actuellement sur une méthode qui prend un fichier texte et le réduit à ~10 Mo. Cette méthode est utilisée pour tronquer les fichiers journaux et les maintenir dans une limite de 10 Mo.

La logique derrière le code est essentiellement la suivante... si le fichier est de 250 Mo ou plus, lire les octets jusqu'à ce que le tableau atteigne 250 Mo. Stockez-les dans un fichier StringBuilder la position pour la lecture suivante et répétez jusqu'à ce que la touche StringBuilder contient ~10 MB de données. Ensuite, on écrit dans le fichier en effaçant toutes les données et en ne laissant que 10 Mo des écritures les plus récentes.

Pour éviter de couper des lignes en deux, il vérifie où se trouve la dernière CrLf et écrit ensuite toutes les données à partir de ce point.

Mon problème est que je ne peut pas faire en sorte que la recherche se positionne correctement après la première lecture. Il lit les données correctement la première fois, puis lorsque j'utilise la position de la lecture précédente pour l'itération suivante, il "ignore" la position et lit à nouveau depuis le début du fichier.

If logFile.Length > (1024 * 1024 * 250) Then
    Dim DataToDelete As Integer = logFile.Length - (1024 * 1024 * 250)
    Dim ArrayIndex As Integer = 0
    While DataToDelete > 0
        Using fs As FileStream = New FileStream(logFile.FullName, FileMode.Open, FileAccess.ReadWrite)
            fs.Seek(ArrayIndex, SeekOrigin.Begin)
            If strBuilder.Length < (1024 * 1024 * 250) Then
                Dim bytes() As Byte = New Byte((1024 * 1024 * 250)) {}
                Dim n As Integer = fs.Read(bytes, 0, (1024 * 1024 * 250))
                ArrayIndex = bytes.Length
                Dim enc As Encoding = Encoding.UTF8
                strBuilder.Append(enc.GetString(bytes))
            Else
                If DataToDelete - strBuilder.Length < 0 And strBuilder.Length > (1024 * 1024 * My.Settings.Threshold) Then
                    Dim DataToCut As Integer = strBuilder.Length - (1024 * 1024 * My.Settings.Threshold)
                    While Not (strBuilder.Chars(DataToCut).ToString.Equals(vbCr)) And DataToCut <> 0
                        DataToCut -= 1
                    End While
                    strBuilder.Remove(0, DataToCut)
                    File.WriteAllText(logFile.FullName, strBuilder.ToString)
                Else
                    DataToDelete -= strBuilder.Length
                    strBuilder.Clear()
                End If
            End If
        End Using
    End While
End If

1voto

Steven Doggart Points 22763

Pour ce que vous faites, il n'est pas nécessaire, et ce n'est vraiment pas une bonne idée, de charger le fichier entier en mémoire. Il serait bien mieux de lire uniquement la partie du fichier journal que vous avez l'intention de conserver (les 10 derniers Mo). Par exemple, il serait beaucoup plus simple et plus efficace de faire quelque chose comme ceci :

Private Sub ShrinkLog(ByVal filePath As String, ByVal maxSize As Integer)
    Dim buffer As String
    If New FileInfo(filePath).Length > maxSize Then
        Using reader As New StreamReader(filePath)
            reader.BaseStream.Seek(-maxSize, SeekOrigin.End)
            buffer = reader.ReadToEnd()
        End Using
        File.WriteAllText(filePath, buffer)
    End If
End Sub

Il y a aussi d'autres façons de procéder. Il serait même plus efficace, si vous deviez conserver une plus grande partie du fichier, de ne pas charger tout cela en mémoire, mais de passer directement d'un flux à l'autre. De plus, cet exemple simple ne montre pas comment vous pourriez éviter de couper une ligne en cours de route dans le fichier, mais je suis sûr que vous pourriez continuer à chercher un octet à la fois jusqu'à ce que vous trouviez le premier saut de ligne.

0voto

user1732364 Points 379

Voici mon résultat final, qui fonctionne comme un charme !

        Dim Maxsize As Integer = (1024 * 1024 * My.Settings.Threshold)
    For Each logfile In filesToTrim
        Dim sb As New StringBuilder
        Dim buffer As String = String.Empty
        If logfile.Length > Maxsize Then
            Using reader As New StreamReader(logfile.FullName)
                reader.BaseStream.Seek(-Maxsize, SeekOrigin.End)
                buffer = reader.ReadToEnd()
                sb.Append(buffer)
            End Using
            Dim Midpoint As Integer = 0
            While Not (sb.Chars(Midpoint).ToString.Equals(vbCr)) And Midpoint <> sb.Length - 1
                Midpoint += 1
            End While
            sb.Remove(0, Midpoint)
            File.WriteAllText(logfile.FullName, sb.ToString)
        End If
    Next

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X