解决方案有两个部分。对于第一部分,您需要向后读取内存映射以抓取行,直到您读取所需的行数(在本例中为 20)。
对于第二部分,您希望将文件截断最后二十行(通过将它们设置为 string.Empty)。我不确定您是否可以使用内存映射来做到这一点。 您可能需要在某处制作文件副本并用源数据覆盖原始数据,除了最后 xxx 个字节(表示最后二十行)
下面的代码将提取最后二十行并显示出来。
您还将获得位置(lastBytePos 变量)
最后二十行开始的地方。您可以使用该信息来了解在何处截断文件。
更新:截断文件调用FileStream.SetLength(lastBytePos)
我不确定你所说的最后 20 行不好是什么意思。如果磁盘物理损坏并且无法读取数据,我添加了一个 badPositions 列表,其中包含内存映射在读取数据时出现问题的位置。
我没有要测试的 +2GB 文件,但它应该可以工作(手指交叉)。
using System;
using System.Collections.Generic;
using System.Text;
using System.IO.MemoryMappedFiles;
using System.IO;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
string filename = "textfile1.txt";
long fileLen = new FileInfo(filename).Length;
List<long> badPositions = new List<long>();
List<byte> currentLine = new List<byte>();
List<string> lines = new List<string>();
bool lastReadByteWasLF = false;
int linesToRead = 20;
int linesRead = 0;
long lastBytePos = fileLen;
MemoryMappedFile mapFile = MemoryMappedFile.CreateFromFile(filename, FileMode.Open);
using (mapFile)
{
var view = mapFile.CreateViewAccessor();
for (long i = fileLen - 1; i >= 0; i--) //iterate backwards
{
try
{
byte b = view.ReadByte(i);
lastBytePos = i;
switch (b)
{
case 13: //CR
if (lastReadByteWasLF)
{
{
//A line has been read
var bArray = currentLine.ToArray();
if (bArray.LongLength > 1)
{
//Add line string to lines collection
lines.Insert(0, Encoding.UTF8.GetString(bArray, 1, bArray.Length - 1));
//Clear current line list
currentLine.Clear();
//Add CRLF to currentLine -- comment this out if you don't want CRLFs in lines
currentLine.Add(13);
currentLine.Add(10);
linesRead++;
}
}
}
lastReadByteWasLF = false;
break;
case 10: //LF
lastReadByteWasLF = true;
currentLine.Insert(0, b);
break;
default:
lastReadByteWasLF = false;
currentLine.Insert(0, b);
break;
}
if (linesToRead == linesRead)
{
break;
}
}
catch
{
lastReadByteWasLF = false;
currentLine.Insert(0, (byte) '?');
badPositions.Insert(0, i);
}
}
}
if (linesToRead > linesRead)
{
//Read last line
{
var bArray = currentLine.ToArray();
if (bArray.LongLength > 1)
{
//Add line string to lines collection
lines.Insert(0, Encoding.UTF8.GetString(bArray));
linesRead++;
}
}
}
//Print results
lines.ForEach( o => Console.WriteLine(o));
Console.ReadKey();
}
}
}