在Windows上截断大文件

时间:2015-09-23 09:53:38

标签: r windows winapi

根据R中truncate函数的手册页,在某些平台上包括Windows:

  

...它不适用于大型(> 2Gb)文件

经过一些实验,我设法制作了一个玩具示例,显示可以使用visual c ++对大文件(非常容易)执行此操作:

// ConsoleApplication1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
#include <iostream>
#include <string>

//  Forward declarations:
void append(LPCTSTR, LPCVOID, DWORD);
void readTail(LPCTSTR, LPVOID, DWORD);
void truncateTail(LPCTSTR, long);


int main()
{
    LPCTSTR fn = L"C:/kaiyin/kybig.out";
    char buf[] = "helloWorld"; 
    append(fn, buf, 10);
    BYTE buf1[10] = {0};
    readTail(fn, buf1, 5);
    std::cout << (char*) buf1 << std::endl;
    //truncateTail(fn, 5);
    //for (int i = 0; i < 10; i++) {
    //  buf1[i] = 0;
    //}
    //readTail(fn, buf1, 5);
    //std::cout << (char*) buf1 << std::endl;

    printf("End of program\n");
    std::string s = "";
    std::getline(std::cin, s);
    return 0;
}

void append(LPCTSTR filename, LPCVOID buf, DWORD writeSize) {
    LARGE_INTEGER size;
    size.QuadPart = 0;
    HANDLE fh = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    GetFileSizeEx(fh, &size);
    SetFilePointerEx(fh, size, NULL, FILE_BEGIN);
    WriteFile(fh, buf, writeSize, NULL, NULL);
    CloseHandle(fh);
}

void readTail(LPCTSTR filename, LPVOID buf, DWORD readSize) {
    LARGE_INTEGER size;
    size.QuadPart = 0;
    HANDLE fh = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    GetFileSizeEx(fh, &size);
    size.QuadPart -= readSize;
    SetFilePointerEx(fh, size, NULL, FILE_BEGIN);
    ReadFile(fh, buf, readSize, NULL, NULL);
    CloseHandle(fh);
}

void truncateTail(LPCTSTR filename, long truncateSize) {
    LARGE_INTEGER size;
    size.QuadPart = 0;
    HANDLE fh = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (fh == INVALID_HANDLE_VALUE) {
        std::cerr << GetLastError();
        return;
    }
    GetFileSizeEx(fh, &size);
    size.QuadPart -= truncateSize;
    SetFilePointerEx(fh, size, NULL, FILE_BEGIN); 
    if (SetEndOfFile(fh) == 0) {
        std::cerr << GetLastError();
        return;
    }
    CloseHandle(fh);
}

这将附加&#34; helloWorld&#34;到文件&#34; C:/kaiyin/kybig.out" ;,然后截断&#34; World&#34;。在控制台中它应该打印&#34; World&#34; (截断前的尾巴),然后&#34;你好&#34; (截断后的尾巴)。

在截断大于2GB的文件的尾部似乎没有任何问题 - 实际上,我已经使用4e9字节文件进行了测试,程序仍能正常运行。

我是否遗漏了某些内容,或者截断功能确实可以在Windows上可靠(并且轻松)实现?

更新

按照@ hrbrmstr对this R bugzilla link的引用,我尝试了一些R代码来验证truncate函数是否在Windows 8.1上正常工作:

filename = "C:/kaiyin/kybig.out"
f = file(filename, "w")
seek(f, 5L, "end")
truncate(f)
file.info(filename)$size

结果:

> filename = "C:/kaiyin/kybig.out"
> f = file(filename, "w")
> seek(f, 5L, "end")
[1] 0
> truncate(f)
NULL
> file.info(filename)$size
[1] 0

显然truncate只是在seek接近结束时才会破坏所有内容。

2 个答案:

答案 0 :(得分:3)

  

我是否遗漏了某些内容,或者截断功能确实可以在Windows上可靠(并且轻松)实现?

可能的解释是,问题与Windows无关,而是与R函数的实现有关。在Windows上,它可能使用带符号的32位整数来指定截断的文件大小,因此限制。

文档可能已过时也是合理的,R开发人员现在已经设法弄清楚如何在Windows上正确实现此功能。

答案 1 :(得分:0)

R似乎没有使用Windows API来访问文件,而是看起来它使用POSIX (or POSIX-like) layer,至少根据我在链接问题中提到的the source code。因此,虽然截断大文件可能在使用Windows API的Windows上运行(如代码所示),但R使用的这个POSIX(类似)层可能(尚未)完全支持这一点(同样,请参阅源代码) )。