2010-06-12 13 views
13

Próbuję wykonać pewne przetwarzanie, które będzie łatwiejsze przy użyciu wyrażeń regularnych..NET Wyrażenia regularne na bajty zamiast znaków

Dane wejściowe są tablicą (lub wyliczeniem) bajtów.

nie chcę konwertować bajtów na znaki z następujących powodów:

  1. sprawność Obliczanie
  2. efektywności zużycia pamięci
  3. Niektóre niedrukowalne bajtów może być złożony do konwersji znaków. Nie wszystkie bajty można wydrukować.

Więc nie mogę użyć Regex.

Jedyne rozwiązanie, jakie znam, to używanie Boost.Regex (która działa na bajtach - C-znakach), ale jest to biblioteka C++, której owijanie za pomocą C++/CLI wymaga znacznej pracy.

Jak używać wyrażeń regularnych na bajtach w .NET bezpośrednio, bez pracy z ciągami i znakami .NET?

Dziękuję.

+1

Czy możesz podać przykład wzoru RegEx, który chcesz dopasować? –

+1

@Nate Dudek, nie rozumiem, jak to ma znaczenie. Szukam ogólnej biblioteki regex, ponieważ próbuję analizować złożone protokoły internetowe. Nie jest to konkretne wyrażenie regularne. Jeśli naprawdę potrzebujesz przykładu zobacz http://www.ietf.org/rfc/rfc2616.txt dla różnych wyrażeń regularnych (odnosi się to również do innych programów). – brickner

+2

W rzeczywistości .NET nie obsługuje wejścia binarnego dla Wyrażenia regularnego. Rozumiem, że nie chcesz tracić czasu na przetwarzanie, ale jeśli jedynym bohaterem są znaki specjalne, możesz konwertować bajty na ciągi bez problemu, ponieważ ciąg .NET jest kodowany przy użyciu UTF-16 Unicode. Wszystkie znaki będą obsługiwane. – Ucodia

Odpowiedz

7

Trwa tutaj niedopasowanie impedancji. Chcesz pracować z wyrażeń regularnych w .Net, które używają ciągów (znaki wielobajtowe), ale chcesz pracować ze znakami jednobajtowymi. Nie możesz mieć obu jednocześnie używając .Net jak zwykle.

Jednak, aby przełamać tę niedopasowanie, można zająć się ciągiem w sposób zorientowany na bajt i zmutować go. Zmutowany ciąg może następnie działać jako bufor do ponownego wykorzystania. W ten sposób nie będziesz musiał konwertować bajtów na znaki lub konwertować bufora wejściowego na ciąg znaków (jak na twoje pytanie).

Przykład:

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

Stosując tę ​​technikę można przeznaczyć ciąg „bufor”, które mogą być ponownie wykorzystane jako wejście do Regex, ale można zmutować go ze swoimi bajtów za każdym razem. Pozwala to uniknąć narzutu konwersji \ kodowania tablicy bajtów na nowy ciąg .Net za każdym razem, gdy chcesz zrobić dopasowanie. Może to okazać się bardzo znaczące, ponieważ widziałem wiele algorytmów w .Net, próbujących pokonać miliony mil na godzinę, by zostać sprowadzonym na kolana przez generowanie ciągów, a następnie rozsyłanie spamu i czas spędzony w GC.

Oczywiście jest to kod niebezpieczny, ale jest .Net.

Wyniki Regex będą generować ciągi znaków, więc masz tu problem. Nie jestem pewien, czy istnieje sposób użycia Regex, który nie generuje nowych ciągów. Z pewnością można uzyskać informacje o indeksie i długości dopasowania, ale generowanie ciągów narusza wymagania dotyczące wydajności pamięci.

Aktualizacja

Właściwie po demontażu Regex \ Mecz \ Grupa \ chwytanie, wygląda na to, że tylko generuje przechwycony ciąg podczas korzystania z właściwości Value, więc może przynajmniej nie generować ciągi Jeśli masz tylko Właściwości indeksu dostępu i długości. Będziesz jednak generował wszystkie wspierające obiekty Regex.

+0

Twoje rozwiązanie wydaje się działać, gdy dane wejściowe są ciągami. Moje dane wejściowe to bajty, których nie chcę konwertować na ciąg znaków.Nie jestem pewien, dlaczego nie mogę obu - jeśli ktoś już opakował Boost.Regex z C++/CLI, mogę mieć oba. – brickner

+0

Tak, sugeruję, że używasz łańcucha jako bufora i mutujesz go używając swoich bajtów. Tam, gdzie go mutuję, używałbyś tu swojego bufora bajtów. Jest to tylko podstawowy przykład mutacji ciągu znaków, ale oznacza to, że możesz mieć bufor ciągów wielokrotnego użytku dla wejścia regex, które mutujesz z wprowadzonymi danymi. –

+0

Niezły pomysł. Ale to oznacza, że ​​za każdym razem, gdy mam nowy zestaw bajtów, będę musiał skopiować je do tablicy char. Wydaje się, że jest to dokładnie to samo rozwiązanie, co przekształcanie bajtów w ciąg znaków za pomocą kodowania, ale ponowne użycie ciągu (co może być problematyczne, jeśli chodzi o ograniczenia wielkości i jeśli chcę użyć dwóch wejść równolegle). – brickner

2

Cóż, gdybym zmierzył się z tym problemem, zrobiłbym opakowanie C++/CLI, z wyjątkiem tego, że utworzyłbym specjalny kod do tego, co chcę osiągnąć. Ostatecznie rozwijaj opakowanie z czasem, by robić ogólne rzeczy, ale to tylko opcja.

Pierwszym krokiem jest zawijanie tylko wejścia i wyjścia Boost :: Regex. Twórz wyspecjalizowane funkcje w C++, które robią wszystkie potrzebne rzeczy i używaj CLI, aby przekazać dane wejściowe do kodu C++, a następnie pobierz wynik z powrotem z CLI. Nie wygląda mi to na zbyt dużą pracę.

Aktualizacja:

Pozwól mi spróbować wyjaśnić mój punkt widzenia. Mimo że mogę się mylić, sądzę, że nie będziesz w stanie znaleźć żadnej implementacji, której możesz użyć. Dlatego - niezależnie od tego, czy ci się to podoba, czy nie - będziesz zmuszony wybrać między wrapperem CLI a konwersją bajtów na chars, aby użyć Regexa .NET. Moim zdaniem opakowanie jest lepszym wyborem, ponieważ będzie działało szybciej. I nie robić żadnych badań porównawczych, to tylko przypuszczenie oparte na:

  1. Korzystanie opakowanie po prostu trzeba rzucić typ wskaźnika (bajtów < -> znaków).
  2. Korzystając z Regexu .NET, musisz konwertować każdy bajt wejściowy.
+2

Dokładnie wiem, jak to owinąć, ale szukam rozwiązania, którego sam nie muszę pisać. – brickner

1

Jako alternatywę dla korzystania niebezpieczne, tylko za pisanie proste, rekurencyjne comparer jak:

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

Można poprawić efektywność w wielu sposobów (tj szukając pierwszy bajt mecz zamiast iteracji, etc.) ale to może zacząć ... mam nadzieję, że to pomaga.

0

Osobiście zastosowałem inne podejście i napisałem mały automat stanów, który można rozszerzyć. Wierzę, że jeśli dane protokołu analizy są dużo bardziej czytelne niż 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; 
} 
Powiązane problemy