2010-06-12 8 views
13

Estoy tratando de hacer algunos análisis que serán más fáciles usando expresiones regulares..NET Expresiones regulares en bytes en lugar de caracteres

La entrada es una matriz (o enumeración) de bytes.

no quiero convertir los bytes de caracteres por las siguientes razones:

  1. eficiencia Computación
  2. eficiencia en el consumo de memoria
  3. algunos bytes no imprimibles pueden ser complejos para convertir a caracteres. No todos los bytes son imprimibles.

Así que no puedo usar Regex.

La única solución que conozco es usar Boost.Regex (que funciona en bytes -C caracteres), pero esta es una biblioteca de C++ que envolver usando C++/CLI requerirá un trabajo considerable.

¿Cómo puedo usar expresiones regulares en bytes en .NET directamente, sin trabajar con .NET strings y chars?

Gracias.

+1

¿Puede proporcionar un ejemplo de un patrón RegEx que está intentando hacer coincidir? –

+1

@Nate Dudek, no veo cómo es relevante. Estoy buscando una biblioteca general de expresiones regulares ya que estoy tratando de analizar protocolos de Internet complejos. No es una expresión regular específica. Si realmente necesita un ejemplo, consulte http://www.ietf.org/rfc/rfc2616.txt para ver las expresiones regulares allí (también hace referencia a otros archivos rfcs) – brickner

+2

En realidad, .NET no admite entradas binarias para expresiones regulares. Entiendo que no desea perder tiempo de computación en la conversión, pero si su único temor es tener caracteres especiales, puede convertir los bytes en cadenas sin problemas ya que la cadena .NET está codificada con Unicode UTF-16. Todos los personajes serán compatibles. – Ucodia

Respuesta

7

Aquí hay un poco de desajuste de impedancia. Desea trabajar con expresiones regulares en .Net que utilizan cadenas (caracteres de varios bytes), pero desea trabajar con caracteres de un solo byte. No puede tener ambos al mismo tiempo usando .Net como de costumbre.

Sin embargo, para eliminar esta falta de coincidencia, podría tratar una cadena de forma orientada a bytes y mutarla. La cadena mutada puede actuar como un buffer reutilizable. De esta forma, no tendrá que convertir bytes en caracteres, o convertir su búfer de entrada en una cadena (según su pregunta).

Un ejemplo:

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

Usando esta técnica se puede asignar una cadena "buffer" que puede ser reutilizado como entrada de expresiones regulares, pero se puede mutar con sus bytes cada vez. Esto evita la sobrecarga de convertir \ codificación de su matriz de bytes en una nueva cadena .Net cada vez que desea hacer una coincidencia. Esto podría ser muy significativo ya que he visto que muchos algoritmos en .Net intentan ir a un millón de millas por hora solo para que se pongan de rodillas por la generación de cadenas y el posterior montón de spam y el tiempo pasado en GC.

Obviamente este es un código inseguro, pero es .Net.

Los resultados de la Regex generarán cadenas, por lo que tiene un problema aquí. No estoy seguro de si hay alguna forma de usar Regex que no genere nuevas cadenas. Sin duda, puede obtener información sobre el índice y la duración del partido, pero la generación de cadenas infringe sus requisitos de eficiencia de la memoria.

actualización

en realidad después de desmontar expresión regular \ partido \ Grupo \ Capture, que parece que sólo genera la cadena capturado cuando acceda a la propiedad Value, por lo que es posible que al menos no estar generando cadenas si solo acceder a las propiedades de índice y longitud. Sin embargo, generará todos los objetos Regex de soporte.

+0

Su solución parece funcionar cuando la entrada es una cadena. Mi entrada es bytes, que no quiero convertir a una cadena.No estoy seguro de por qué no puedo ambas cosas: si alguien ya ha envuelto Boost.Regex con C++/CLI, puedo tener ambas. – brickner

+0

Sí, lo que sugiero es que use la cadena como un búfer y la mute con sus bytes. Donde estoy mutando, usarías tu buffer de byte aquí. Este es solo un ejemplo básico de mutación de una cadena, pero significa que puede tener un búfer de cadena reutilizable para la entrada de expresiones regulares que usted mute con su entrada. –

+0

Buena idea. Pero esto significa que cada vez que tengo un nuevo conjunto de bytes, tendré que copiarlos a la matriz de caracteres. Esta parece ser la misma solución que convertir los bytes en una cadena usando Codificación, pero con la reutilización de cadenas (que puede ser problemático cuando se trata de límites de tamaño y si quiero usar dos entradas en paralelo). – brickner

2

Bueno, si tuviera que enfrentar este problema, HAGA el contenedor C++/CLI, excepto que crearía un código especializado para lo que quiero lograr. Eventualmente desarrolle el contenedor con tiempo para hacer cosas generales, pero esto solo es una opción.

El primer paso es ajustar la entrada y salida de Boost :: Regex solamente. Cree funciones especializadas en C++ que hagan todo lo que desee y use CLI solo para pasar los datos de entrada al código C++ y luego recuperar el resultado con la CLI. Esto no me parece demasiado trabajo para hacer.

Actualización:

Voy a tratar de aclarar mi punto. Aunque pueda estar equivocado, creo que no podrá encontrar ninguna implementación .NET Binary Regex que pueda usar. Es por eso que, te guste o no, te verás obligado a elegir entre la envoltura de la CLI y la conversión de bytes a caracteres para usar Regex de .NET. En mi opinión, la envoltura es una mejor opción, porque funcionará más rápido. No he hecho ninguna evaluación comparativa, esto es sólo una suposición basada en:

  1. Usando envoltorio sólo hay que echar el tipo de puntero (bytes < -> caracteres).
  2. Usando .NET's Regex tiene que convertir cada byte de la entrada.
+2

De hecho, sé exactamente cómo envolverlo, pero estoy buscando una solución que no tenga que escribir yo mismo. – brickner

1

Como una alternativa al uso inseguro, solo que no hacer un simple comparador, recursiva como:

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

podría mejorar la eficiencia en un número de formas (es decir, buscando el primer partido de bytes en lugar de la iteración, etc.) pero esto podría comenzar ... espero que ayude.

0

Personalmente fui a un enfoque diferente y escribí una pequeña máquina de estado que se puede ampliar. Creo que si se analizan los datos de protocolo esto es mucho más legible que 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; 
} 
Cuestiones relacionadas