Frage

Ich bin über das Lesen Tonnen von Binärdateien, die jeweils mit 1000 oder mehr Aufzeichnungen zu beginnen. Neue Dateien werden Ich schreibe ständig hinzugefügt, um einen Windows-Dienst, um die Verzeichnisse zu überwachen und neue Dateien zu verarbeiten, wie sie empfangen werden. Die Dateien wurden mit einem c ++ Programm erstellt. Ich habe die Struktur Definitionen in c # neu erstellt und die Daten in Ordnung lesen, aber ich bin besorgt darüber, dass die Art, wie ich tue es wird schließlich meine Anwendung töten.

using (BinaryReader br = new BinaryReader(File.Open("myfile.bin", FileMode.Open)))
{
    long pos = 0L;
    long length = br.BaseStream.Length;

    CPP_STRUCT_DEF record;
    byte[] buffer = new byte[Marshal.SizeOf(typeof(CPP_STRUCT_DEF))];
    GCHandle pin;

    while (pos < length)
    {
        buffer = br.ReadBytes(buffer.Length);
        pin = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        record = (CPP_STRUCT_DEF)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(CPP_STRUCT_DEF));
        pin.Free();

        pos += buffer.Length;

        /* Do stuff with my record */
    }
}

Ich glaube nicht, ich brauche GCHandle zu verwenden, weil ich eigentlich nicht mit dem C ++ App in Verbindung stehe, wird alles wird von verwalteten Code getan, aber ich weiß nicht, ein alternatives Verfahren.

War es hilfreich?

Lösung

Für Ihre spezielle Anwendung, nur eine Sache gibt Ihnen die endgültige Antwort: Profil es.

Das hier gesagt sind die Lektionen, die ich gelernt habe, während sie mit großen PInvoke Lösungen arbeiten. Der effektivste Weg, um Daten zu Marshal Felder Marschall, die blitfähig sind. Gemeint ist die CLR kann einfach das tun, was zu einer Memcpy beträgt Daten zwischen systemeigenen und verwalteten Code zu bewegen. In einfachen Worten, erhalten alle nicht-Inline-Arrays und Strings aus Ihren Strukturen. Wenn sie in der nativen Struktur sind, stellen sie mit einem IntPtr und die Werte nach Bedarf in verwaltetem Code Marschall.

Ich habe nicht immer den Unterschied zwischen der Verwendung von Marshal.PtrToStructure Profil eine native API dereferenzieren den Wert gegenüber haben. Dies ist wahrscheinlich etwas, das Sie investieren in sollten sollte PtrToStructure als Engpass durch Profilierung enthüllt werden.

Für große Hierarchien Marschall auf Anfrage gegen eine ganze Struktur in verwaltetem Code zu einem einzigen Zeitpunkt zu ziehen. Ich habe in dieser Frage laufen die meisten, wenn sie mit großen Baumstrukturen zu tun. einen einzelnen Knoten Rangierung ist sehr schnell, wenn es blitfähig ist und Leistung klug es funktioniert nur aus Marschall, was Sie in diesem Moment brauchen.

Andere Tipps

Marshal.PtrToStructure Verwendung ist ziemlich langsam. Ich fand den folgenden Artikel auf Codeproject, die (und Benchmarking) verschiedene Arten des Lesens binäre Daten sehr hilfreich ist der Vergleich:

  

Schnelle Binärdatei Lesung mit C #

Neben JaredPar der umfassenden Antwort, Sie müssen nicht GCHandle verwenden, können Sie stattdessen unsicheren Code verwenden können.

fixed(byte *pBuffer = buffer) {
     record = *((CPP_STRUCT_DEF *)pBuffer);
}  

Der ganze Zweck der GCHandle / fixed Aussage ist das jeweilige Speichersegment an Pin / fixiert, wobei den Speicher unbeweglich aus GC-Sicht zu machen. Wenn der Speicher beweglich ist, würde jede Verlagerung Ihre Zeiger ungültig machen.

Nicht sicher, welchen Weg ist schneller though.

Dies kann außerhalb der Grenzen Ihrer Frage, aber ich würde geneigt sein, eine kleine Montage in Managed C ++ zu schreiben, die eine fread tat () oder etwas ähnlich schnell in den Strukturen zu lesen. Sobald Sie haben in ihnen lesen, können Sie C # tun alles, was Sie mit ihnen benötigen.

verwenden

Hier ist eine kleine Klasse i eine Weile zurück gemacht, während sie mit strukturierten Dateien. es war die schnellste Methode, die ich scheut gehen unsicher zu der Zeit herausfinden konnte (das war, was ich versuche, eine vergleichbare Leistung zu ersetzen und zu pflegen.)

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;

namespace PersonalUse.IO {

    public sealed class RecordReader<T> : IDisposable, IEnumerable<T> where T : new() {

        const int DEFAULT_STREAM_BUFFER_SIZE = 2 << 16; // default stream buffer (64k)
        const int DEFAULT_RECORD_BUFFER_SIZE = 100; // default record buffer (100 records)

