4

我有一个大文本文件 (>2gb)。我目前正在使用 Filestream 以 1kb 的块读取文件。在每个块中,我计算行数并使用此计数在文件中找到必须删除行的位置。

例如,如果我要删除的行的字节位置是 4097,那么在 C# 中有没有办法可以删除从 4097 开始的行中的字符,直到我遇到 \n 字符。

我在看 Filestream.Seek() 方法直接去删除位置。但是,我不确定如何进一步进行。

因为它是一个大文件,所以我不想创建另一个会消耗大量系统空间和内存的文件。有没有一种有效的方法可以在不创建新文件的情况下删除该行。

任何建议和帮助将不胜感激。

提前致谢!

4

2 回答 2

1

我觉得缩短文件的唯一方法是真正复制其中一些,然后跳过,然后复制其余部分。如果您真的需要就地执行,您可以选择某种形式的逻辑删除。例如,您可以仅使用 LF 对文本文件中的新行进行编码(这不是Windows 上的默认设置,而是使用一对 CR-LF),这很可能需要 8 位 ASCII 并选择类似这样的内容:

    public static void LogicalEraseLine(string filename, int toDel)
    {
        FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite);

        fs.Seek(toDel, SeekOrigin.Current);
        int c;

        while ((c = fs.ReadByte()) != -1)
        {
            if (c == '\n')
            {
                break;
            }
            else
            {
                fs.Seek(-1, SeekOrigin.Current);
                fs.WriteByte((byte)'\n');
            }
        }

        fs.Close();
    }

注意toDel是要删除的第一个字符的索引,而不是要删除的行的索引。这段代码只是用相同数量的空行替换了toDel 处和行尾之间的所有字符。然后你需要另一个函数将文件复制到另一个文件,但跳过所有空行。您可以在将来任何方便的时间进行此清理。不过,您的实际算法需要能够处理文件中的大量空白行。此外,您应该分块读取文件是对的,但是在这种情况下也可以应用此示例中显示的基本思想。

编辑 您可以使用此功能擦除逻辑删除的行:

    public static void Cleanup (string filename)
    {
        FileStream input = new FileStream(filename, FileMode.Open, FileAccess.Read);
        FileStream output = new FileStream(filename + ".tmp", FileMode.Create, FileAccess.Write);

        bool emptyLine = true;
        int c;

        while ((c = input.ReadByte()) != -1)
        {
            if (c == '\n')
            {
                if (!emptyLine)
                {
                    output.WriteByte((byte)c);
                    emptyLine = true;
                }
            }
            else
            {
                output.WriteByte((byte)c);
                emptyLine = false;
            }
        }

        input.Close();
        output.Close();

        File.Delete (filename);
        File.Copy(filename + ".tmp", filename);
        File.Delete(filename + ".tmp");
    }

此外,在删除文件时,最好非常小心并仔细检查可能出错的所有内容。

编辑 第一个算法有点没有意义,因为我还在阅读整个文件;现在它是有道理的。

于 2013-03-03T23:10:59.843 回答
0

处理大文件最有效的方法是使用内存映射文件。这样做的好处是您不需要读取整个文件,修改它然后再次写入,您只需修改数据中感兴趣的部分即可。将 4097 设置为偏移量并加载大约 100 KB。这个来自 MSDN 的示例应该可以帮助您入门。

long offset = 0x10000000;  
long length = 0x20000000; // 512 megabytes 

// Create the memory-mapped file. 
using (var mmf = MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data", FileMode.Open,"ImgA"))
{
    // Create a random access view, from the 256th megabyte (the offset) 
    // to the 768th megabyte (the offset plus length). 
    using (var accessor = mmf.CreateViewAccessor(offset, length))
    {
        int colorSize = Marshal.SizeOf(typeof(MyColor));
        MyColor color;

        // Make changes to the view. 
        for (long i = 0; i < length; i += colorSize)
        {
            accessor.Read(i, out color);
            color.Brighten(10);
            accessor.Write(i, ref color);
        }
    }
}
于 2013-03-03T22:58:34.610 回答