2009-12-25 11 views
33

Estoy buscando una forma rápida y segura de aplicar expresiones regulares en las transmisiones.¿Aplicar un Regex en Stream?

Encontré algunos ejemplos en Internet que hablan de convertir cada búfer a String y luego aplicar el Regex en la cadena.

Este enfoque tiene dos problemas:

  • Rendimiento: convertir a las cadenas y las cuerdas GC'ing es pérdida de tiempo y de la CPU y seguro que puede ser evitado si hubiera una manera más nativa para aplicar Regex en Corrientes .
  • Compatibilidad con Regex puro: Regex el patrón a veces puede coincidir solo si se combinan dos almacenamientos intermedios (el búfer 1 finaliza con la primera parte del emparejamiento y el búfer 2 comienza con la segunda parte del emparejamiento). La forma de conversión a cadena no puede manejar este tipo de coincidencia de forma nativa, tengo que proporcionar más información como la longitud máxima que el patrón puede igualar, esto no admite los signos de expresión regular + y * y nunca será compatible (concordancia ilimitada longitud).

Por lo tanto, la forma de convertir a cadena no es rápida, y no es totalmente compatible con Regex.

¿Hay alguna forma/biblioteca que se pueda usar para aplicar Regex en transmisiones sin convertir a cadenas y con soporte Regex completo?

+0

¿Por qué no puedes esperar hasta recibir todos los datos? – ChaosPandion

+0

En mi experiencia, a menudo la expresión regular obstaculiza el rendimiento, no las cadenas de conversión y GC. A menos que su coincidencia sea muy compleja, le sugiero que cree su propio escáner de flujo para las coincidencias en lugar de Regex. Pero debe compararlo con el uso de una expresión regular para asegurarse de que está en el camino correcto. –

+2

@ChaosPandion: Si la transmisión es un archivo grande, no voy a cargar todas sus gigas en la memoria, especialmente no en utf-16 (codificación de cadena en la memoria en .net). Si el flujo proviene de Internet, quiero poder escanearlo antes de que se reciban todos los datos recibidos (IE HTML Parser, muestre las partes descargadas antes de que el resto de la página se descargue). – DxCK

Respuesta

2

Quizás este artículo podría ayudar? Aunque supongo que podrían ser las "cosas en Internet" que encontraste que no fueron de ayuda.

Building a Regular Expression Stream Search with the .NET Framework

+0

Esta solución no funciona en general. Si no conoce la longitud máxima de una coincidencia y la distancia mínima entre posibles coincidencias antes de llamar al constructor de StreamSearchExpression, no podrá encontrar todas las coincidencias (establecer la distancia mínima entre las posibles coincidencias en 1 hará que el algoritmo sea terriblemente ineficiente). Además, esta solución convierte el flujo en cadenas debajo del capó (de la longitud máxima de coincidencia pasada en el constructor), que es lo que psubsee2003 estaba tratando de evitar. – mheyman

+0

Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace de referencia. Las respuestas de solo enlace pueden dejar de ser válidas si la página vinculada cambia. - [De la crítica] (/ reseña/mensajes de baja calidad/18810911) –

0

Parece que usted sabe los delimitadores de inicio y finalización de los partidos que están tratando de conseguir, correcto? (es decir, [,] o INICIO, FIN, etc.). ¿Tendría sentido buscar estos delimitadores a medida que ingresen los datos de su flujo y luego crear una subcadena entre los delimitadores y hacer un procesamiento posterior en esos?

Sé que es más o menos lo mismo que rodar su propia, pero será con un propósito más específico e incluso ser capaz de procesar, ya que entra en acción.

El problema con expresiones regulares en este caso es que funcionan en función de las coincidencias, por lo que solo puede coincidir con la cantidad de información que tiene. Si tienes un flujo, deberías leer todos los datos para obtener todas las coincidencias (problema de restricción de espacio/tiempo), intentar hacer coincidir el personaje a la vez (bastante inútil), unir en trozos (nuevamente, algo puede pasar fácilmente por alto allí) o generar cadenas de interés que, si coinciden con sus criterios, pueden enviarse a otro lugar para su posterior procesamiento.

