【问题标题】:Fail Read Int64 value from binary file created by C++从 C++ 创建的二进制文件中读取 Int64 值失败
【发布时间】:2012-10-06 21:11:21
【问题描述】:

我正在开发一个 C# CE 应用程序来从 C++ 程序创建的二进制文件中读取数据以进行项目验证。

下面是C++程序的编码..

 // File Name: Ean2an.bin which is created by struct 
    struct EAN2AN_TYPE 
    {
    __int64     ean:40;     // 5 bytes, up to 12 digits
    __int64     rec_no:24;  // 3 bytes rec no in the c_ItemMaster File, up to 16 million  records
    };

    // After bind data to struct, wil create the binary file
   bool CreateBin_EAN2AN_TYPE()
   {
    if(mn_RecordCount_EAN2AN_TYPE == 0) return false;

    FILE   *binfile;

    qsort(mc_EAN2AN_TYPE, mn_RecordCount_EAN2AN_TYPE, sizeof(struct EAN2AN_TYPE), qsort_EAN2AN_TYPE);
    try
    {
        binfile = fopen(ms_Path_EAN2AN_TYPE, "wb");
        fwrite(&mc_EAN2AN_TYPE, sizeof(struct EAN2AN_TYPE), mn_RecordCount_EAN2AN_TYPE, binfile);
    }
    catch(Exception ^ex)
    {
        TaskProgramLibrary::Message::ERR("Create EAN2AN_TYPE.bin fail!\r\n       " + ex->Message);
    }
    finally
    {
        fclose(binfile);

        mdw_FileSize_EAN2AN_TYPE = FileSize(ms_Path_EAN2AN_TYPE);
    }

    return true;
      }

我尝试使用二进制读取(基于位置)读取数据并使用bitconverter转换为int64或使用Marshal.PtrToStructure,但返回值不正确。然后我尝试从文件中读取 5 个字节而不是 8 个字节,但返回的值仍然不正确。

