2010-06-12 27 views
13

Tôi đang cố thực hiện một số phân tích cú pháp sẽ dễ dàng hơn khi sử dụng cụm từ thông dụng..NET Cụm từ thông dụng trên byte thay vì ký tự

Đầu vào là một mảng (hoặc liệt kê) các byte.

Tôi không muốn chuyển đổi các byte để chars vì những lý do sau đây:

  1. hiệu quả tính toán
  2. hiệu quả tiêu thụ bộ nhớ
  3. Một số byte không thể in được có thể là phức tạp để chuyển đổi sang ký tự. Không phải tất cả các byte đều có thể in được.

Vì vậy, tôi không thể sử dụng Regex.

Giải pháp duy nhất tôi biết, đang sử dụng Boost.Regex (hoạt động trên các ký tự byte - C), nhưng đây là thư viện C++ gói bằng C++/CLI sẽ hoạt động đáng kể.

Tôi có thể sử dụng cụm từ thông dụng trên byte trong .NET trực tiếp như thế nào, mà không cần làm việc với chuỗi và ký tự .NET?

Cảm ơn bạn.

+1

Bạn có thể cung cấp ví dụ về mẫu RegEx bạn đang cố gắng khớp không? –

+1

@Nate Dudek, tôi không thấy nó có liên quan như thế nào. Tôi đang tìm một thư viện regex chung vì tôi đang cố phân tích các giao thức internet phức tạp. Không phải là một regex cụ thể. Nếu bạn thực sự cần một ví dụ, hãy xem http://www.ietf.org/rfc/rfc2616.txt cho regex khác ở đó (nó cũng tham chiếu đến các rfcs khác) – brickner

+2

Thực ra, .NET không hỗ trợ đầu vào nhị phân cho Cụm từ thông dụng. Tôi hiểu rằng bạn không muốn mất thời gian tính toán trong chuyển đổi, nhưng nếu các ký tự đặc biệt là nỗi sợ duy nhất của bạn, bạn có thể chuyển đổi byte thành chuỗi mà không gặp sự cố. Chuỗi .NET được mã hóa bằng Unicode UTF-16. Tất cả các ký tự sẽ được hỗ trợ. – Ucodia

Trả lời

7

Có một chút không phù hợp trở kháng xảy ra tại đây. Bạn muốn làm việc với các biểu thức chính quy trong .Net sử dụng các chuỗi (các ký tự nhiều byte), nhưng bạn muốn làm việc với các ký tự byte đơn. Bạn không thể có cả hai cùng một lúc bằng cách sử dụng. Net như bình thường.

Tuy nhiên, để phá vỡ sự không phù hợp này, bạn có thể xử lý chuỗi theo kiểu byte theo định hướng và thay đổi nó. Chuỗi biến đổi sau đó có thể hoạt động như một bộ đệm có thể sử dụng lại. Bằng cách này bạn sẽ không phải chuyển đổi byte thành ký tự, hoặc chuyển đổi bộ đệm đầu vào của bạn thành một chuỗi (theo câu hỏi của bạn).

Một ví dụ:

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

Sử dụng kỹ thuật này, bạn có thể phân bổ một chuỗi "đệm" mà có thể được tái sử dụng làm đầu vào cho Regex, nhưng bạn có thể biến nó với byte của bạn mỗi lần. Điều này tránh được việc chuyển đổi \ mã hóa mảng byte của bạn thành một chuỗi .Net mới mỗi khi bạn muốn thực hiện một kết quả phù hợp. Điều này có thể chứng minh là rất có ý nghĩa như tôi đã thấy nhiều một thuật toán trong Net cố gắng đi tại một triệu dặm một giờ chỉ để được đưa đến đầu gối của mình bằng thế hệ chuỗi và gửi thư rác đống tiếp theo và thời gian dành cho GC.

Rõ ràng đây là mã không an toàn, nhưng đó là .Net.

Kết quả của Regex sẽ tạo chuỗi mặc dù, do đó bạn gặp sự cố ở đây. Tôi không chắc chắn nếu có một cách sử dụng Regex sẽ không tạo ra các chuỗi mới. Bạn chắc chắn có thể nhận được thông tin về chỉ số và độ dài phù hợp nhưng việc tạo chuỗi vi phạm các yêu cầu của bạn về hiệu quả bộ nhớ.

Cập nhật

