我有一个随时间附加的文本文件,并且我想定期将其截断到一定大小,例如 10MB,但保留最后 10MB 而不是第一个。
有什么聪明的方法可以做到这一点吗?我猜我应该寻找正确的点,从那里读入一个新文件,删除旧文件并将新文件重命名为旧名称。有更好的想法或示例代码吗?理想情况下,我不会将整个文件读入内存,因为文件可能很大。
请不要对使用 Log4Net 等提出建议。
我有一个随时间附加的文本文件,并且我想定期将其截断到一定大小,例如 10MB,但保留最后 10MB 而不是第一个。
有什么聪明的方法可以做到这一点吗?我猜我应该寻找正确的点,从那里读入一个新文件,删除旧文件并将新文件重命名为旧名称。有更好的想法或示例代码吗?理想情况下,我不会将整个文件读入内存,因为文件可能很大。
请不要对使用 Log4Net 等提出建议。
您无需在写入之前读取整个文件,尤其是在写入不同文件时。你可以分块工作;读一点,写,再读一点,再写。事实上,这就是所有 I/O 的完成方式。尤其是对于较大的文件,您永远不想一次全部阅读它们。
但是您建议的是从文件开头删除数据的唯一方法。你必须重写它。Raymond Chen 也有一篇关于该主题的博文。
如果您可以将最后 10MB 读入内存,这应该可以:
using(MemoryStream ms = new MemoryStream(10 * 1024 * 1024)) {
using(FileStream s = new FileStream("yourFile.txt", FileMode.Open, FileAccess.ReadWrite)) {
s.Seek(-10 * 1024 * 1024, SeekOrigin.End);
s.CopyTo(ms);
s.SetLength(10 * 1024 * 1024);
s.Position = 0;
ms.Position = 0; // Begin from the start of the memory stream
ms.CopyTo(s);
}
}
我从“假”测试了解决方案,但它对我不起作用,它修剪文件但保留它的开头,而不是结尾。
我怀疑CopyTo
复制整个流而不是从流位置开始。这是我如何使它工作的:
int trimSize = 10 * 1024 * 1024;
using (MemoryStream ms = new MemoryStream(trimSize))
{
using (FileStream s = new FileStream(logFilename, FileMode.Open, FileAccess.ReadWrite))
{
s.Seek(-trimSize, SeekOrigin.End);
byte[] bytes = new byte[trimSize];
s.Read(bytes, 0, trimSize);
ms.Write(bytes, 0, trimSize);
ms.Position = 0;
s.SetLength(trimSize);
s.Position = 0;
ms.CopyTo(s);
}
}
您可以将文件读入二进制流并使用 seek 方法检索最后 10MB 的数据并将它们加载到内存中。然后将此流保存到一个新文件中并删除旧文件。文本数据可能会被截断,因此您必须确定这是否可以例外。
在此处查看 Seek 方法的示例: