我开始阅读吨的二进制文件,每1000个或多个记录。新的文件都在不断增加,所以我在写一Windows服务监测的目录和处理新的文件,因为他们收到。该文件被创建一个c++的程序。我已经重新结构定义在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之间的差异。这可能是你应该投资在应累得PtrToStructure所能发现通过剖析一个瓶颈。

有关需求与在单一时间拉动整个结构成托管代码大型层次结构编组。我已经运行到大的树结构的最打交道时这个问题。编组单个节点是非常快,如果它是blittable和性能明智它的作品了,只元帅,你需要在那一刻是什么。

其他提示

使用Marshal.PtrToStructure是相当缓慢的。我发现在CodeProject下面的文章被比较(和基准)的读取二进制数据非常有益的不同的方式:

  

快速二进制文件与C#

除了JaredPar的全面的答案,你不需要使用GCHandle,您可以使用不安全的代码来代替。

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

GCHandle / fixed语句的整个目的是PIN /修复的特定存储器段,使得存储器从视图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