在存档中搜索文件并将其加载到内存中

时间:2015-06-12 11:27:55

标签: c++ memory archive ram

基本上我需要将存档中的文件加载到内存中,但由于用户能够修改存档的内容,因此文件偏移量很可能会发生变化。

所以我需要创建一个函数,在十六进制模式的帮助下搜索存档中的文件,返回文件偏移量,将文件加载到内存中并返回文件地址。

要将文件加载到内存中并返回我当前使用的地址:

DWORD LoadBinary(char* filePath)
{
    FILE *file = fopen(filePath, "rb");
    long fileStart = ftell(file);
    fseek(file, 0, SEEK_END);
    long fileSize = ftell(file);
    fseek(file, fileStart, 0);
    BYTE *fileBuffer = new BYTE[fileSize];
    fread(fileBuffer, fileSize, 1, file);
    LPVOID newmem = VirtualAlloc(NULL, fileSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(newmem, fileBuffer, fileSize);
    delete[]fileBuffer;
    fclose(file);
    return (DWORD)newmem;
}

存档既不加密也不压缩,但它很大(大约1 GB),如果可能的话我不想将整个文件加载到内存中。

我知道我在档案中寻找的文件的大小,所以我不需要该函数来找到另一种模式的文件末尾。

文件模式:“\ x30 \ x00 \ x00 \ x00 \ xA0 \ x10 \ x04 \ x00”

文件长度:4096字节

我如何实现这一点以及需要哪些功能?

解决方案

大型文件的代码可能很慢,但这对我有用,因为我正在寻找的文件位于存档的开头。

FILE *file = fopen("C:/data.bin", "rb");
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
rewind(file);

BYTE *buffer = new BYTE[4];
int b = 0; //bytes read
long offset = 0;

for (int i = 0; i < fileSize; i++)
{
    int input = fgetc(file);

    *(int *)((DWORD)buffer + b) = input;

    if (b == 3)
    {
        b = 0;
    }
    else {
        b = b + 1;
    }

    if (buffer[0] == 0xDE & buffer[1] == 0xAD & buffer[2] == 0xBE & buffer[3] == 0xEF)
    {
        offset = (ftell(file) - 4);
        printf("Match @ 0x%08X", offset);
        break;
    }
}
fclose(file);

1 个答案:

答案 0 :(得分:1)

原则在this answer中说明:你需要一个有限状态机(FSM),它将文件字节逐个作为输入,并根据FSM状态将当前输入与模式中的字节进行比较,这是一个模式中的索引。

这是最简单但却天真的解决方案模板:

FILE *file = fopen(path, "rb");
size_t state = 0;
for (int input_result; (input_result = fgetc(file)) != EOF;) {
    char input = (char)input_result;
    if (input == pattern[state]) {
        ++state;
    } else {
        state = 0;
    }
    if (pattern_index == pattern_size) {
        // Pattern is found at (ftell(file) - pattern_size).
        break;
    }
}
fclose(file);

state变量保持模式中的位置,它是FSM的状态。

虽然这个解决方案满足了您的需求,但它并不是最佳选择,因为从文件中读取一个字节几乎与读取更大的块(例如512字节甚至更多)相同。您可以通过两个步骤自行改进:

  1. 每次迭代都会读取一个块,而不是一个字符。使用fread()。请注意,模式位置的计算(在找到之后)变得有点复杂,因为ftell()不再匹配input位置。
  2. 添加一个内部循环来遍历您刚读过的块。像以前一样处理输入字符 - 这是FSM方法证明自己有用的地方。