我有一些大文本文件(> 1Gb),需要替换或删除一些行。我需要能够将一个随机选择的行替换为另一个,删除它或插入一行接一行。
我尝试使用getline(file, line)
来计算行数 - 这需要花费太多时间。这也导致需要很长时间才能通过它的数量来达到一条线。
有更高效或更好的方法吗?
答案 0 :(得分:2)
您必须考虑到,为了更改一行,您需要读取整个文件,搜索该行,更改它然后写入整个文件。这是不好的,但文件本质上是连续的,你不能访问所有以前的文件。
答案 1 :(得分:2)
您基本上有两种选择,具体取决于您的实际问题:
1)如果对文件重复执行此操作,可以使用更高级的数据结构对其进行一些优化。基本上你不再存储平面文本文件,而是存储行集合。这可以通过为每一行添加一个带有偏移的标题来实现,另一个delta文件包含所有更改(当然,在阅读时必须考虑这些更改)仅在它开始变大或运行时应用已完成,甚至将所有行保留在更传统的DBMS中。
2)如果每个文件很少执行此操作,您可能希望稍微优化您的读取例程。您可能有mmap
整个文件的最佳机会,并自己扫描它为EOL,因为您可以通过这种方式摆脱一大堆内存分配/字符串副本。虽然mmap
显然会在后台造成内存压力,但我发现这种技术在实践中相当快,而且很容易实现。
答案 2 :(得分:0)
我认为您可以使用数据库来保持每一行。表的每一行可以有两列(ID和行)。提交数据库后,您可以向数据库询问具有随机ID的行。
答案 3 :(得分:0)
我相信,您的最佳选择是使用内存映射文件。 CreateFileMapping和MapViewOfFile将为您提供帮助。
下面是在Windows中使用内存映射文件的简单示例。我省略了错误检查代码并简化了用'?'替换随机字符串字符的任务。这样做只是为了概述主要方面:
1)创建一个内存映射文件(CreateFile - > CreateFileMapping-> MapViewOfFile)
2)处理它,因为它只是一个内存块。是的,如果你需要在线模式下进行处理,你必须从头开始扫描,但是根据我的信念,它将是最快的方法。
重要说明:真实生活任务可能需要缩小或扩展原始文件。减少它很容易 - 只需将相应的部分保存回来并在关闭时截断文件。扩展将需要更多棘手的技术。
3)不要忘记使用UnmapViewOfFile / CloseHandle释放资源。
#include "stdafx.h"
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
TCHAR fileName[] = _T("SomeHugeFile.dat");
HANDLE hFile = CreateFile(
fileName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (!hFile) {
// Process the error
}
DWORD fileSize = GetFileSize(hFile, NULL);
if (!fileSize) {
// Check if it's an actual errro with GetLastError
// and process accordingly if so
}
HANDLE hMemMappedFile = CreateFileMapping(
hFile,
NULL,
PAGE_READWRITE,
0,
0,
NULL
);
if (!hMemMappedFile) {
// Process the error
}
LPVOID mappedMemory = MapViewOfFile(
hMemMappedFile,
FILE_MAP_ALL_ACCESS,
0,
0,
0 // To the end of the file
);
DWORD lineToDelete = 3; // Some random line number
// Assuming the file is ASCII and with Unix line endings
char *mappedMemoryStart = (char *)mappedMemory;
char *mappedMemoryEnd = mappedMemoryStart + fileSize;
char *lineStart = NULL;
char *lineEnd = NULL;
// Find the line start:
DWORD lineNumber = 0;
for (lineStart = (char *)mappedMemory; lineStart < mappedMemoryEnd; lineStart++) {
if (*lineStart == '\n') {
lineNumber++;
if (lineNumber == lineToDelete) {
lineStart++;
break;
}
}
}
if (lineStart >= mappedMemoryEnd) {
// Error: has gone beyond file end
}
for (lineEnd = lineStart; lineEnd < mappedMemoryEnd; lineEnd++) {
if (*lineEnd == '\n') {
break;
}
}
// Now mangle the found line:
while (lineStart < lineEnd) {
*lineStart = '?';
lineStart++;
}
UnmapViewOfFile(mappedMemory);
CloseHandle(hMemMappedFile);
CloseHandle(hFile);
return 0;
}