8

サイズがわからない場合、MappedViewAccessor を使用して MemoryMappedFile のすべての内容を読み取るには、ReadToEnd または ReadAllBytes に似たものが必要ですが、どうすればよいですか?

私はそれを検索しました、私はこの質問を見ましたが、それは私が探しているものではありません:

.NET でメモリ マップ ファイルからバイトをすばやく読み取るにはどうすればよいですか?

編集:

問題があります。(int)stream.Length は正しい長さを示していません。むしろ、使用されている内部バッファのサイズを示しています! この質問は非常に差し迫っているので、更新する必要があります。

4

7 に答える 7

15

代わりにストリームを使用します。

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);
            }
        }
    }
}
于 2013-02-19T10:00:48.960 に答える
8

あなたが指定していないアプリケーションの詳細がまだたくさんあるので、これに答えるのは難しいですが、Guffa と Amer の両方の答えはまだ部分的に正しいと思います:

  • MemoryMappedFile は、ファイルよりも多くのメモリです。これは、メモリ内の 4Kb ページのシーケンスです。したがって、stream.Length は実際にはすべてのバイトを提供します (「内部バッファー サイズ」はありません) が、サイズは常に 4Kb 境界に切り上げられるため、予想よりも多くのバイトが提供される可能性があります。
  • 「ファイル」セマンティックは、MemoryMappedFile を実際のファイルシステム ファイルに関連付けることに由来します。ファイルを作成するプロセスが常にファイル サイズを調整すると仮定すると、fileSystem を介してファイルの正確なサイズを取得できます。

上記のすべてがアプリケーションに適合する場合、次のように動作するはずです。

    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 の回答がおそらく最適です。

于 2013-03-12T05:07:37.930 に答える
7

そんなことはできません。

ビュー アクセサーは、システム ページの最小サイズで作成されます。つまり、実際のファイルよりも大きくなる可能性があります。ビュー ストリームはアクセサーの単なるストリーム形式であるため、同じ動作をします。

「ビューはシステムページ単位で提供され、ビューのサイズは次のシステムページサイズに切り上げられます」

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

アクセサーは、例外をスローすることなく、実際のファイルの外部で喜んで読み書きします。読み取り時に、ファイルの外側のバイトはすべてゼロになります。書き込み時には、ファイルの外側に書き込まれたバイトは単に無視されます。

元のファイルの正確なサイズでメモリ マップされたファイルからファイルを読み取るには、そのサイズを既に知っている必要があります。

于 2013-03-05T18:02:39.920 に答える
1

以下に示すように、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;
    }
}
于 2015-06-05T15:01:12.123 に答える
0

Vb.NETに翻訳された@Amer Sawanソリューションのみ:

' 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
于 2014-08-21T03:03:23.520 に答える