我有一个大文本文件(> 2gb)。我目前正在使用Filestream以1kb的块读取文件。在每个块中,我计算行数并使用此计数我已在文件中找到必须删除行的位置。
例如,如果我要删除的行的字节位置是4097,那么在C#中是否有一种方法可以删除从4097开始直到我点击\ n字符的行中的字符。
我正在看Filestream.Seek()方法直接转到删除位置。但是,我不确定如何继续前进。因为,它是一个大文件,我不想创建另一个文件,它会占用系统和内存中的大量空间。有没有一种有效的方法可以用来删除行而不创建新文件。
任何建议和帮助将不胜感激。
提前致谢!
答案 0 :(得分: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");
}
此外,删除文件时,最好小心非常并仔细检查可能出错的所有内容。
修改强> 第一种算法有点无意义,因为我还在阅读整个文件;现在它是有道理的。
答案 1 :(得分:0)
处理大文件的最有效方法是使用Memory-Mapped files。它的好处是你不需要读取整个文件,修改它然后再写,你只需要修改有趣的数据部分。将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);
}
}
}