ما هي الطريقة الأكثر فعالية لحشد البنيات C ++ إلى C #؟

StackOverflow https://stackoverflow.com/questions/878073

سؤال

وأنا على وشك أن تبدأ طن قراءة الملفات الثنائية، ولكل منها 1000 أو أكثر من السجلات. يتم إضافة ملفات جديدة باستمرار حتى أنا أكتب خدمة ويندوز لمراقبة الدلائل ومعالجة ملفات جديدة ورودها. تم إنشاء الملفات مع برنامج ج ++. لقد صوغه التعاريف البنية في C #، ويمكن قراءة غرامة البيانات، ولكن أنا قلق من أن الطريق أنا أفعل ذلك سيقتل في نهاية المطاف طلبي.

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 */
    }
}

وأنا لا أعتقد أنني بحاجة إلى استخدام GCHandle لأنني لست التواصل فعلا مع التطبيق C ++، يجري كل شيء من التعليمات البرمجية المدارة، ولكن أنا لا أعرف من وسيلة بديلة.

هل كانت مفيدة؟

المحلول

لطلبك خاص، وشيء واحد فقط تعطيك إجابة شافية: التعريف به.

وأن يقال هنا هي الدروس التي تعلمتها أثناء العمل مع حلول PInvoke كبيرة. الطريقة الأكثر فعالية لحشد البيانات هي تنظيم المجالات التي هي blittable. يمكن أن يعني CLR القيام بسيطة ما يرقى إلى memcpy لنقل البيانات بين التعليمات البرمجية الأصلية وإدارتها. بعبارات بسيطة، وعلى كل من المصفوفات غير مضمنة وسلاسل من الهياكل الخاصة بك. إذا كانت موجودة في الهيكل الأصلي، تمثيلهم مع IntPtr وحشد القيم على الطلب في التعليمات البرمجية المدارة.

وأنا لم محة من أي وقت مضى الفرق بين استخدام Marshal.PtrToStructure مقابل وجود API الأصلي dereference القيمة. وربما هذا هو ما يجب أن تستثمر في أن يتم الكشف PtrToStructure بمثابة عنق الزجاجة من خلال التنميط.

لالتسلسلات الهرمية مارشال كبير على الطلب مقابل سحب بنية بأكمله في التعليمات البرمجية المدارة في وقت واحد. لقد واجهت هذه المسألة أكثر عند التعامل مع الهياكل شجرة كبيرة. حشد عقدة الفردية سريع جدا اذا كان blittable والأداء الحكمة يعمل بها لحشد ما تحتاج إليه فقط في تلك اللحظة.

نصائح أخرى

وعن طريق Marshal.PtrToStructure بطيئة نوعا ما. لقد وجدت المقالة التالية على CodeProject الذي يقارن (والقياس) طرق مختلفة لقراءة البيانات الثنائية مفيدة جدا:

<اقتباس فقرة>   

سريع ملف ثنائي قراءة مع C #

وبالإضافة إلى JaredPar في إجابة شاملة، لا تحتاج إلى استخدام GCHandle، يمكنك استخدام رمز غير آمنة بدلا من ذلك.

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

والغرض كله من البيان GCHandle / fixed هو دبوس / إصلاح مقطع الذاكرة معين، مما يجعل الذاكرة غير المنقولة من وجهة GC وجهة نظر. إذا كانت الذاكرة المنقولة، فإن أي انتقال تجعل المؤشرات الخاصة بك غير صالحة.

وغير متأكد من طريقة أسرع على الرغم من.

وهذا قد يكون خارج حدود لسؤالك، ولكن أود أن يميل لكتابة القليل من التجمع في إدارتها C ++ التي لم على fread () أو شيء سريع على غرار قراءة في البنيات. مرة واحدة كنت قد حصلت عليها قراءة في، يمكنك استخدام C # لتفعل كل شيء آخر تحتاج معهم.

وهنا فئة صغيرة أدليت به من حين الى الوراء بينما كان يلعب مع الملفات المهيكلة. كان أسرع طريقة يمكنني أن معرفة في الوقت خجولة من الذهاب غير آمنة (الذي كان ما كنت أحاول أن تحل محل والحفاظ على الأداء مقارنة).

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

والاستخدام:

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

و(جديد جدا هنا، ونأمل أن لم يكن أكثر من اللازم للنشر ... بلصقه في الصف، لم يقطع من التعليقات أو أي شيء لتقصيره.)

ويبدو أن هذا لا علاقة له لا C ++ ولا التنظيم شيئا. كنت تعرف بنية ماذا لا تحتاج إليه.

من الواضح أنك بحاجة إلى رمز بسيط الذي سيقرأ مجموعة من وحدات البايت تمثل البنية واحد ثم استخدام BitConverter لوضع بايت في المقابلة C # المجالات ..

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top