Использование .NET Micro Framework. Почему мое регулярное выражение не совпадает?

Я разрабатываю небольшой интерпретатор сетевых команд для .net Micro Framework 4.3, работающий на Netduino. Я использую регулярное выражение для анализа пользовательского ввода, поступающего из сети через потоковый сокет. Команды имеют следующий формат:

<T1,0,CommandVerb=Payload>

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

Вот регулярное выражение, которое я использую:

    /*
     * Regex matches command strings in format "<Dn,TT,CommandVerb=Payload>
     * D is the Device class
     * n is the device index
     * TT is a numeric transaction ID of at least 1 digits.
     * CommandVerb is, obviously, the command verb ;-)
     * Payload is optional and is used to supply any parameter values to the command.
     * 
     * N.B. Micro Framework doesn't support named captures and will throw exceptions if they are used.
     */

    const string CommandRegex = @"<(\w\d),(\d+),([A-Za-z]\w+)(=((\d+)|(.+)))?>";
    static readonly Regex Parser = new Regex(CommandRegex);

Это выражение предназначено для выделения различных частей команды, чтобы я мог легко получить к ним доступ в коде. Последняя часть (=((\d+)|(.+)))? различает числовую полезную нагрузку и текстовую полезную нагрузку или вообще никакой полезной нагрузки.

Это хорошо работает для меня и подтверждает OK в валидаторе регулярных выражений ReSharper. Вот результат, который я ожидаю получить (я думаю, что он немного отличается от результатов, которые вы получите от полного NetFX, мне пришлось работать с этим методом проб и ошибок):

        /* Command with numeric payload has the following groups
         * Group[0] contains [<F1,234,Move=12345>]
         * Group[1] contains [F1]
         * Group[2] contains [234]
         * Group[3] contains [Move]
         * Group[4] contains [=12345]
         * Group[5] contains [12345]
         * Group[6] contains [12345]  
         * -----
         * Command with text payload has the following groups:
         * Group[0] contains [<F1,234,Nickname=Fred>]
         * Group[1] contains [F1]
         * Group[2] contains [234]
         * Group[3] contains [Nickname]
         * Group[4] contains [=Fred]
         * Group[5] contains [Fred]
         * Group[7] contains [Fred]
         * -----
         * Command with verb only (no payload) produces these groups:
         * Group[0] contains [<F1,234,Stop>]
         * Group[1] contains [F1]
         * Group[2] contains [234]
         * Group[3] contains [Stop]
         */

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

<W1,0,HttpPost=http://deathstar.com/route>

Я ожидаю получить результат «команда с текстовой полезной нагрузкой», но на самом деле я получаю результат «команда без полезной нагрузки». Если я уберу точку, то она проанализирует, как я ожидаю, и я получу «команду с текстовой полезной нагрузкой». Как только я снова поставил точку, то (по иронии судьбы) .+ больше не совпадает.

Еще раз обратите внимание: это правильно проверяется в средстве проверки регулярных выражений ReSharper и, похоже, работает на обычной «настольной» платформе, как и ожидалось, но не в .NET Micro Framework. Реализация регулярных выражений Micro Framework является подмножеством полной версии, но документации о том, что должно работать, а что нет, практически не существует.

Я не могу понять, почему .+ не соответствует тексту с точкой внутри. Кто-нибудь может понять, почему он не работает?

ОБНОВЛЕНИЕ 1 - добавлена ​​диагностика

Вот результат:

[Cmd Processor     ] Parser matched 8 groups
[Cmd Processor     ]   Group[0]: <W1,0,HttpPost=http://deat
[Cmd Processor     ]   Group[1]: W1
[Cmd Processor     ]   Group[2]: 0
[Cmd Processor     ]   Group[3]: HttpPost
A first chance exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll

Так что дело не в том, что Group[4] равно нулю, он выдает ArgumentOutOfRangeException для этого индексатора, хотя групп 8. Кроме того, Group[0] таинственным образом усекается. Хм...

Обновление 2 — улучшена диагностика

Я добавил этот диагностический метод в свой код на основе ответа @Shar1er80:

    [Conditional("DEBUG")]
    static void PrintMatches(Match match)
        {
        if (!match.Success)
            {
            Dbg.Trace("No match", Source.CommandProcessor);
            return;
            }
        Dbg.Trace("Parser matched "+match.Groups.Count + " groups", Source.CommandProcessor);
        for (int i = 0; i < match.Groups.Count; i++)
            {
            string value;
            try
                {
                var group = match.Groups[i];
                value = group == null ? "null group" : group.Value ?? "null value";
                }
            catch (Exception ex)
                {
                value = "threw " + ex.GetType() + " " + ex.Message??string.Empty;
                }
            Dbg.Trace("  Groups[" + i + "]: " + value, Source.CommandProcessor);
            }
        }

С тестовым вводом <W1,0,HttpPost=http://deathstar.com> вывод был:

