我有一個6GB的文件,最後20行是壞的。我想在.NET 4中使用內存映射文件來讀取最後幾行,並將它們顯示在console.writelines中,然後轉到最後20行,並用String.Empty替換它們。使用帶C#示例的內存映射文件/流來做這件事很酷的方法是什麼?內存映射文件讀取文件結尾?
謝謝。
我有一個6GB的文件,最後20行是壞的。我想在.NET 4中使用內存映射文件來讀取最後幾行,並將它們顯示在console.writelines中,然後轉到最後20行,並用String.Empty替換它們。使用帶C#示例的內存映射文件/流來做這件事很酷的方法是什麼?內存映射文件讀取文件結尾?
謝謝。
從這個問題聽起來像你需要一個內存映射文件。但是,有一種方法可以在不使用內存映射文件的情況下執行此操作。
正常打開文件,然後將文件指針移到文件的末尾。一旦你在最後,反向讀取文件(每次讀取後遞減文件指針),直到獲得所需數量的字符。
酷酷的方式......將字符反轉載入數組,然後在完成讀取後不必將其反轉。
對數組進行修復然後將其寫回。關閉,沖洗,完成!
解決方案有兩個部分。對於第一部分,您需要向後讀取存儲器映射以獲取線條,直到讀取了所需的行數(在這種情況下爲20)。
對於第二部分,您希望截斷最後20行的文件(將它們設置爲string.Empty)。我不確定你是否可以用內存映射來做到這一點。
您可能必須在某處複製文件,並使用除最後xxx字節(代表最後20行)之外的源數據覆蓋原始文件
以下代碼將提取最後20行並顯示它。
您還將獲得最後20行開始的位置(lastBytePos變量) 。您可以使用該信息來了解截斷文件的位置。
UPDATE:截斷文件調用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();
}
}
}
內存映射文件可以是(是一個大小相當於或大於RAM更大的通常文件)大文件的一個問題,如果你最終映射整個文件。如果你只繪製結尾,那應該不是真正的問題。
無論如何,這裏是一個C#實現,它不使用內存映射文件,而是一個普通的FileStream。它基於ReverseStreamReader
實現(代碼也包含在內)。在性能和內存消耗方面,我會好奇的看到它與其他MMF解決方案相比。
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();
}
}
我對ReverseStreamReaders一無所知。該解決方案是[基本上]簡單:
雖然,惡魔在細節,關於「讀反向線」。有一些複雜因素可能會讓您陷入困境:
我不知道有一個良好的,容易明顯之外的解決方案:通過文件順序讀取和不寫了二十行。
你知道一個普通的方式,現在正在尋找一個很酷呢? – khachik 2010-12-09 20:23:30
我現在不知道任何方式。我希望默認'酷'。現在我實際上使用File對象上的old-school流和readline讀取文件直到結束,並顯示結束,我甚至不在刪除部分。 – Snowy 2010-12-10 16:31:39