        readonly long _fileSize; // size of the underlying file
        readonly int _recordSize; // size of the record structure
        byte[] _buffer; // the buffer itself, [record buffer size] * _recordSize
        FileStream _fs;

        T[] _structBuffer;
        GCHandle _h; // handle/pinned pointer to _structBuffer 

        int _recordsInBuffer; // how many records are in the buffer
        int _bufferIndex; // the index of the current record in the buffer
        long _recordPosition; // position of the record in the file

        /// <overloads>Initializes a new instance of the <see cref="RecordReader{T}"/> class.</overloads>
        /// <summary>
        /// Initializes a new instance of the <see cref="RecordReader{T}"/> class.
        /// </summary>
        /// <param name="filename">filename to be read</param>
        public RecordReader(string filename) : this(filename, DEFAULT_STREAM_BUFFER_SIZE, DEFAULT_RECORD_BUFFER_SIZE) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="RecordReader{T}"/> class.
        /// </summary>
        /// <param name="filename">filename to be read</param>
        /// <param name="streamBufferSize">buffer size for the underlying <see cref="FileStream"/>, in bytes.</param>
        public RecordReader(string filename, int streamBufferSize) : this(filename, streamBufferSize, DEFAULT_RECORD_BUFFER_SIZE) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="RecordReader{T}"/> class.
        /// </summary>
        /// <param name="filename">filename to be read</param>
        /// <param name="streamBufferSize">buffer size for the underlying <see cref="FileStream"/>, in bytes.</param>
        /// <param name="recordBufferSize">size of record buffer, in records.</param>
        public RecordReader(string filename, int streamBufferSize, int recordBufferSize) {
            _fileSize = new FileInfo(filename).Length;
            _recordSize = Marshal.SizeOf(typeof(T));
            _buffer = new byte[recordBufferSize * _recordSize];
            _fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None, streamBufferSize, FileOptions.SequentialScan);

            _structBuffer = new T[recordBufferSize];
            _h = GCHandle.Alloc(_structBuffer, GCHandleType.Pinned);

            FillBuffer();
        }

        // fill the buffer, reset position
        void FillBuffer() {
            int bytes = _fs.Read(_buffer, 0, _buffer.Length);
            Marshal.Copy(_buffer, 0, _h.AddrOfPinnedObject(), _buffer.Length);
            _recordsInBuffer = bytes / _recordSize;
            _bufferIndex = 0;
        }

        /// <summary>
        /// Read a record
        /// </summary>
        /// <returns>a record of type T</returns>
        public T Read() {
            if(_recordsInBuffer == 0)
                return new T(); //EOF
            if(_bufferIndex < _recordsInBuffer) {
                // update positional info
                _recordPosition++;
                return _structBuffer[_bufferIndex++];
            } else {
                // refill the buffer
                FillBuffer();
                return Read();
            }
        }

        /// <summary>
        /// Advances the record position without reading.
        /// </summary>
        public void Next() {
            if(_recordsInBuffer == 0)
                return; // EOF
            else if(_bufferIndex < _recordsInBuffer) {
                _bufferIndex++;
                _recordPosition++;
            } else {
                FillBuffer();
                Next();
            }
        }

        public long FileSize {
            get { return _fileSize; }
        }

        public long FilePosition {
            get { return _recordSize * _recordPosition; }
        }

        public long RecordSize {
            get { return _recordSize; }
        }

        public long RecordPosition {
            get { return _recordPosition; }
        }

        public bool EOF {
            get { return _recordsInBuffer == 0; }
        }

        public void Close() {
            Dispose(true);
        }

        void Dispose(bool disposing) {
            try {
                if(disposing && _fs != null) {
                    _fs.Close();
                }
            } finally {
                if(_fs != null) {
                    _fs = null;
                    _buffer = null;
                    _recordPosition = 0;
                    _bufferIndex = 0;
                    _recordsInBuffer = 0;
                }
                if(_h.IsAllocated) {
                    _h.Free();
                    _structBuffer = null;
                }
            }
        }

        #region IDisposable Members

        public void Dispose() {
            Dispose(true);
        }

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator() {
            while(_recordsInBuffer != 0) {
                yield return Read();
            }
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        #endregion

    } // end class

} // end namespace

zu verwenden:

using(RecordReader<CPP_STRUCT_DEF> reader = new RecordReader<CPP_STRUCT_DEF>(path)) {
    foreach(CPP_STRUCT_DEF record in reader) {
        // do stuff
    }
}

(ziemlich neu hier, hoffen, dass nicht zu viel zu schreiben ... klebte nur in der Klasse, nicht die Kommentare oder etwas hacken sie zu verkürzen.)

Es scheint, das hat nichts mit weder C ++ noch Rangierung zu tun. Sie kennen die Struktur, was sonst Sie benötigen.

Natürlich müssen Sie einen einfachen Code, welche Gruppe von Bytes einer Struktur gelesen wird repräsentiert und dann BitConverter mit Bytes zu platzieren in C # Feldern entsprechen ..

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top