Как представить необязательную группу с помощью регулярного выражения? - PullRequest
0 голосов
/ 25 октября 2019

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

У меня есть следующий текст пример 1

Fn.If(first condition) 
   When the first condition is valid! This is a required section
Fn.ElseIf(some second condition)
   When the second condition is valid! This is an optional section
Fn.ElseIf(third second condition)
   When the third condition is valid! This is an optional section
Fn.Else
    Catch all! This is an optional section
Fn.End

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

  • (группа 1A): Fn.If
  • (группа 1B): первое условие
  • (группа 1C): когда первое условие действительно! Это обязательный раздел
  • (группа 2A): Fn.ElseIf
  • (группа 2B): второе условие
  • (группа 2C): если второе условие действительно! Это необязательный раздел
  • (группа 3A): Fn.ElseIf
  • (группа 3B): третье условие
  • (группа 3C): когда действует третье условие! Это необязательный раздел
  • (группа 4А): Fn.Else
  • (группа 4В): поймать все! Это необязательный раздел
  • (группа C): Fn.End

Как видно из комментариев, группа 1 (A / B / C) должна существовать вместе споследняя группа для шаблона, чтобы быть действительным. Однако все промежуточные группы являются необязательными, то есть они могут существовать или, возможно, не существовать.

В дополнение к приведенному выше текстовому примеру шаблон должен быть в состоянии проанализировать следующий текст example 2

Fn.If(first condition) 
   When first condition is valid! This is a required section
Fn.EndIf

или текст пример 3

Fn.If(first condition) 
   When first condition is valid! This is a required section
Fn.Else
    Catch all! This is an optional section
Fn.EndIf

Я могу это сделать

  1. (Fn\.If\s*)\((.+?)\)([\s\S]+)(Fn\.EndIf) работает с примером текста 2
  2. (Fn\.ElseIf\s*)\((.+?)\)([\s\S]+) вернет Fn.ElseIf(...).... группу
  3. (Fn\.Else)([\s\S]+) захватит Fn.Else..... групп

Однако я изо всех сил пытаюсь поставить все 3в шаблонах, которые можно объединить при идеализации этой строки 2, может быть ноль или более групп, за которыми следует одна или ни одной строки 3.

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

(Fn\.If\s*)\((.+?)\)([\s\S]+)
((Fn\.ElseIf\s*)\((.+?)\)([\s\S]+))*
((Fn\.Else)([\s\S]+))?
(Fn\.EndIf)

Ответы [ 2 ]

0 голосов
/ 25 октября 2019

Это выполнимо с одним регулярным выражением

Это версия регулярного выражения на языке Python, но она должна быть переведена в C #

ключ должен использовать одну группу захвата для всех совпадений

(Fn\.[A-Za-z]+[^\(\n]*)((\((.+?)\)(?<=\)))?([\s\S]*?)(?=Fn\.))?

протестировано со всеми 3 примерами

онлайн-превью: https://regex101.com/r/VqNlMm/1

0 голосов
/ 25 октября 2019

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

void Main()
{
    const String input = 
@"Fn.If(first condition)
   When the first condition is valid! This is a required section
Fn.ElseIf(some second condition)
   When the second condition is valid! This is an optional section
Fn.ElseIf(third second condition)
   When the third condition is valid! This is an optional section
Fn.Else
    Catch all! This is an optional section
Fn.End  
    ";

    Regex rIf     = new Regex( @"^Fn\.If\((.+)\)\s*$" );
    Regex rElseIf = new Regex( @"^Fn\.ElseIf\((.+)\)\s*$" );
    Regex rElse   = new Regex( @"^Fn\.Else\s*$" );
    Regex rEnd    = new Regex( @"^Fn\.End\s*$" );

    String[] lines = input.Split(new String[] { "\r\n" }, StringSplitOptions.None );

    List<Statement> statements = new List<Statement>();

    String type = null;
    String condition = null;
    StringBuilder sb = new StringBuilder();

    State state = State.Outside;
    foreach( String line in lines )
    {
        switch( state )
        {
        case State.Outside:

            Match mIf = rIf.Match( line );
            if( mIf.Success )
            {
                type = "Fn.If";
                condition = mIf.Groups[1].Value;

                state = State.InIf;
            }

            break;
        case State.InIf:
        case State.InElseIf:

            Match mElseIf = rElseIf.Match( line );
            if( mElseIf.Success )
            {
                statements.Add( new Statement( type, condition, sb.ToString() ) );
                sb.Length = 0;

                state = State.InElseIf;
                type = "Fn.ElseIf";
                condition = mElseIf.Groups[1].Value;
            }
            else
            {
                Match mElse = rElse.Match( line );
                if( mElse.Success )
                {
                    statements.Add(new Statement(type, condition, sb.ToString()));
                    sb.Length = 0;

                    state = State.InElse;
                    type = "Fn.Else";
                    condition = null;
                }
                else
                {
                    sb.Append( line );
                }
            }

            break;

        case State.InElse:

            Match mEnd = rEnd.Match(line);
            if (mEnd.Success)
            {
                statements.Add(new Statement(type, condition, sb.ToString()));
                sb.Length = 0;

                state = State.Outside;
                type = null;
                condition = null;
            }
            else
            {
                sb.Append( line );
            }

            break;
        }
    }

    statements.Dump();
}

class Statement
{
    public Statement( String type, String condition, String contents )
    {
        this.Type = type;
        this.Condition = condition;
        this.Contents = contents;
    }

    public String Type { get; }
    public String Condition { get; }
    public String Contents { get; }
}

// Define other methods and classes here
enum State
{
    Outside,
    InIf,
    InElseIf,
    InElse
}

Запуск в Linqpad дает мне этовыход:

enter image description here

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