关于c#:读取内存映射文件或内存映射视图访问器的所有内容,而不知道它的大小

Read all contents of memory mapped file or Memory Mapped View Accessor without knowing the size of it

我需要类似于readtoend或readallbytes的东西来使用mappedviewaccessor读取memoryMapped文件的所有内容。如果我不知道它的大小,我该怎么做?

我已经搜索过了,我看到了这个问题,但这不是我要找的:

如何从.NET中的内存映射文件快速读取字节?

编辑:

有一个问题,(int)stream.length没有给出正确的长度,而是给出了所使用的内部缓冲区的大小!我需要重新回答这个问题,因为它非常紧迫。


而是使用流:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static Byte[] ReadMMFAllBytes(string fileName)
{
    using (var mmf = MemoryMappedFile.OpenExisting(fileName))
    {
        using (var stream = mmf.CreateViewStream())
        {
            using (BinaryReader binReader = new BinaryReader(stream))
            {
                return binReader.ReadBytes((int)stream.Length);
            }
        }
    }
}


这是很难回答的,因为你的申请还有很多细节,但我认为Guffa和Amer的回答仍然部分正确:

  • memoryMappedFile的内存大于文件;它是内存中4KB页的序列。因此,stream.length实际上会提供所有字节(没有"内部缓冲区大小"),但它可能会提供比预期更多的字节,因为该大小总是四舍五入到4KB边界。
  • "文件"语义来自将memoryMappedFile关联到真正的文件系统文件。假设创建文件的过程总是调整文件大小,那么您可以通过文件系统获得文件的精确大小。

如果以上所有内容都适合您的应用程序,那么以下内容应该有效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    static byte[] ReadMemoryMappedFile(string fileName)
    {
        long length = new FileInfo(fileName).Length;
        using (var stream = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite))
        {
            using (var mmf = MemoryMappedFile.CreateFromFile(stream, null, length, MemoryMappedFileAccess.Read, null, HandleInheritability.Inheritable, false))
            {
                using (var viewStream = mmf.CreateViewStream(0, length, MemoryMappedFileAccess.Read))
                {
                    using (BinaryReader binReader = new BinaryReader(viewStream))
                    {
                        var result = binReader.ReadBytes((int)length);
                        return result;
                    }
                }
            }
        }
    }

