用C ++替换,添加和删除大文本文件中的行

时间:2015-06-05 19:43:42

标签: c++ windows

我有一些大文本文件(> 1Gb),需要替换或删除一些行。我需要能够将一个随机选择的行替换为另一个,删除它或插入一行接一行。

我尝试使用getline(file, line)来计算行数 - 这需要花费太多时间。这也导致需要很长时间才能通过它的数量来达到一条线。

有更高效或更好的方法吗?

4 个答案:

答案 0 :(得分:2)

您必须考虑到,为了更改一行,您需要读取整个文件,搜索该行,更改它然后写入整个文件。这是不好的,但文件本质上是连续的,你不能访问所有以前的文件。

答案 1 :(得分:2)

您基本上有两种选择,具体取决于您的实际问题:

1)如果对文件重复执行此操作,可以使用更高级的数据结构对其进行一些优化。基本上你不再存储平面文本文件,而是存储行集合。这可以通过为每一行添加一个带有偏移的标题来实现,另一个delta文件包含所有更改(当然,在阅读时必须考虑这些更改)仅在它开始变大或运行时应用已完成,甚至将所有行保留在更传统的DBMS中。

2)如果每个文件很少执行此操作,您可能希望稍微优化您的读取例程。您可能有mmap整个文件的最佳机会,并自己扫描它为EOL,因为您可以通过这种方式摆脱一大堆内存分配/字符串副本。虽然mmap显然会在后台造成内存压力,但我发现这种技术在实践中相当快,而且很容易实现。

答案 2 :(得分:0)

我认为您可以使用数据库来保持每一行。表的每一行可以有两列(ID和行)。提交数据库后,您可以向数据库询问具有随机ID的行。

答案 3 :(得分:0)

我相信,您的最佳选择是使用内存映射文件。 CreateFileMappingMapViewOfFile将为您提供帮助。

下面是在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;
}