Memory Mapped File to Read End of File?
See the question and my original answer on StackOverflowMemory Mapped Files can be a problem for big files (typically files that are of a size equivalent or bigger than the RAM), in case you eventually map the whole file. If you map only the end, that should not be a real issue.
Anyway, here is a C# implementation that does not use Memory Mapped File, but a regular FileStream. It is based on a ReverseStreamReader
implementation (code also included). I would be curious to see it compared to other MMF solutions in terms of performance and memory consumption.
public static void OverwriteEndLines(string filePath, int linesToStrip)
{
if (filePath == null)
throw new ArgumentNullException("filePath");
if (linesToStrip <= 0)
return;
using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite))
{
using (ReverseStreamReader reader = new ReverseStreamReader(file))
{
int count = 0;
do
{
string line = reader.ReadLine();
if (line == null) // end of file
break;
count++;
if (count == linesToStrip)
{
// write CR LF
for (int i = 0; i < linesToStrip; i++)
{
file.WriteByte((byte)'\r');
file.WriteByte((byte)'\n');
}
// truncate file to current stream position
file.SetLength(file.Position);
break;
}
}
while (true);
}
}
}
// NOTE: we have not implemented all ReadXXX methods
public class ReverseStreamReader : StreamReader
{
private bool _returnEmptyLine;
public ReverseStreamReader(Stream stream)
: base(stream)
{
BaseStream.Seek(0, SeekOrigin.End);
}
public override int Read()
{
if (BaseStream.Position == 0)
return -1;
BaseStream.Seek(-1, SeekOrigin.Current);
int i = BaseStream.ReadByte();
BaseStream.Seek(-1, SeekOrigin.Current);
return i;
}
public override string ReadLine()
{
if (BaseStream.Position == 0)
{
if (_returnEmptyLine)
{
_returnEmptyLine = false;
return string.Empty;
}
return null;
}
int read;
StringBuilder sb = new StringBuilder();
while((read = Read()) >= 0)
{
if (read == '\n')
{
read = Read();
// supports windows & unix format
if ((read > 0) && (read != '\r'))
{
BaseStream.Position++;
}
else if (BaseStream.Position == 0)
{
// handle the special empty first line case
_returnEmptyLine = true;
}
break;
}
sb.Append((char)read);
}
// reverse string. Note this is optional if we don't really need string content
if (sb.Length > 1)
{
char[] array = new char[sb.Length];
sb.CopyTo(0, array, 0, array.Length);
Array.Reverse(array);
return new string(array);
}
return sb.ToString();
}
}