Прочитайте все содержимое файла с отображением памяти или Memory Mapped View Accessor, не зная его размера

Мне нужно что-то похожее на ReadToEnd или ReadAllBytes, чтобы прочитать все содержимое MemoryMappedFile с помощью MappedViewAccessor, если я не знаю его размер, как я могу это сделать?

Я искал его, я видел этот вопрос, но это не то, что я ищу:

Как быстро прочитать байты из файла с отображением памяти в .NET?

Edit:

Возникает проблема, поток (int). Длина строки не дает мне правильной длины, она скорее дает размер используемого внутреннего буфера! Мне нужно обновить этот вопрос, потому что это очень важно.

Ответ 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);
            }
        }
    }
}

Ответ 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 на кодирование длины (или конечного маркера) в самих данных.

Ответ 3

Вы не можете этого сделать.

Аксессуар вида создается с минимальным размером системной страницы, что означает, что он может быть больше, чем фактический файл. Поток представления - это просто форма потока аксессора, поэтому он также даст такое же поведение.

"представлены в единицах системных страниц, а размер представления округляется до следующего размера страницы системы"

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

Аксессор будет с радостью читать и писать вне фактического файла, не вызывая исключения. При чтении любые байты вне файла будут равны нулю. При записи байты, записанные вне файла, просто игнорируются.

Чтобы прочитать файл из файла с отображением памяти с точным размером исходного файла, вы должны уже знать этот размер.

Ответ 4

Просто решение @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

Ответ 5

Я хотел бы иметь что-то из метода MemoryStream.ToArray() для возврата всех байтов, а код ниже работает для меня:

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

Ура!

Ответ 6

Используйте класс FileInfo для получения длины, как показано ниже.

    public void WriteToMemoryMap(DataSet ds, string key, string fileName)
    {
        var bytes = CompressData(ds);
        using (System.IO.MemoryMappedFiles.MemoryMappedFile objMf = System.IO.MemoryMappedFiles.MemoryMappedFile.CreateFromFile(fileName, System.IO.FileMode.OpenOrCreate, key, bytes.Length))
        {
            using (System.IO.MemoryMappedFiles.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 (System.IO.MemoryMappedFiles.MemoryMappedFile objMf = System.IO.MemoryMappedFiles.MemoryMappedFile.CreateFromFile(fileName, FileMode.Open))
        {
            using (System.IO.MemoryMappedFiles.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 ex)
        {
            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 ex)
        {
            return null;
        }
    }