要写入数据,可以使用以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    private static void WriteData(string fileName, byte[] data)
    {
        using (var stream = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
        {
            using (var mmf = MemoryMappedFile.CreateFromFile(stream, null, data.Length, MemoryMappedFileAccess.ReadWrite, null, HandleInheritability.Inheritable, true))
            {
                using (var view = mmf.CreateViewAccessor())
                {
                    view.WriteArray(0, data, 0, data.Length);
                }
            }

            stream.SetLength(data.Length);  // Make sure the file is the correct length, in case the data got smaller.
        }
    }

但是,当您完成上述所有操作时,您也可以直接使用文件并避免内存映射。如果无法将其映射到文件系统,那么Guffa对数据本身的长度(或结束标记)进行编码的答案可能是最好的。


你不能那样做。

视图访问器是以最小系统页面大小创建的,这意味着它可能大于实际文件。视图流只是访问器的流形式,因此它也将提供相同的行为。

"views are provided in units of system pages, and the size of the view
is rounded up to the next system page size"

http://msdn.microsoft.com/en-us/library/dd267577.aspx

访问器很乐意在实际文件之外读写,而不会引发异常。读取时,文件之外的任何字节都将为零。写入时,只忽略文件外写入的字节。

要从与原始文件大小完全相同的内存映射文件中读取文件,必须知道该大小。


使用fileinfo类获取长度,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
using System.Data;
using System.IO;
using System.IO.Compression;
using System.IO.MemoryMappedFiles;

// ...

public void WriteToMemoryMap(DataSet ds, string key, string fileName)
{
    var bytes = CompressData(ds);
    using (MemoryMappedFile objMf = MemoryMappedFile.CreateFromFile(fileName, FileMode.OpenOrCreate, key, bytes.Length))
    {
        using (MemoryMappedViewAccessor accessor = objMf.CreateViewAccessor())
        {
            accessor.WriteArray(0, bytes, 0, bytes.Length);
        }
    }
}
public DataSet ReadFromMemoryMap(string fileName)
{
    var fi = new FileInfo(fileName);
    var length = (int)fi.Length;
    var newBytes = new byte[length];
    using (MemoryMappedFile objMf = MemoryMappedFile.CreateFromFile(fileName, FileMode.Open))
    {
        using (MemoryMappedViewAccessor accessor = objMf.CreateViewAccessor())
        {
            accessor.ReadArray(0, newBytes, 0, length);
        }
    }
    return DecompressData(newBytes);
}
public byte[] CompressData(DataSet ds)
{
    try
    {
        byte[] data = null;
        var memStream = new MemoryStream();
        var zipStream = new GZipStream(memStream, CompressionMode.Compress);
        ds.WriteXml(zipStream, XmlWriteMode.WriteSchema);
        zipStream.Close();
        data = memStream.ToArray();
        memStream.Close();
        return data;
    }
    catch (Exception)
    {
        return null;
    }
}
public DataSet DecompressData(byte[] data)
{
    try
    {
        var memStream = new MemoryStream(data);
        var unzipStream = new GZipStream(memStream, CompressionMode.Decompress);
        var objDataSet = new DataSet();
        objDataSet.ReadXml(unzipStream, XmlReadMode.ReadSchema);
        unzipStream.Close();
        memStream.Close();
        return objDataSet;
    }
    catch (Exception)
    {
        return null;
    }
}


我想从memoryStream.toArray()方法中得到一些返回所有字节的内容,下面的代码对我有用:

1
2
3
4
5
6
7
8
9
10
11
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting(MemoryMappedName))
{
    using (MemoryMappedViewStream stream = mmf.CreateViewStream())
    {
        using (MemoryStream memStream = new MemoryStream())
        {
            stream.CopyTo(memStream);
            return memStream.ToArray();
        }
    }
}

干杯!


只需将@amer sawan解决方案翻译为vb.net:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
' Usage Example:
'
Dim ReadBytes As Byte() = ReadMemoryMappedFile(Name:="My MemoryMappedFile Name") ' Read the byte-sequence from memory.
'
Dim Message As String = System.Text.Encoding.ASCII.GetString(ReadBytes.ToArray) ' Convert the bytes to String.
'
Message = Message.Trim({ControlChars.NullChar}) ' Remove null chars (leading zero-bytes)
'
MessageBox.Show(Message,"", MessageBoxButtons.OK) ' Show the message.    '
'
'
'' <summary>
''' Reads a byte-sequence from a <see cref="IO.MemoryMappedFiles.MemoryMappedFile"/> without knowing the exact size.
'
'' Note that the returned byte-length is rounded up to 4kb,
''' this means if the mapped memory-file was written with 1 byte-length, this method will return 4096 byte-length.
'
'' </summary>
''' <param name="Name">Indicates an existing <see cref="IO.MemoryMappedFiles.MemoryMappedFile"/> assigned name.</param>
'
'' <returns>System.Byte().</returns>
Private Function ReadMemoryMappedFile(ByVal Name As String) As Byte()

    Try
        Using MemoryFile As IO.MemoryMappedFiles.MemoryMappedFile =
            IO.MemoryMappedFiles.MemoryMappedFile.OpenExisting(Name, IO.MemoryMappedFiles.MemoryMappedFileRights.ReadWrite)

            Using Stream = MemoryFile.CreateViewStream()

                Using Reader As New BinaryReader(Stream)

                    Return Reader.ReadBytes(CInt(Stream.Length))

                End Using ' Reader

            End Using '
Stream

        End Using ' MemoryFile

    Catch exNoFile As IO.FileNotFoundException
        Throw
        Return Nothing

    Catch ex As Exception
        Throw

    End Try

End Function