Frage

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 truncate 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 _chdir(int,long), 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.

War es hilfreich?

Lösung

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.

Andere Tipps

You probably want the SetEndOfFile function.

EDIT: This should work with files larger than 4GB. Use the SetFilePointerEx function for that.

To remove bytes from end of file on Windows:

FSUTIL file seteof <filename> <new size>

To add (null)bytes to end of (existing)file:

FSUTIL file seteof <filename> <new size>

No need to copy/"rewrite the whole file on windows"

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top