[Cmd Processor     ] Parser matched 8 groups
[Cmd Processor     ]   Groups[0]: <W1,0,HttpPost=http://deaths
[Cmd Processor     ]   Groups[1]: W1
[Cmd Processor     ]   Groups[2]: 0
[Cmd Processor     ]   Groups[3]: HttpPost
A first chance exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll
[Cmd Processor     ]   Groups[4]: threw System.ArgumentOutOfRangeException Exception was thrown: System.ArgumentOutOfRangeException
A first chance exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll
[Cmd Processor     ]   Groups[5]: threw System.ArgumentOutOfRangeException Exception was thrown: System.ArgumentOutOfRangeException
A first chance exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll
[Cmd Processor     ]   Groups[6]: threw System.ArgumentOutOfRangeException Exception was thrown: System.ArgumentOutOfRangeException
A first chance exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll
[Cmd Processor     ]   Groups[7]: threw System.ArgumentOutOfRangeException Exception was thrown: System.ArgumentOutOfRangeException
A first chance exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll

Ясно, что это неправильно, потому что сообщается о 8 совпадениях, но попытка доступа к чему-либо о Groups[3] вызывает исключение. Трассировка стека для исключения: System.String::Substring System.Text.RegularExpressions.Capture::get_Value TA.NetMF.WeatherServer.CommandParser::PrintMatches TA.NetMF.WeatherServer.CommandParser::ParseCommand [snip]

Я открыл проблему в отношении .NET MicroFramework.


person Tim Long    schedule 19.07.2015    source источник
comment
Почему бы тебе не превратить ((\d+)|(.+)) в (.+)   -  person Avinash Raj    schedule 19.07.2015
comment
Не удивляйтесь, если вы нашли ошибку или используете неподдерживаемые части регулярного выражения. В любом случае, не могли бы вы свести эту проблему к минимуму? Вы говорите, что если у вас есть регулярное выражение (.+), оно не будет соответствовать ., или вы говорите, что если у вас есть ((\d+)|(.+)) оно не будет соответствовать . ? Ваше регулярное выражение кажется мне нормальным.   -  person Erti-Chris Eelmaa    schedule 19.07.2015
comment
Кажется, вся последняя часть (=((\d+)|(.+)))? является проблемой. Я все еще пытаюсь сузить круг. Это Group[4] доставляет мне проблемы, она пуста, когда я прихожу ее исследовать.   -  person Tim Long    schedule 19.07.2015
comment
Я думаю, вы можете быть правы, что это может быть ошибка. Дело не в том, что Group[4] является нулевой, она выдает OutOfRangeException для этого индексатора — см. соответствующее обновление.   -  person Tim Long    schedule 19.07.2015
comment
@AvinashRaj: »Последняя часть (=((\d+)|(.+)))? различает числовую полезную нагрузку и текстовую полезную нагрузку или вообще никакой полезной нагрузки». Звучит правильно для меня.   -  person Joey    schedule 19.07.2015
comment
ОК, это явно ошибка, так как он сообщает о 8 совпадениях, а затем выдает исключения для чего-либо выше 3. Я поднял проблему против .Net Micro Framework: netmf.codeplex.com/workitem/2515   -  person Tim Long    schedule 19.07.2015
comment
Я попробовал предложение свернуть мой последний термин только (.+), и когда это не сработало, я попробовал (.*). Фу! Код регулярного выражения настолько сломан, что я получаю разные результаты при нескольких попытках использования одной и той же входной строки. Иногда он выбрасывает одну группу, в других случаях эта группа будет работать нормально, только чтобы выбрасывать следующую. ФУ!   -  person Tim Long    schedule 20.07.2015
comment
Просто дикий удар в темноте, возможно, это на самом деле переполнение стека (нет, я не пытаюсь шутить). Известно, что регулярные выражения интенсивно используют стек. Если бы это был я, я бы отказался от идеи Regex и написал итеративный синтаксический анализатор.   -  person Peter Wone    schedule 11.08.2015
comment
В конце концов, я полностью отказался от регулярных выражений. Чем больше я смотрел, тем более сломанными они были в .Net Micro Framework. В одном случае я обнаружил, что мои совпадения просто произвольно усекались после тридцати или сорока символов. Похоже, что может происходить какое-то выделение статической памяти, и когда она заканчивается, она заканчивается. Что бы это ни было, помимо простого логического указания соответствия/несоответствия, RegEx очень несовершенны в .net MF, хотя API предполагает обратное. Я думаю, что код мог быть написан стажером во время обеденного перерыва :(   -  person Tim Long    schedule 12.08.2015


Ответы (1)


Точка подходит ко всему. "(=((\d+)|(.+)))?>" означает 1. Создать выражение с тегами (знак '?' в конце означает, что это необязательно). 2. Оно должно начинаться со знака равенства и содержать либо 2.1. Целое число или 2,2. что угодно, любого размера.

2.2 будет соответствовать остальной части выражения, каким бы оно ни было.

Затем, когда придет время сопоставить завершающий закрывающий '>', если то, что следует за '=', не было целым числом, в буфере ничего не останется. Следовательно, не совпадает.

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

"(=([^>]+))?>".

person tamlin    schedule 19.07.2015
comment
Хммм, разве .+ не является жадным по умолчанию? Поэтому он должен соответствовать только следующему литералу, не так ли? В любом случае, я достаточно отчаянно пытаюсь что-то попробовать на этом этапе, поэтому я попробовал, и все равно не получается (что интересно, он не видит закрывающуюся угловую скобку). - person Tim Long; 20.07.2015