c#-用c#重写python的struct包-简化版

博客介绍了如何在C#中实现类似Python `struct`模块的功能,用于处理ABI文件的数据提取。作者提供了`StructHelper`类,包含了`CalcSize`方法来计算格式字符串所占字节数,以及`Unpack`方法来解码字节数组。代码实现了大端模式下的数据转换,包括`char`, `ushort`, `uint`等类型,并计划进一步完善`pack`函数和代码。
摘要由CSDN通过智能技术生成

最近我在搞abi文件的解析工作,本来是用Python3写的数据提取和绘图(主要涉及struct、Biopython、matplotlib等包),但是如果想把脚本放到wpf项目中,试了几种方法,效果都不太好。所以我干脆用c#重写。

本文写的是struct包的unpack函数和calcsize函数的c#简单实现,以后会再加入pack函数并改进代码。

准备知识

python3的struct包,介绍看这里: https://docs.python.org/3/library/struct.html

举个例子:

_HEADFMT = ">H4sI2H3I"

这个是ABI文件中header的编码格式。其中:
1.> 表示大端,H是unsigned short,占2字节,s是char,占1字节,I是unsigned int,占4字节。

buffer = struct.pack(">ihb", 1, 2, 3)
# 大端模式,buffer为:(1在它所占4个字节的低端)
# b'\x00\x00\x00\x01\x00\x02\x03'
# 小端模式,buffer为:(1在它所占4个字节的高端)
buffer = struct.pack("<ihb", 1, 2, 3)
# b'\x01\x00\x00\x00\x02\x00\x03'

2.4s表示s重复四次,所以展开其实是 HssssIHHIII,所以一共占26个字节

由于abi文件中涉及的格式有限,所以我只给出一部分,如有需要可以自行补全。

简单实现

public class StructHelper {
    // 不同数据类型对应的字节数目
    public static Dictionary<char, int> charSizeDict = new Dictionary<char, int> {
        {'c', 1},{'b', 1}, {'B', 1}, {'?', 1}, {'p', 1}, {'s', 1}, 
        {'e', 2}, {'h', 2}, {'H', 2},
        {'I', 4}, {'i', 4}, {'l', 4}, {'L', 4}, {'f', 4},
        {'q', 8}, {'Q', 8}, {'d', 8}
    }; 
    
    /// <summary>
    /// 计算符合该格式的字符串的总长度
    /// </summary>
    /// <param name="s">格式</param>
    /// <returns></returns>
    public int CalcSize(string s) {
        int result = 0;
        // 不考虑little-endian或big-endian的区别
        int i = 1;
        Regex regex = new Regex("[0-9]");
        string num = "";
        while (i < s.Length) {
            if (regex.IsMatch(s.Substring(i, 1))) // 是数字
            {
                num += s[i];
            }
            else {
                if (num.Length == 0)
                {
                    result += charSizeDict[s[i]];
                }
                else {
                    int count = int.Parse(num);
                    result += charSizeDict[s[i]] * count;
                    num = "";
                }
            }
            i += 1;
        }
        return result;
    }
    
    /// <summary>
    /// 按类型将byte[]转为string
    /// </summary>
    /// <param name="type">类型:s/H/I/h/b/B/f</param>
    /// <param name="raw">byte[]</param>
    /// <returns></returns>
    private string UnpackHelper(char type, byte[] raw) {
        // 小端模式要翻转
        if (BitConverter.IsLittleEndian)
            Array.Reverse(raw);
        string result = "";
        switch (type) {
            case 's':
                byte[] raw0 = new byte[2];
                if (BitConverter.IsLittleEndian)
                {
                    raw0[0] = raw[0];
                    raw0[1] = 0;
                }
                else {
                    raw0[0] = 0;
                    raw0[1] = raw[0];
                }
                result = BitConverter.ToChar(raw0,0).ToString();
                // 如果需要转ascii也可以下面这样
                //result = System.Text.Encoding.ASCII.GetString(raw);
                break;
            case 'H':
                byte[] raw2 = new byte[8];
                // BitConverter.ToInt32是转为32位有符号整数,对于可能存在的4294967295这种数值会溢出
                // 所以使用ToInt64。但是这需要补足8字节
                if (BitConverter.IsLittleEndian)
                {
                    raw2[0] = raw[0];
                    raw2[1] = raw[1];
                    raw2[2] = 0;
                    raw2[3] = 0;
                    raw2[4] = 0;
                    raw2[5] = 0;
                    raw2[6] = 0;
                    raw2[7] = 0;
                }
                else {
                    raw2[0] = 0;
                    raw2[1] = 0;
                    raw2[2] = 0;
                    raw2[3] = 0;
                    raw2[4] = 0;
                    raw2[5] = 0;
                    raw2[6] = raw[0];
                    raw2[7] = raw[1];
                }
                result = BitConverter.ToInt64(raw2, 0).ToString();
                break;
            ...
            case 'h': // short 2 bytes
                result = BitConverter.ToUInt16(raw, 0).ToString();
                break;
            case 'f': // float, 4 bytes
            	// 这个注意不要用ToDouble
                result = BitConverter.ToSingle(raw, 0).ToString(); 
                break;
            default:
                break;
        }
        return result;
    }
    
    /// <summary>
    /// 解码byte[]
    /// </summary>
    /// <param name="format">格式</param>
    /// <param name="buffer">byte[]</param>
    /// <returns></returns>
    public List<string> Unpack(string format, byte[] buffer) {
        List<string> result = new List<string>();
        char first = format[0];
        bool isBigEndian = false;
        if (first.Equals('>') || first.Equals('!')) {
            isBigEndian = true;
        }
        Regex regex = new Regex("[0-9]");
        string num = "";
        int pos = 0;
        // 暂时不考虑小端
        if (isBigEndian) {
            int i = 1;
            while (i < format.Length) {
                char curChar = format[i];
                // 如果是数字
                if (regex.IsMatch(format.Substring(i, 1)))
                {
                    num += curChar;
                }
                else
                {
                    if (charSizeDict.ContainsKey(curChar) == false) {
                        Console.WriteLine("curChar: " + curChar);
                    }
                    int singleSize = charSizeDict[curChar];
                    // 单一字符
                    if (num.Length == 0)
                    {
                        // 构建byte[]
                        byte[] raw = new byte[singleSize];
                        int k = 0;
                        for (int j = pos; j < pos + singleSize; j++) {
                            raw[k] = buffer[j];
                            k += 1;
                        }
                        // 计算结果
                        result.Add(UnpackHelper(curChar, raw));
                        // 移动buffer中的位置指针
                        pos += charSizeDict[curChar];
                    }
                    else 
                    {
                        int count = int.Parse(num);
                        string tmp = "";
                        for (int m = 0; m < count; m++)
                        {
                            byte[] raw = new byte[singleSize];
                            int k = 0;
                            for (int j = pos; j < pos + singleSize; j++)
                            {
                                raw[k] = buffer[j];
                                k += 1;
                            }
                            // I和H是各自分开的
                            if (curChar != 's')
                            {
                                result.Add(UnpackHelper(curChar, raw));
                            }
                            else { // s的话是要count个合为一个的
                                tmp += UnpackHelper(curChar, raw);
                            }
                            pos += charSizeDict[curChar];
                        }
                        if (tmp.Length > 0) {
                            result.Add(tmp);
                            //tmp = "";
                        }
                        // 清空num
                        num = "";
                    }
                }
                // 移动format中的指针
                i += 1;
            }
        }
        return result;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值