AUTO AUTO - 1 month ago 24
C++ Question

Add/Remove bytes from end of file on Windows

So, I've looked around, but couldn't find a way to remove bytes from the end of a file without rewriting the entire file. I found that a

function works for linux, but didn't find anything for windows. Now, obviously, to expand a file, I can just pad the end with null bytes, but for reducing a file's size, is it literally necessary to rewrite the whole file on windows? or is there a function, maybe in
windows.h
, that allows me, like
truncate
on linux, to reassign a file's size?


EDIT:  I did just find the function , and I'm reading on how to use it.

EDIT:  And, why exactly did
fstream
leave out this vital function?

EDIT:  Ok, so it appears that
_chdir()
will not work (I forgot to mention this, btw), because the function must support files larger than 4 GB - i.e., I'm using 64bit file pointers. I thought that would be inherent, but after reading the arguments to
chsize
, the length is not
size_t
.

Answer

You truncate a file by calling SetFilePointer or SetFilePointerEx to the desired location followed by SetEndOfFile. The following shows how a truncate function can be implemented:

bool truncate( HANDLE hFile, LARGE_INTEGER NewSize ) {
    LARGE_INTEGER Size = { 0 };
    if ( GetFileSizeEx( hFile, &Size ) ) {
        LARGE_INTEGER Distance = { 0 };
        // Negative values move the pointer backward in the file
        Distance.QuadPart = NewSize.QuadPart - Size.QuadPart;
        return ( SetFilePointerEx( hFile, Distance, NULL, FILE_END ) &&
                 SetEndOfFile( hFile ) );
    }
    return false;
}

// Helper function taking a file name instead of a HANDLE
bool truncate( const std::wstring& PathName, LARGE_INTEGER NewSize ) {
    HANDLE hFile = CreateFileW( PathName.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
                                NULL, OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL, NULL );
    if ( hFile == INVALID_HANDLE_VALUE ) {
        return false;
    }
    bool Success = truncate( hFile, NewSize );
    CloseHandle( hFile );
    return Success;
}

EDIT: Shorter Version The truncate function can be shortened to the following:

bool truncate( HANDLE hFile, LARGE_INTEGER NewSize ) {
    return ( SetFilePointerEx( hFile, NewSize, NULL, FILE_BEGIN ) &&
             SetEndOfFile( hFile ) );
}

If you would rather want to pass the amount of bytes by which to shrink the file, the following implementation can be used:

bool truncate( HANDLE hFile, LARGE_INTEGER ShrinkBy ) {
    ShrinkBy.QuadPart = -ShrinkBy.QuadPart;
    return ( SetFilePointerEx( hFile, ShrinkBy, NULL, FILE_END ) &&
             SetEndOfFile( hFile ) );
}

To grow a file, open the file using CreateFile with a dwDesiredAccess that contains FILE_APPEND_DATA. Using SetFilePointer again to set the file pointer to the end of file you can then write new data calling WriteFile. For an example, see Appending One File to Another File.


EDIT: Growing a file without writing to it

If you don't care about the file contents beyond the original file size you can apply the same sequence as shown for truncating a file to extend it:

bool SetFileSize( HANDLE hFile, LARGE_INTEGER NewSize ) {
    return ( SetFilePointerEx( hFile, NewSize, NULL, FILE_BEGIN ) &&
             SetEndOfFile( hFile ) );
}

This is documented behavior for SetEndOfFile:

The SetEndOfFile function can be used to truncate or extend a file. If the file is extended, the contents of the file between the old end of the file and the new end of the file are not defined.