.NET Регулярные выражения на байтах вместо символов - PullRequest
16 голосов
/ 12 июня 2010

Я пытаюсь выполнить синтаксический анализ, который будет проще с помощью регулярных выражений.

Входные данные - это массив (или перечисление) байтов.

Я не хочу преобразовывать байты в символы по следующим причинам:

  1. Эффективность вычислений
  2. Эффективность потребления памяти
  3. Некоторые непечатаемые байты могут быть сложными для преобразования в символы. Не все байты пригодны для печати. ​​

Так что я не могу использовать Regex .

Единственное известное мне решение - это использование Boost.Regex (которое работает с байтами - символами C), но это библиотека C ++, для которой перенос с использованием C ++ / CLI потребует значительной работы.

Как я могу использовать регулярные выражения для байтов в .NET напрямую, без работы со строками и символами .NET?

Спасибо.

Ответы [ 4 ]

8 голосов
/ 12 июня 2010

Здесь происходит небольшое несоответствие импеданса. Вы хотите работать с регулярными выражениями в .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);
    }
}

Используя эту технику, вы можете выделить строковый «буфер», который можно повторно использовать в качестве входных данных для Regex, но вы можете каждый раз изменять его байтами. Это позволяет избежать затрат на преобразование \ кодирование вашего байтового массива в новую строку .Net каждый раз, когда вы хотите выполнить сопоставление. Это может оказаться очень важным, поскольку я видел, как многие алгоритмы в .Net пытаются разогнаться до миллиона миль в час только для того, чтобы оказаться на коленях при генерации строк и последующем рассылке спама и времени, проведенном в GC.

Очевидно, что это небезопасный код, но это .Net.

Результаты регулярных выражений будут генерировать строки, поэтому у вас есть проблема здесь. Я не уверен, есть ли способ использовать Regex, который не будет генерировать новые строки. Вы, конечно, можете получить информацию об индексе соответствия и длине, но генерация строк нарушает ваши требования к эффективности памяти.

Обновление

На самом деле после дизассемблирования Regex \ Match \ Group \ Capture создается впечатление, что он генерирует захваченную строку только при доступе к свойству Value, поэтому, по крайней мере, вы можете не генерировать строки, если обращаетесь только к свойствам index и length. Однако вы будете генерировать все вспомогательные объекты Regex.

2 голосов
/ 12 июня 2010

Ну, если бы я столкнулся с этой проблемой, я бы сделал оболочку C ++ / CLI, за исключением того, что я бы создал специализированный код для того, чего я хочу достичь.В конце концов разработайте обертку со временем, чтобы сделать общие вещи, но это всего лишь вариант.

Первый шаг - обернуть только вход и выход Boost :: Regex.Создайте специализированные функции в C ++, которые выполняют все, что вам нужно, и используйте CLI, чтобы просто передать входные данные в код C ++, а затем получить результат обратно с помощью CLI.Это не выглядит для меня слишком большой работой.

Обновление:

Позвольте мне попытаться уточнить мою точку зрения.Даже если я ошибаюсь, я считаю, что вы не сможете найти какую-либо реализацию .NET Binary Regex , которую вы могли бы использовать.Вот почему - нравится вам это или нет - вы будете вынуждены выбирать между оболочкой CLI и преобразованием байтов в символы для использования .NET Regex.На мой взгляд, обертка - лучший выбор, потому что она будет работать быстрее.Я не делал никаких тестов, это всего лишь предположение, основанное на:

  1. Используя обертку, вам просто нужно привести тип указателя (байты <-> символы).В регулярном выражении NET вы должны конвертировать каждый байт ввода.
1 голос
/ 02 января 2013

В качестве альтернативы использованию небезопасных, просто подумайте о написании простого рекурсивного компаратора, например:

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

Вы можете повысить эффективность несколькими способами (например, поиск первого байтового совпадения вместо итерации и т. Д.), Но это может помочь вам начать работу ... надеюсь, это поможет.

0 голосов
/ 15 сентября 2016

Лично я пошел другим путем и написал небольшой конечный автомат, который можно расширить. Я считаю, что при разборе данных протокола это гораздо более читабельно, чем регулярное выражение.

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