Как я могу обрезать конец двоичного файла? - PullRequest
2 голосов
/ 13 июня 2011

Я использую следующий код для вставки текста в произвольный раздел файла:

Dim prebuffer() As Byte
Dim postbuffer() As Byte

Dim number As Integer
number = FreeFile

Open file For Binary Access Read Write Lock Read Write As number

ReDim prebuffer(after - 2)
ReDim postbuffer(LOF(number) - before)

Get number, 1, prebuffer
Get number, before, postbuffer

Seek number, 1

Put number, , prebuffer
Put number, , value
Put number, , postbuffer

Close number

after и before - это длинные значения, полученные из предыдущих вызовов на Seek(number). Когда after равно before, я просто хочу вставить данные, не стирая их. Но когда after меньше before, я перезаписываю существующие данные некоторыми своими.

Это прекрасно работает, когда (before - after) <= Len(value), поскольку Windows знает, как увеличить размер моего файла для размещения новых байтов. Однако, когда я вставляю меньше байтов, чем удаляю, файл не сжимается и оставляет там существующие байты.

Например, если мой двоичный файл - abcdefghijklmnopqrstuvwxyz и я хочу вставить HELLO с after=5 и before=15, я получу abcdeHELLOopqrstuvwxyzvwxyz с повторением vwxyz. Как мне сжать файл, чтобы я получил только abcdeHELLOopqrstuvwxyz?


Пояснение: я знаю, что могу удалить исходный файл и записать весь буфер обратно, но я стремлюсь к высокой производительности и написал вставку таким образом, потому что чтение и запись всего файла построчно было слишком медленный. Кроме того, я хотел бы сохранить свойства файла в такте и не хочу уничтожать их каждый раз, когда я вставляю запись, создавая совершенно новый файл.

1 Ответ

0 голосов
/ 05 декабря 2018

К сожалению, нет встроенных функций VB для установки длины файла. Можно надеяться использовать FileAttr для получения какого-либо дескриптора файла, который может использовать операционная система, но это допустимо только в 16-битном коде.

VBnet предлагает сделать все это с помощью функций Windows API:

Я написал простой код, основанный на примере VBnet:

Option Explicit

'' constants for CreateFile
Private Const OPEN_ALWAYS As Long = 4, GENERIC_WRITE As Long = &H40000000, GENERIC_READ As Long = &H80000000, FILE_ATTRIBUTE_NORMAL As Long = &H80, INVALID_HANDLE_VALUE As Long = -1

'' constants for SetFilePointer
Private Const FILE_BEGIN As Long = 0, INVALID_SET_FILE_POINTER As Long = -1

'' kernel32 functions needed
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hfile As Long, lpFileSizeHigh As Long) As Long
Private Declare Function SetFilePointer Lib "kernel32" (ByVal hfile As Long, ByVal lDistanceToMove As Long, lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Private Declare Function SetEndOfFile Lib "kernel32" (ByVal hfile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hfile As Long) As Long

Sub truncatefile(filename As String, ByVal size As Currency)
    Dim hfile As Long
    Dim dwFileSizeLow As Long
    Dim dwFileSizeHigh As Long
    Dim ret As Long

    '' open the file
    hfile = CreateFile(filename, _
        GENERIC_WRITE Or GENERIC_READ, _
        0&, ByVal 0&, _
        OPEN_ALWAYS, _
        FILE_ATTRIBUTE_NORMAL, _
        0&)

    Debug.Assert (hfile <> INVALID_HANDLE_VALUE) '' make sure file opened OK

    '' optional: get the current file length
    dwFileSizeLow = GetFileSize(hfile, dwFileSizeHigh)
    Debug.Assert (dwFileSizeLow >= 0 And dwFileSizeHigh = 0) '' TODO: handle 2GB and higher
    Debug.Print "Old file size: " & dwFileSizeLow

    '' split length into DWORDs (TODO: handle 2GB and higher)
    dwFileSizeLow = size
    dwFileSizeHigh = 0

    '' seek to the desired file length
    ret = SetFilePointer(hfile, dwFileSizeLow, dwFileSizeHigh, FILE_BEGIN)
    Debug.Assert ret <> INVALID_SET_FILE_POINTER

    '' set this as the length of the file
    ret = SetEndOfFile(hfile)
    Debug.Assert (ret <> 0)

    '' close the file handle
    Debug.Assert CloseHandle(hfile) <> 0

End Sub
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...