Trên thực tế sau khi tháo Regex \ Match \ Group \ Capture, có vẻ như nó chỉ tạo ra các chuỗi bắt khi bạn truy cập vào các tài sản giá trị gia tăng, vì vậy bạn có thể ít nhất là không được tạo ra chuỗi nếu bạn chỉ truy cập chỉ mục và thuộc tính độ dài. Tuy nhiên, bạn sẽ tạo ra tất cả các đối tượng Regex hỗ trợ.

+0

Giải pháp của bạn dường như hoạt động khi đầu vào là một chuỗi. Đầu vào của tôi là byte, mà tôi không muốn chuyển đổi thành chuỗi.Tôi không chắc chắn lý do tại sao tôi không thể cả hai - nếu ai đó đã gói Boost.Regex với C++/CLI tôi có thể có cả hai. – brickner

+0

Có, những gì tôi đề nghị là bạn sử dụng chuỗi như một bộ đệm và thay đổi nó bằng cách sử dụng byte của bạn. Nơi tôi đang đột biến nó, bạn sẽ sử dụng bộ đệm byte của bạn ở đây. Đây chỉ là một ví dụ cơ bản của việc thay đổi một chuỗi, nhưng nó có nghĩa là bạn có thể có một bộ đệm chuỗi có thể sử dụng lại cho đầu vào regex mà bạn thay đổi với đầu vào của bạn. –

+0

Ý tưởng hay. Nhưng điều này có nghĩa là mỗi khi tôi có bộ byte mới, tôi sẽ cần sao chép chúng vào mảng char. Đây có vẻ là giải pháp chính xác giống như chuyển đổi byte thành chuỗi bằng cách sử dụng Mã hóa, nhưng với việc sử dụng lại chuỗi (có thể có vấn đề khi nói đến giới hạn kích thước và nếu tôi muốn sử dụng hai đầu vào song song). – brickner

2

Vâng, nếu tôi gặp phải sự cố này, tôi sẽ LÀM trình bao bọc C++/CLI, ngoại trừ tôi sẽ tạo mã chuyên biệt cho những gì tôi muốn đạt được. Cuối cùng phát triển các wrapper với thời gian để làm những điều chung chung, nhưng điều này chỉ là một lựa chọn.

Bước đầu tiên là bọc đầu vào Boost :: Regex và đầu ra. Tạo các chức năng chuyên dụng trong C++ để thực hiện tất cả những thứ bạn muốn và sử dụng CLI chỉ để truyền dữ liệu đầu vào tới mã C++ và sau đó lấy lại kết quả với CLI. Điều này không nhìn tôi như quá nhiều việc phải làm.

Cập nhật:

Hãy để tôi cố gắng để làm rõ quan điểm của tôi. Mặc dù tôi có thể sai, tôi tin rằng bạn sẽ không thể tìm thấy bất kỳ triển khai thực hiện .NET Binary Regex nào mà bạn có thể sử dụng. Đó là lý do tại sao - cho dù bạn có thích hay không - bạn sẽ bị buộc phải chọn giữa trình bao bọc CLI và chuyển đổi byte thành ký tự để sử dụng Regex của .NET. Theo tôi, wrapper là lựa chọn tốt hơn, bởi vì nó sẽ hoạt động nhanh hơn. Tôi không làm bất cứ điểm chuẩn, điều này chỉ là một giả định dựa trên:

  1. Sử dụng wrapper bạn chỉ phải bỏ kiểu con trỏ (byte < -> chars).
  2. Sử dụng Regex của .NET bạn phải chuyển đổi từng byte của đầu vào.
+2

Tôi thực sự biết chính xác làm thế nào để bọc nó, nhưng tôi đang tìm một giải pháp mà tôi không phải tự viết. – brickner

1

Là một thay thế cho việc sử dụng không an toàn, chỉ cần xem xét cách viết một đơn giản, Comparer đệ quy như:

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

Bạn có thể nâng cao hiệu quả trong một số cách (tức là tìm kiếm sự phù hợp với byte đầu tiên thay vì iterating vv) nhưng điều này có thể giúp bạn bắt đầu ... hy vọng điều đó sẽ hữu ích.

0

Cá nhân tôi đã đi theo một cách tiếp cận khác và viết một máy trạng thái nhỏ có thể được mở rộng. Tôi tin rằng nếu phân tích cú pháp dữ liệu giao thức, điều này dễ đọc hơn nhiều so với 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; 
} 
Các vấn đề liên quan