如何修剪二进制文件的末尾? [英] How can I trim the end of a binary file?

查看:68
本文介绍了如何修剪二进制文件的末尾?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用以下代码将文本插入文件的任意部分:

I am using the following code to insert text into an arbitrary section of a file:

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

afterbefore是从以前对Seek(number)的调用中检索的long.当after等于before时,我只是想插入数据而不会擦除任何数据.但是,当after小于before时,我将使用自己的一些数据覆盖现有数据.

after and before are longs retrieved from previous calls to Seek(number). When after equals before, I simply want to insert data without erasing any. But when after is less than before, I'm overwriting existing data with some of my own.

(before - after) <= Len(value)时,这非常有用,因为Windows知道增加我的文件大小以容纳新的字节.但是,当我插入的字节少于删除的字节时,文件不会缩小,而将现有字节保留在那里.

This works great when (before - after) <= Len(value), as Windows knows to increase my file size to accommodate the new bytes. However, when I'm inserting less bytes than I'm removing, the file does not shrink, and leaves the existing bytes there.

例如,如果我的二进制文件是abcdefghijklmnopqrstuvwxyz,并且我想在after=5before=15中插入HELLO,则得到abcdeHELLOopqrstuvwxyzvwxyz,并重复vwxyz.如何缩小文件以便只得到abcdeHELLOopqrstuvwxyz?

For example, if my binary file is abcdefghijklmnopqrstuvwxyz and I want to insert HELLO with after=5 and before=15 I get abcdeHELLOopqrstuvwxyzvwxyz, with vwxyz being repeated. How do I shrink the file so that I only get abcdeHELLOopqrstuvwxyz?

澄清:我知道我可以删除原始文件并写回整个缓冲区,但我的目标是提高性能,并以这种方式编写插入内容,因为也可以逐行读取和写入整个文件慢.另外,我想保持文件的属性完好无损,并且不想在每次插入记录时都通过制作一个全新的文件来销毁它们.

Clarification: I know that I could delete the original file and write the entire buffer back, but I'm aiming for high performance, and wrote the insert this way because reading and writing the entire file line-by-line was too slow. Also, I would like to keep the properties of the file in tact, and don't want to destroy them every time I insert a record by making an entirely new file.

推荐答案

没有用于设置文件长度的本地VB函数.可能希望使用 FileAttr 获取操作系统可以使用的某种文件句柄,但这仅在16位代码中有效.

There are no native VB functions for setting the length of the file, sadly. One might hope to use FileAttr to get some sort of file handle the operating system can use, but this is valid only in 16-bit code.

VBnet 建议使用Windows进行全部操作API函数:

VBnet suggests doing the whole thing using the Windows API functions:

  • CreateFile / CloseHandle to open/close the file
  • GetFileSize to get the size from the file handle
  • SetFilePointer to seek within the file
  • SetEndOfFile to truncate the file

我已经基于VBnet示例编写了一些简单的代码:

I've written some simple code based on the VBnet example:

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

这篇关于如何修剪二进制文件的末尾?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