在C#中,当已知行的字节位置时,从文件中删除一行文本

4
我有一个大于2GB的文本文件,并且正在使用Filestream以1kb的块读取文件。在每个块中,我会计算行数,并使用此计数找到要删除行的文件位置。
例如,如果我想要删除的行的字节位置为4097,是否有一种方法可以在C#中删除从4097开始直到遇到\n字符为止的行中的字符?
我正在查看Filestream.Seek()方法以直接转到删除位置。但是,我不确定如何进一步进行。
由于它是一个大文件,我不想创建另一个文件,这将在系统和内存中消耗大量空间。是否有一种有效的方法可以使用,而不必创建新文件来删除该行。
非常感谢您的建议和帮助!

可能是从文本文件中删除一行的有效方法的重复问题和https://dev59.com/cHRB5IYBdhLWcg3wSVi1。 - Darin Dimitrov
如果你要删除一行代码,最终你需要编写另一个文件或者将后续的代码移到文件的前面,然后再删除末尾的部分。 - Paul Farry
你可以逻辑删除这些行,然后当你插入新的一行时,只需替换你标记为删除的第一行,如果没有找到,则将其放置在末尾。 - Steve's a D
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");
    }

此外,在删除文件时,最好非常小心并仔细检查可能出错的所有内容。
编辑:第一个算法有点毫无意义,因为我仍然在读取整个文件;现在它有意义了。

那是个好主意,damix911。我已经实现了它,而且它完美地运行!很抱歉我目前无法为你的答案投票,因为我的声望很低,我刚刚设置了一个帐户。当我有足够的声望时会投票支持的。谢谢! - bedinesh
不客气,我很高兴能帮到你 :-) 感谢你的问题,这些有关持久化的话题总是非常有趣。 - damix911

0
处理大文件的最有效方法是使用内存映射文件。它的好处在于您不需要读取整个文件,修改它,然后再写入,您只需修改数据的有趣部分。将4097设置为偏移量并加载一些100 KBytes。这个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);
        }
    }
}

Vlad,这似乎是一个不错的建议。我会仔细考虑并尝试实施它。当我有更多的声望时会投票支持你的答案。谢谢! - bedinesh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接