Разбить строку с использованием Regex

201
11 мая 2018, 09:25

я пытаюсь разбить строку через пробелы, но не разбивать всё что заключено в скобки ()

Данный код работает нормально для (a + b) * c * (x - y) ==> (a + b), *, c, *, (x - y)

Regex.Matches(expression, @"[\(].+?[\)]|[^ ]+")
                                        .Cast<Match>()
                                        .Select(m => m.Value)
                                        .ToArray();

Но этот пример разбивается не так как я ожидаю...

(a * (x - y)) * c

Получается: (a * (x - y), ), *, с Ожидается: (a * (x - y)), *, с

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

Но вот не могу написать такое выражение...

Answer 1

Используйте "balanced construct":

Regex.Matches(s, @"(?x)
      \(                    # открывающая круглая скобка
      (?>                   # начало группы, которая находит... 
         [^()]+|            # 1+ символов, отличных от круглых скобок
         (?<o>)\(|          # открывающая круглая скобка (пустая строка добавляется в стек группы `o`)
         (?<-o>)\)          # закрывающая круглая скобка (пустая строка удаляется из стека группы `o`)
       )*                   #  повторить 0 и более раз
       (?(o)(?!))           # если в стеке группы `o` остались значения, отменить совпадение
      \)                    # закрывающая круглая скобка
     |                      # или
      [^\s()]+              # 1+ символов, отличных от пробельных и круглых скобок
     ")           
    .Cast<Match>()
    .Select(m => m.Value)
    .ToArray();

См. демо регулярного выражения, а здесь демо C#.

Подробности

  • \( - открывающая круглая скобка
  • (?> - начало группы, которая находит...
    • [^()]+| - 1+ символов, отличных от круглых скобок
    • (?<o>)\(| - открывающая круглая скобка (пустая строка добавляется в стек группы o)
    • (?<-o>)\) - закрывающая круглая скобка (пустая строка удаляется из стека группы o)
  • )* - повторить 0 и более раз
  • (?(o)(?!)) - если в стеке группы o остались значения, отменить совпадение
  • \) - закрывающая круглая скобка
  • | - или
  • [^\s()]+ - 1+ символов, отличных от пробельных и круглых скобок.

Answer 2

Если задача просто разбить строку по пробелам, то можно попробовать и без регулярок это сделать. Например так

IEnumerable<string> Split(string input)
{
    var counter = 0;
    var startPos = 0;
    List<string> parts = new List<string>();
    for (var i = 0; i < input.Length; i++)
    {
        var c = input[i];
        if (c == ' ' && counter == 0)
        {
            var len = i - startPos;
            if (len > 0)
            {
                parts.Add(input.Substring(startPos, len));
            }
            startPos = i+1;
        }
        if (c == '(') counter++;
        if (c == ')') counter--;        
    }
    var lastInd = input.Length;
    var lastLen = lastInd - startPos;
    if (lastLen > 0) parts.Add(input.Substring(startPos, lastLen ));
    return parts;
}

Да, кода больше, но имхо он понятней, чем регулярка, да и работает в теории побыстрее.

Вариант с ленивым вычислением. Пользоваться можно также, как первым вариантом.

IEnumerable<string> Split(string input)
{
    var counter = 0;
    var startPos = 0;   
    for (var i = 0; i < input.Length; i++)
    {
        var c = input[i];
        if (c == ' ' && counter == 0)
        {
            var len = i - startPos;
            if (len > 0)
            {                   
                yield return input.Substring(startPos, len);
            }
            startPos = i+1;
        }
        if (c == '(') counter++;
        if (c == ')') counter--;        
    }
    var lastInd = input.Length;
    var lastLen = lastInd - startPos;       
    if (lastLen > 0) yield return input.Substring(startPos, lastLen);
}
READ ALSO
Работа с modbus C#

Работа с modbus C#

Есть контроллер, работающий по протоколу modbus rtuНужно к нему написать приложение для опроса

171
POST запрос и хранение клиента в PhantomJs с# wpf

POST запрос и хранение клиента в PhantomJs с# wpf

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

196
Почему меню не доступно?

Почему меню не доступно?

Вопрос! Почему меню не доступно? Visual Studio 2010

140
Ошибка при разборе EntityName

Ошибка при разборе EntityName

Есть следующий код

155