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

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

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

我不想将字节转换为字符,原因如下:

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

所以我不能使用正则表达式 。

我所知道的唯一解决方案是使用Boost.Regex (它在字节上工作–C字符),但这是一个C ++库,使用C ++ / CLI进行包装将需要相当多的工作。

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

谢谢。

这里有一些阻抗不匹配。 您希望使用.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); } } 

使用这种技术,你可以分配一个字符串“buffer”,它可以重新用作Regex的输入,但你可以每次用你的字节改变它。 这样可以避免每次要进行匹配时将\编码字节数组转换为新的.Net字符串的开销。 这可能certificate是非常重要的,因为我已经看到.Net中的许多算法试图以每小时一百万英里的速度通过字符串生成以及随后的堆垃圾邮件和在GC中花费的时间来降低。

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

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

更新

实际上在反汇编Regex \ Match \ Group \ Capture后,看起来它只在访问Value属性时生成捕获的字符串,因此如果只访问索引和长度属性,则至少不能生成字符串。 但是,您将生成所有支持的Regex对象。

好吧,如果我遇到这个问题,我会做C ++ / CLI包装器,除了我为我想要实现的目标创建专门的代码。 最终用时间开发包装器做一般事情,但这只是一个选项。

第一步是仅包装Boost :: Regex输入和输出。 在C ++中创建专门的函数来执行您想要的所有操作,并使用CLI将输入数据传递给C ++代码,然后使用CLI获取结果。 这对我来说并不像是要做太多工作。

更新:

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

  1. 使用包装器只需要转换指针类型(bytes < - > chars)。
  2. 使用.NET的Regex,您必须转换输入的每个字节。

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

 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; } } 

你可以通过多种方式提高效率(即寻找第一个字节匹配而不是迭代等),但这可以让你开始...希望它有所帮助。

我个人采用了不同的方法,编写了一个可以扩展的小型状态机。 我相信如果解析协议数据,这比正则表达式更具可读性。

 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; }