【问题标题】:Read all contents of memory mapped file or Memory Mapped View Accessor without knowing the size of it在不知道大小的情况下读取内存映射文件或内存映射视图访问器的所有内容
【发布时间】:2013-02-03 21:25:12
【问题描述】:

如果我不知道它的大小,我需要类似于 ReadToEnd 或 ReadAllBytes 的东西来使用 MappedViewAccessor 读取 MemoryMappedFile 的所有内容,我该怎么做?

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

How can I quickly read bytes from a memory mapped file in .NET?

编辑:

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

【问题讨论】:

  • 我不认为这是一个有效的问题......固件的大小如何适合您的数据必须适合指定区域的内存区域。您拥有创建的文件的大小,如果您没有该大小,则需要在通过抽象或 API 处理文件之前将其提供给您或已知。如果需要,您还可以在内存映射文件中创建一个标头来指示长度和当前偏移量。例如,如果您的文件大小小于页面大小并且我在您的大小之后写入该区域..

标签: c# .net ipc accessor memory-mapped-files


【解决方案1】:

宁可使用 Stream:

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);
            }
        }
    }
}

【讨论】:

  • 答案有问题,请提供更好的答案。
  • 太棒了,我没有发现以这种方式读取/写入字符串的任何问题,当获取字符串字节时,那些四舍五入的 4096 字节会消失。非常有用的解决方案。
【解决方案2】:

这很难回答,因为您的应用程序仍有许多细节您没有指定,但我认为 Guffa 和 Amer 的答案仍然部分正确:

  • MemoryMappedFile 比文件内存大;它是内存中 4Kb 页面的序列。所以,stream.Length 实际上会给你所有的字节(没有“内部缓冲区大小”),但它可能会给你比你预期的更多的字节,因为大小总是四舍五入到 4Kb 边界。李>
  • “文件”语义来自将 MemoryMappedFile 与真实文件系统文件相关联。假设创建文件的进程总是调整文件大小,那么您可以通过文件系统获得文件的精确大小。

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

    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;
                    }
                }
            }
        }
    }

要写入数据,可以这样:

    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 在数据本身中编码长度(或结束标记)的答案可能是最好的。

【讨论】:

  • 无需获取 FileInfo(fileName).Length CreateFromFile 内部使内存映射文件的容量与文件的大小相匹配。只需传递 0 作为大小。
  • @BransDs 即使您将 0 作为 size 传递,容量也将等于 或大于 实际文件大小(四舍五入到下一个 4096 字节边界)。如果您需要真实的文件大小,则无法绕过 new FileInfo(path).Length.
【解决方案3】:

你不能这样做。

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

"视图以系统页面为单位提供,视图的大小 向上舍入到下一个系统页面大小”

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

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

要从内存映射文件中读取具有原始文件确切大小的文件,您必须已经知道该大小。

【讨论】:

  • 您对 IPC 有什么建议?把一些尾巴添加到文件中?还是什么?
  • 我正在考虑将文件的大小放在开头。
  • @MohamedSakherSawan:是的,可以使用文件本身的数据来确定大小的任何文件结构都可以。
  • 你也可以在标题中包含当前的偏移量/长度......
  • @MohamedSakherSawan,您是否将内存映射文件用于 IPC?你为什么没有写在你的问题中?这是一个重要的细节!
【解决方案4】:

由 MemoryMappedFile 创建的流的长度与文件系统页面大小(通常为 4096)对齐。您必须从其他地方获取文件大小。如果它是内存映射 file 您可以使用该代码:

byte[] ReadAllMemoryMappedFileBytes(string filePath)
{
    var fileInfo = new FileInfo(filePath);
    using (var file = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open))
    using (var stream = file.CreateViewAccessor())
    {
        byte[] bytes = new byte[fileInfo.Length];
        stream.ReadArray(0, bytes, 0, bytes.Length);
        return bytes;
    }
}

【讨论】:

    【解决方案5】:

    使用 FileInfo 类获取长度如下所示

    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;
        }
    }
    

    【讨论】:

    • 如果可能的话,请多解释一下这段代码是如何工作的,以便提问者理解为什么它可以解决他们的问题。
    • 这一行很关键 var fi = new FileInfo(fileName); var 长度 = (int)fi.Length;一旦他知道长度,它将允许他用来读取该文件中的所有内容。
    • 我认为您不能在共享内存文件上使用 FileInfo。
    【解决方案6】:

    只需将@Amer Sawan 解决方案翻译成 Vb.NET:

    ' 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
    

    【讨论】:

      【解决方案7】:

      我想从 MemoryStream .ToArray() 方法中得到一些东西来返回所有字节,下面的代码对我有用:

      using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting(MemoryMappedName))
      {
          using (MemoryMappedViewStream stream = mmf.CreateViewStream())
          {
              using (MemoryStream memStream = new MemoryStream())
              {
                  stream.CopyTo(memStream);
                  return memStream.ToArray();
              }
          }
      }
      

      干杯!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-06
        • 1970-01-01
        • 2012-03-25
        相关资源
        最近更新 更多