在存档中搜索文件并将其加载到内存中
Search for file in archive and load it into memory
基本上我需要将存档中的文件加载到内存中,但由于用户能够修改存档的内容,因此文件偏移量很可能会发生变化。
所以我需要创建一个函数,借助十六进制模式在存档中搜索文件,returns 文件偏移量,将文件加载到内存中,returns文件地址.
加载文件到内存和return地址我目前使用这个:
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);
原理在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 字节甚至更多)所花费的时间几乎相同。您可以通过两个步骤自行改进:
- 每次迭代读取一个块,而不是单个字符。使用
fread()
。请注意模式位置的计算(在找到之后)变得有点复杂,因为 ftell()
不再匹配 input
位置。
- 添加一个内部循环以遍历您刚刚阅读的块。以与以前相同的方式处理输入字符——这就是 FSM 方法证明自己有用的地方。
基本上我需要将存档中的文件加载到内存中,但由于用户能够修改存档的内容,因此文件偏移量很可能会发生变化。
所以我需要创建一个函数,借助十六进制模式在存档中搜索文件,returns 文件偏移量,将文件加载到内存中,returns文件地址.
加载文件到内存和return地址我目前使用这个:
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);
原理在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 字节甚至更多)所花费的时间几乎相同。您可以通过两个步骤自行改进:
- 每次迭代读取一个块,而不是单个字符。使用
fread()
。请注意模式位置的计算(在找到之后)变得有点复杂,因为ftell()
不再匹配input
位置。 - 添加一个内部循环以遍历您刚刚阅读的块。以与以前相同的方式处理输入字符——这就是 FSM 方法证明自己有用的地方。