代码之家  ›  专栏  ›  技术社区  ›  brickner

字节而不是字符上的.NET正则表达式

  •  15
  • brickner  · 技术社区  · 14 年前

    我正在尝试使用正则表达式进行一些更容易的解析。

    输入是字节数组(或枚举)。

    1. 计算效率
    2. 内存消耗效率
    3. 某些不可打印的字节可能很难转换为字符。并非所有的字节都是可打印的。

    Regex .

    Boost.Regex (它在字节-C字符上工作),但这是一个C++库,用C++或CLI包装将需要大量的工作。

    如何直接在.NET中的字节上使用正则表达式,而不使用.NET字符串和字符?

    4 回复  |  直到 14 年前
        1
  •  8
  •   Robert Simmons    14 年前

    这里有点阻抗不匹配。您希望使用.Net中使用字符串(多字节字符)的正则表达式,但希望使用单字节字符。不能像往常一样同时使用.Net。

    但是,要打破这种不匹配,可以以面向字节的方式处理字符串并对其进行变异。变异后的字符串可以作为一个可重用的缓冲区。这样,您就不必将字节转换为字符,或将输入缓冲区转换为字符串(根据您的问题)。

    举个例子:

    //BLING
    byte[] inputBuffer = { 66, 76, 73, 78, 71 };
    
    string stringBuffer = new string('\0', 1000);
    
    Regex regex = new Regex("ING", RegexOptions.Compiled);
    
    unsafe
    {
        fixed (char* charArray = stringBuffer)
        {
            byte* buffer = (byte*)(charArray);
    
            //Hard-coded example of string mutation, in practice you would
            //loop over your input buffers and regex\match so that the string
            //buffer is re-used.
    
            buffer[0] = inputBuffer[0];
            buffer[2] = inputBuffer[1];
            buffer[4] = inputBuffer[2];
            buffer[6] = inputBuffer[3];
            buffer[8] = inputBuffer[4];
    
            Console.WriteLine("Mutated string:'{0}'.",
                 stringBuffer.Substring(0, inputBuffer.Length));
    
            Match match = regex.Match(stringBuffer, 0, inputBuffer.Length);
    
            Console.WriteLine("Position:{0} Length:{1}.", match.Index, match.Length);
        }
    }
    

    显然这是不安全的代码,但它是.Net。

    正则表达式的结果将生成字符串,所以这里有一个问题。我不确定是否有一种使用Regex的方法不会生成新字符串。当然可以获得匹配索引和长度信息,但是字符串的生成违反了内存效率的要求。

    更新

        2
  •  2
  •   Aoi Karasu    14 年前

    如果我面对这个问题,我会做C++ + CLI包装器,除非我为我想要的东西创建专门的代码。最终随着时间的推移开发包装器来做一般的事情,但这只是一个选择。

    第一步是仅包装Boost::Regex输入和输出。在C++中创建专门的函数,它可以完成所有想要的东西,并使用CLI将输入数据传递给C++代码,然后用CLI返回结果。在我看来,这不是太多的工作要做。

    更新:

    让我试着澄清我的观点。即使我可能错了,我相信你也找不到 .NET二进制正则表达式 可以使用的实现。这就是为什么-不管您喜欢与否-您将被迫在CLI包装和字节到字符转换之间进行选择,以使用.NET的Regex。在我看来,包装机是更好的选择,因为它会工作得更快。我没有做任何基准测试,这只是基于以下假设:

    1. 使用包装,你只需投
    2. 使用.NET的正则表达式 转换输入的每个字节。
        3
  •  1
  •   Daeron Lockett    12 年前

    作为使用unsafe的替代方法,只需考虑编写一个简单的递归比较器,如:

    static bool Evaluate(byte[] data, byte[] sequence, int dataIndex=0, int sequenceIndex=0)
    {
           if (sequence[sequenceIndex] == data[dataIndex])
           {
               if (sequenceIndex == sequence.Length - 1)
                   return true;
               else if (dataIndex == data.Length - 1)
                   return false;
               else
                   return Evaluate(data, sequence, dataIndex + 1, sequenceIndex + 1);
           }
           else
           {
               if (dataIndex < data.Length - 1)
                   return Evaluate(data, sequence, dataIndex+1, 0);
               else
                   return false;
           }
    }
    

        4
  •  0
  •   rollsch    8 年前

    我个人采用了另一种方法,编写了一个可以扩展的小型状态机。我相信,如果解析协议数据,这比regex可读得多。

    bool ParseUDSResponse(PassThruMsg rxMsg, UDScmd.Mode txMode, byte txSubFunction, out UDScmd.Response functionResponse, out byte[] payload)
    {
        payload = new byte[0];
        functionResponse = UDScmd.Response.UNKNOWN;
        bool positiveReponse = false;
        var rxMsgBytes = rxMsg.GetBytes();
    
        //Iterate the reply bytes to find the echod ECU index, response code, function response and payload data if there is any
        //If we could use some kind of HEX regex this would be a bit neater
        //Iterate until we get past any and all null padding
        int stateMachine = 0;
        for (int i = 0; i < rxMsgBytes.Length; i++)
        {
            switch (stateMachine)
            {
                case 0:
                    if (rxMsgBytes[i] == 0x07) stateMachine = 1;
                    break;
                case 1:
                    if (rxMsgBytes[i] == 0xE8) stateMachine = 2;
                    else return false;
                case 2:
                    if (rxMsgBytes[i] == (byte)txMode + (byte)OBDcmd.Reponse.SUCCESS)
                    {
                        //Positive response to the requested mode
                        positiveReponse = true;
                    }
                    else if(rxMsgBytes[i] != (byte)OBDcmd.Reponse.NEGATIVE_RESPONSE)
                    {
                        //This is an invalid response, give up now
                        return false;
                    }
                    stateMachine = 3;
                    break;
                case 3:
                    functionResponse = (UDScmd.Response)rxMsgBytes[i];
                    if (positiveReponse && rxMsgBytes[i] == txSubFunction)
                    {
                        //We have a positive response and a positive subfunction code (subfunction is reflected)
                        int payloadLength = rxMsgBytes.Length - i;
                        if(payloadLength > 0)
                        {
                            payload = new byte[payloadLength];
                            Array.Copy(rxMsgBytes, i, payload, 0, payloadLength);
                        }
                        return true;
                    } else
                    {
                        //We had a positive response but a negative subfunction error
                        //we return the function error code so it can be relayed
                        return false;
                    }
                default:
                    return false;
            }
        }
        return false;
    }