0

Puede agregar un método adicional a StreamReader (el código fuente de, p. Ej.Mono podría ser utilizado para ese propósito):

private StringBuilder lineBuilder; 
    public int RegexBufferSize 
    { 
     set { lastRegexMatchedLength = value; } 
     get { return lastRegexMatchedLength; } 
    } 
    private int lastRegexMatchedLength = 0; 

    public virtual string ReadRegex(Regex regex) 
    { 
     if (base_stream == null) 
      throw new ObjectDisposedException("StreamReader", "Cannot read from a closed RegexStreamReader"); 

     if (pos >= decoded_count && ReadBuffer() == 0) 
      return null; // EOF Reached 

     if (lineBuilder == null) 
      lineBuilder = new StringBuilder(); 
     else 
      lineBuilder.Length = 0; 

     lineBuilder.Append(decoded_buffer, pos, decoded_count - pos); 
     int bytesRead = ReadBuffer(); 

     bool dataTested = false; 
     while (bytesRead > 0) 
     { 
      var lineBuilderStartLen = lineBuilder.Length; 
      dataTested = false; 
      lineBuilder.Append(decoded_buffer, 0, bytesRead); 

      if (lineBuilder.Length >= lastRegexMatchedLength) 
      { 
       var currentBuf = lineBuilder.ToString(); 
       var match = regex.Match(currentBuf, 0, currentBuf.Length); 
       if (match.Success) 
       { 
        var offset = match.Index + match.Length; 
        pos = 0; 
        decoded_count = lineBuilder.Length - offset; 
        ensureMinDecodedBufLen(decoded_count); 
        lineBuilder.CopyTo(offset, decoded_buffer, 0, decoded_count); 
        var matchedString = currentBuf.Substring(match.Index, match.Length); 
        return matchedString; 
       } 
       else 
       { 
        lastRegexMatchedLength *= (int) 1.1; // allow for more space before attempting to match 
        dataTested = true; 
       } 
      } 

      bytesRead = ReadBuffer(); 
     } 

     // EOF reached 

     if (!dataTested) 
     { 
      var currentBuf = lineBuilder.ToString(); 
      var match = regex.Match(currentBuf, 0, currentBuf.Length); 
      if (match.Success) 
      { 
       var offset = match.Index + match.Length; 
       pos = 0; 
       decoded_count = lineBuilder.Length - offset; 
       ensureMinDecodedBufLen(decoded_count); 
       lineBuilder.CopyTo(offset, decoded_buffer, 0, decoded_count); 
       var matchedString = currentBuf.Substring(match.Index, match.Length); 
       return matchedString; 

      } 
     } 
     pos = decoded_count; 

     return null; 
    } 

En el método anterior, se usan las siguientes vars:

  1. decoded_buffer: la char buffer que contiene/contendrá los datos leídos
  2. pos : offset dentro de la matriz que contiene datos no controlados
  3. decoded_count: el último elemento dentro del búfer que contiene los datos de lectura
  4. RegexBufferSize: el tamaño mínimo de la entrada de expresiones regulares antes de una ny se produce la coincidencia.

El método ReadBuffer() necesita leer los datos de la secuencia. El método ensureMinDecodedBufLen() necesita asegurarse de que decoded_buffer sea lo suficientemente grande.

Al llamar al método, pase la Regex que debe coincidir.

4

Intel ha abierto recientemente la biblioteca hyperscan con licencia BSD. Se trata de un motor regex basado en NFA de alto rendimiento y sin retroceso.

Características: capacidad de trabajar en flujos de datos de entrada y coincidencia de patrones múltiple simultánea. El último difiere del enfoque (pattern1|pattern2|...), en realidad coincide con los patrones al mismo tiempo.

También utiliza los juegos de instrucciones SIMD de Intel como SSE4.2, AVX2 y BMI. El resumen del diseño y explicación del trabajo se puede encontrar here. También tiene excelentes desarrolladores reference guide con muchas explicaciones y consideraciones de rendimiento y uso. Pequeño article para usarlo en la naturaleza (en ruso).