Below is the written C# coding
    //Struct created in C#
   [StructLayout(LayoutKind.Sequential)]
        public struct EAN2AN_TYPE
        {
            [MarshalAs(UnmanagedType.I8)]
            public Int64 ean;
            [MarshalAs(UnmanagedType.I8)]
            public Int64 rec_no;
        } 

    //The ways i tried to read in C#
    //1.Read Int64 by Binary 
    private void ReadByBinary()
         {
            using (BinaryReader b = new BinaryReader(_fs))
            {
                while (b.PeekChar() != 0)
                {
                    Int64 x = b.ReadInt64();
                    Console.WriteLine(x.ToString());

                }
            }

        }

   //2.Using Marshal to convert the Intptr to struct's field type
    private object ReadByMarshal(Type iType)
        {
            _oType = iType;// typeof(System.Int64);
            byte[] buffer = new byte[Marshal.SizeOf(_oType)];
            //byte[] buffer = new byte[5];

            object oReturn = null;

            try
            {
                _fs.Read(buffer, 0, buffer.Length);

                GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                oReturn = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), _oType);
                handle.Free();

                return oReturn;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }


    //3. Use Binary and use bit converter to convert to Int64
     private void ReadByBinaryAndUseBitConverter()
        {
            using (BinaryReader b = new BinaryReader(_fs))
            {
                byte[] x = b.ReadBytes(8);
                Int64 y = BitConverter.ToInt64(x, 0);
                Console.WriteLine(y);

                byte[] x2 = b.ReadBytes(8);
                Int64 y2 = BitConverter.ToInt64(x2,0);
                Console.WriteLine(y2);
            }

        }


    //4. Use Marshal and convert to struct
    public EAN2AN_TYPE  GetStructValue()
        {

            byte[] buffer = new byte[Marshal.SizeOf(typeof(EAN2AN_TYPE)];

            EAN2AN_TYPE oReturn = new EAN2AN_TYPE();

            try
            {
                //if (EOF) return null;

                _fs.Read(buffer, 0, buffer.Length);
                GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                IntPtr rawDataPtr = handle.AddrOfPinnedObject();

               oReturn = (EAN2AN_TYPE)Marshal.PtrToStructure(rawDataPtr, typeof(EAN2AN_TYPE));

                handle.Free();

                if (_fs.Position >= _fs.Length)
                    Close();

                return oReturn;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

编辑:上传二进制文件的图像

编辑:C#程序读取的前8个字节值

编辑器显示的二进制数据

有人知道吗?

提前致谢

【问题讨论】:

  • 你期望什么值,你得到了什么不正确的值?您是否使用十六进制编辑器查看文件以查看文件是否包含正确的值?
  • 看看下面我的回答。我有一个名为“传入”的变量。以相同格式向我们展示您的 实际 数据的 8 个字节是什么,并告诉我们您希望从这 8 个字节中获得的 eanrec-no 值是什么。
  • 在文本编辑器中打开的二进制文件的屏幕截图几乎没有用。您正在尝试反序列化 8 个字节。向我们展示这 8 个字节,仅显示这 8 个字节,按照它们在文件中的顺序,最好是十六进制。

标签: c# binary windows-ce


【解决方案1】:

ean 定义为 40 位实体,rec_no 是 24 位,使得整个结构只有 64 位。您对EAN2AN_TYPE 的定义是128 位,所以显然会有问题。我质疑编写初始代码的人的理智,但你的工作是取回并使用它,所以你可以玩你所处理的。

编辑:更新为使用您指定的数据并考虑到 Ben 的投诉

这里有两种方法可以得到相同的结果。一个更容易理解,因为它是逐步移动的,另一个更快且“更正确”。我将您的示例 EAN 数据放入我的输入中以验证结果。

public struct EAN2AN_TYPE
{
    public long ean; // can hold 5 bytes
    public int rec_no; // can hold 3 bytes
}

byte[] incoming = new byte[] { 0x6F, 0x5D, 0x7C, 0xBA, 0xE3, 0x06, 0x07, 0x08 };

内存复制:

using(var stream = new MemoryStream(incoming))
using (var reader = new BinaryReader(stream))
{
    // I leave it to you to get to the data
    stream.Seek(0, SeekOrigin.Begin);

    // get the data, padded to where we need for endianness
    var ean_bytes = new byte[8];
    // read the first 5 bytes
    Buffer.BlockCopy(reader.ReadBytes(5), 0, ean_bytes, 0, 5);
    var rec_no_bytes = new byte[4];
    // read the last 3
    Buffer.BlockCopy(reader.ReadBytes(3), 0, rec_no_bytes, 0, 3);

    var ean2 = new EAN2AN_TYPE();

    // convert
    ean2.ean = BitConverter.ToInt64(ean_bytes, 0);
    ean2.rec_no = BitConverter.ToInt32(rec_no_bytes, 0);
}

位移:

using (var stream = new MemoryStream(incoming))
using (var reader = new BinaryReader(stream))
{
    // I leave it to you to get to the data
    stream.Seek(0, SeekOrigin.Begin);

    // get the data
    var data = BitConverter.ToUInt64(reader.ReadBytes(8), 0);
    var ean2 = new EAN2AN_TYPE();

    // shift into our data
    ean2.ean = (long)(data & ~0xFFFFFF0000000000);
    ean2.rec_no = (int)(data >> 40);
}

当然,您可以将 EAN2AN_TYPE 设为一个类,以 8 个字节提供它,然后让属性访问器也为您执行转换恶作剧。如果这必须是双向的事情(即您需要将数据放入其中一个结构中以发送回 C 应用程序),我会这样做。

【讨论】:

  • 呸呸呸呸。位移运算符是在没有它们的语言中模拟位域的正确方法。
  • @Ben:同意,但如果您是编程新手,我认为我的第一个示例更清楚实际发生的情况(是的,性能不是那么好,它会产生垃圾)。
【解决方案2】:

这可能是数据的字节顺序(如果这是一个词)有问题。如果数据是在大端系统上写入的,而在小端系统上读取时,您会遇到这样的问题,反之亦然。

另一个问题是这两个字段实际上被打包成一个 64 位值。您可能需要读取 Int64,然后使用位操作来提取这两个字段。您的所有代码似乎都在通过各种方式读取两个 Int64 值。

【讨论】:

    【解决方案3】:

    感谢您的回复..

    最初的 C++ 代码由供应商编写。我只能通过阅读 C++ 代码来尝试理解。据我了解..它只是创建二进制文件并写入数据.. 我无法从代码中找到任何编码/转换部分..

    我尝试通过代码手动将第一个 ean(978086288751) 转换为 byte[]。 byte[] 是 111 93 124 186 227 0 0 0 这与我得到的结果不同..

    我已经测试了 ctacke 建议的代码。但我仍然无法获得正确的 ean..

    下面是编码..(我在文件流中添加了读取二进制文件)

     using (FileStream fileStream = File.OpenRead(_File))
                {
                    MemoryStream memStream = new MemoryStream();
                    memStream.SetLength(fileStream.Length);
                    fileStream.Read(memStream.GetBuffer(), 0, (int)fileStream.Length);
    
                    using (BinaryReader reader = new BinaryReader(memStream))
                    {
                        //stream.SetLength(_fs);               
                        // I leave it to you to get to the data
                        memStream.Seek(0, SeekOrigin.Begin);
    
                        // get the data, padded to where we need for endianness
                        byte[] ean_bytes = new byte[8];
                        // if this is wrong - then change param 4 to '3' to align at the other end
                        Buffer.BlockCopy(reader.ReadBytes(8), 0, ean_bytes, 0, 8);
                        //byte[] rec_no_bytes = new byte[4];
    
                        byte[] rec_no_bytes = new byte[4];
                        // if this is wrong - then change param 4 to '1' to align at the other end
                        Buffer.BlockCopy(reader.ReadBytes(3), 0, rec_no_bytes, 0, 3);
    
                        EAN2AN_TYPE ean2 = new EAN2AN_TYPE();
    
                        // convert
                        ean2.ean = BitConverter.ToInt64(ean_bytes, 0);
                        ean2.rec_no = BitConverter.ToInt32(rec_no_bytes, 0);
                    }
    
                }
    

    //结果 读取 5 个字节:17 0 0 0 0 平均数:17

    //我改成

       var ean_bytes = new byte[8];
        Buffer.BlockCopy(reader.ReadBytes(8), 0, ean_bytes, 0, 8);
    

    结果 读取 8 个字节:17 0 0 0 0 108 94 5 恩:386865365256241169

    很抱歉,我仍然是新用户.. 无法发布任何附件.. 希望你能从我的解释中理解。

    【讨论】:

    • 这应该是对问题的更新,因为它只是增加了清晰度,但没有提供实际的答案。
    • 是的,我更新了我的问题..以及我尝试了您的建议代码后的结果。我仍然无法获得正确的 ean =( 我错过了编码中的任何内容吗?
    • 告诉我你在这 8 个字节中拥有的 exact 数据,以及 exactly 你对结果的期望。字节序可能在起作用,或者期望数据是连续字节,而不是存储为实际数字。
    • 从 bin 文件中读取的一些确切结果:Ean No 978086288751 Rec No 162010 Ean No 497185017195 Rec No 196711 Ean No 200000035773 Rec No 421407 Ean No 71881353183 Rec No 517 无法上传任何 m4文件在这里,不能复制粘贴二进制文件内容(由于很多/0)..如果你不介意..我可以通过电子邮件给你发送bin文件吗?
    猜你喜欢
    • 1970-01-01
    • 2021-09-07
    • 1970-01-01
    • 2023-03-13
    • 2021-12-24
    • 2011-05-08
    • 1970-01-01
    • 2017-10-01
    • 2011-09-03
    相关资源
    最近更新 更多