我正在为本地和托管C ++编写4GB +的大型存档文件(见下文)的编辑器。
为了访问文件,我正在使用文件映射(见下文),就像任何理智的人一样。这对于读取数据非常有用,但实际编辑存档时会出现问题。 文件映射不允许在访问文件时调整文件大小,所以我不知道当用户想要在文件中插入新数据时(如果文件被映射时会超出文件的原始大小,我应该如何继续)。 / p>
我应该每次都重新映射整件事吗?这肯定会很慢。但是,我希望通过独占文件访问实时保持编辑器,因为这样可以简化编程,并且在修改时不会让文件被其他应用程序搞砸。我不想永远在编辑上工作;对于我正在进行的实际项目,它只是一个简单的开发工具。
所以我想听听您是如何处理类似案件的,以及其他归档软件以及其他游戏如何解决此问题?
澄清:
这不是文本文件,我正在编写特定的二进制存档文件格式。我指的是一个包含许多其他文件的大文件,在目录中。出于多种原因,自定义存档文件在游戏使用中非常常见。使用我的格式,我的目标是使用与Valve Software's GCF format类似(但有点简单)的结构 - 我会原样使用GCF格式,但遗憾的是格式没有编辑器,尽管有很多很棒的格式阅读它们的实现,如HLLib。
访问文件必须快速,因为它用于存储游戏资源。所以它不是一个数据库。数据库文件将包含在其中,以及GFX,SFX等文件。
这里讨论的“文件映射”是Windows平台上的一种特定技术,它允许通过创建部分文件的“视图”直接访问大文件,请参见此处:http://msdn.microsoft.com/en-us/library/aa366556(VS.85).aspx - 这技术允许最小的延迟和内存使用,并且是访问任何大文件的明智之举。 所以不意味着将整个4GB文件读入内存,恰恰相反。
答案 0 :(得分:2)
“编辑软件”是什么意思?如果这是一个文本文件,在编写自己的编辑器之前,您是否尝试过现有的生产质量编辑器?如果它是存储二进制数据的文件,您是否考虑过使用RDBMS并使用SQL语句操作其内容?
如果你必须从头开始写这个,我不确定mmapping是否可行。映射一个巨大的文件会给你的机器的VM系统带来很大的压力,除非整个文件有很多编辑操作,否则它的效率可能会落后于一个简单的读/写方案。更糟糕的是,正如您所说,当您想要扩展文件时,您会遇到问题。
相反,维护文件数据的缓冲区窗口,用户可以修改。当用户决定保存文件时,顺序遍历文件和编辑的缓冲区以创建新的文件图像。如果你有磁盘空间,那么编写一个新文件会更容易(特别是如果缓冲区的大小已经改变),否则你需要在用新内容覆盖它之前聪明地预读现有数据。
或者,您可以保留编辑操作的日志。当用户决定保存文件时,在日志上执行拓扑排序并在现有文件上播放以创建新文件。
对于独占文件访问,请使用操作系统的文件锁定或实现应用程序级锁定(如果只有编辑器将触摸这些文件)。取决于独占访问的mmap会限制您的实现选择。
答案 1 :(得分:2)
映射文件是为实际访问数据而创建的,但我认为您需要另一个表示文件结构的抽象。有多种方法可以做到这一点,但考虑将文件表示为“范围”序列。
从文件开始是一个等同于整个映射的范围。如果用户随后开始编辑文件,则会在编辑点将单个范围拆分为两个,并插入包含用户已插入数据的新范围。修改和删除也会通过创建或修改这些范围来修改您的文件视图。
也许您可以检查一个开源编辑器的源代码 - 有很多可供选择,但找到一个足够简单的将是挑战。
答案 2 :(得分:1)
对于这个问题没有简单的答案 - 我已经找了很长时间,徒劳无功。你必须修改文件的大小,然后重新映射它。
答案 3 :(得分:1)
我所做的是关闭视图句柄和FileMapping句柄,设置文件大小然后重新打开映射/视图句柄。
// Open memory mapped file
HANDLE FileHandle = ::CreateFileW(file_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
size_t Size = ::GetFileSize(FileHandle, 0);
HANDLE MappingHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READWRITE, 0, Size, NULL);
void* ViewHandle = ::MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, Size);
...
// increase size of file
UnmapViewOfFile(ViewHandle);
CloseHandle(MappingHandle);
Size += 1024;
LARGE_INTEGER offset;
offset.QuadPart = Size;
LARGE_INTEGER newpos;
SetFilePointerEx(FileHandle, offset, &newpos, FILE_BEGIN);
SetEndOfFile(FileHandle);
MappingHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READWRITE, 0, Size, NULL);
ViewHandle = ::MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, Size);
上面的代码没有错误检查,也没有处理64位大小,但这并不难解决。
答案 4 :(得分:1)
映射在远程系统上存在文件的基本问题。
在旧的DOS时代,有一个名为Norton Editor的精美编辑器(ne.com ..这个 文件名,而不是网站)。它可以加载任何大小的文件(我们说的是640kb RAM 和20 GB硬盘(如果有的话)。
它过去只加载部分文件,巧妙地按需管理文件长搜索 正在加载
恕我直言,应该采用这种方法。如果在文件读写层下正确隐藏,它可能会非常透明。
答案 5 :(得分:0)
我会在构建时从片段构建大文件。您可以让编辑器处理普通文件系统中的普通文件(包含子目录等)。然后,您有一个编译步骤,将所有这些部分收集到您的存档文件格式中。