首页 > C#:NET4.0中如何使用内存映射操作大文件

C#:NET4.0中如何使用内存映射操作大文件

以下代码在执行的时候,最终读取的结果并不是文件test.txt中的内容。
不知道哪里码错了,还请大家帮忙指点下。

MemoryMappedFile memoryFile = MemoryMappedFile.CreateFromFile("test.txt", FileMode.OpenOrCreate, "MyFile", FILE_SIZE);
accessor1 = memoryFile.CreateViewAccessor(0, FILE_SIZE / 2);
StringBuilder sb = new StringBuilder(FILE_SIZE);
for (int i = 0; i < FILE_SIZE / 2; i++)
{
    char ch = accessor1.ReadChar(i);
    sb.Append(ch);
}
MessageBox.Show(sb.ToString());

你的test.txt里面存的是什么样的文本?编码格式是什么?这些都决定了你读出来的内容是否正确。这段程序只有在UTF-16编码的文本中能读出你想要的内容,因为.NET的char是unicode的。如果你不是用UTF-16编码,比如前两个字符是ab,ReadChar会把ab一起读出来当成一个unicode字符,天知道那会是什么结果。而且如果是ReadChar的话一次读两个字节,你怎么可以i++呢,至少要i+=2吧。另外很多文本文件(至少要windows中)前几个字节是BOM标志。你也没处理。

所以,最简单的办法是用CreateViewStream而不是CreateViewAccessor,然后用StreamReader指定Encoding去读。

var filePath = @"D:\junk\test.txt";
var fileSize = new FileInfo(filePath).Length;
var viewSize = fileSize / 2;

using (var mm = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read))
using (var stream = mm.CreateViewStream(0, viewSize, MemoryMappedFileAccess.Read))
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
    Console.WriteLine(reader.ReadToEnd());
}

如果非要用Accessor,那这么写,但是BOM没处理,前几个字符是乱码

var filePath = @"D:\junk\test.txt";
var fileSize = new FileInfo(filePath).Length;
var viewSize = fileSize / 2;

using (var mm = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read))
using (var accessor = mm.CreateViewAccessor(0, viewSize, MemoryMappedFileAccess.Read))
{
    byte[] bytes = new byte[viewSize];
    for (int i = 0; i < viewSize; i++)
    {
        bytes[i] = accessor.ReadByte(i);
    }

    var results = Encoding.UTF8.GetString(bytes);
    Console.WriteLine(results);
}

另外:

  1. 你是想要从已经存在的文件里读取数据,那么FileMode就不要OpenOrCreate,用Open就好了。不然文件不存在重新创建了一个,你读一堆0出来没有意义。

  2. 如果不是为了和其它进程共享,mapName可以传null。

  3. 如果只是读文件,fileSize的参数可以传0,系统会自动match文件的真实大小。

  4. 对于实现了IDisposible的类,记得用using,或者在finally里dispose掉。

【热门文章】
【热门文章】