Поиск всех символов в скобках с помощью .NET Regex - PullRequest
3 голосов
/ 06 апреля 2010

Мне нужно получить все символы между символами '(' и ')'.

   var str = "dfgdgdfg (aaa.bbb) sfd (c) fdsdfg (   ,ddd   (eee) )";

В этом примере мне нужно получить 3 строки:

(aaa.bbb)
(c)
(    ,ddd   (eee) )

Какой шаблон я должен написать? Пожалуйста, помогите.

Ответы [ 8 ]

4 голосов
/ 06 апреля 2010

Попробуйте что-то вроде этого:

\(([^)]+)\)

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

2 голосов
/ 06 апреля 2010

.NET поддерживает рекурсию в регулярных выражениях с использованием групп балансировки. См. Например, http://blog.stevenlevithan.com/archives/balancing-groups

Освоение регулярных выражений настоятельно рекомендуется

1 голос
/ 06 апреля 2010

Вы хотите использовать функцию сбалансированного сопоставления регулярных выражений .net.

var s = "dfgdgdfg (aaa.bbb) sfd (c) fdsdfg (   ,ddd   (eee) )";
var exp = "\([^()]*((?<paren>\()[^()]*|(?<close-paren>\))[^()]*)*(?(paren)(?!))\)";
var matches = Regex.Matches(s,exp);
1 голос
/ 06 апреля 2010

Не говорю, что это лучше, чем Regex, но вот еще один вариант

    public static IEnumerable<string> InParen(string s)
    {
        int count = 0;
        StringBuilder sb = new StringBuilder();
        foreach (char c in s)
        {
            switch (c)
            {
                case '(':
                    count++;
                    sb.Append(c);
                    break;
                case ')':
                    count--;
                    sb.Append(c);
                    if (count == 0)
                    {
                        yield return sb.ToString();
                        sb = new StringBuilder();
                    }
                    break;
                default:
                    if (count > 0)
                        sb.Append(c);
                    break;
            }
        }
    }
1 голос
/ 06 апреля 2010

Для этого необходима рекурсия.

Пример Perl:

#!/usr/bin/perl

$re = qr  /
     (                      # start capture buffer 1
        \(                  #   match an opening paren
        (           # capture buffer 2
        (?:                 #   match one of:
            (?>             #     don't backtrack over the inside of this group
                [^()]+    #       one or more 
            )               #     end non backtracking group
        |                   #     ... or ...
            (?1)            #     recurse to opening 1 and try it again
        )*                  #   0 or more times.
        )           # end of buffer 2
        \)                  #   match a closing paren
     )                      # end capture buffer one
    /x;


sub strip {
my ($str) = @_;
while ($str=~/$re/g) {
    $match=$1; $striped=$2;
    print "$match\n";
    strip($striped) if $striped=~/\(/;
    return $striped;
    }
}


$str="dfgdgdfg (aaa.bbb) sfd (c) fdsdfg (   ,ddd   (eee) )";

print "\n\nstart=$str\n";

while ($str=~/$re/g) { 
    strip($1) ;
}

Вывод:

start=dfgdgdfg (aaa.bbb) sfd (c) fdsdfg (   ,ddd   (eee) )
(aaa.bbb)
(c)
(   ,ddd   (eee) )
(eee)
1 голос
/ 06 апреля 2010

Вам нужно либо лексер / парсер, либо использовать лексер с поддержкой стека. Но само по себе регулярное выражение никуда вас не приведет.

0 голосов
/ 06 апреля 2010

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

\((?:[^()]|\([^)]*\))*\)

, что означает:

\(         # match the character '('
(?:        # start non-capture group 1 
  [^()]    #   match any character not from the set {'(', ')'}
  |        #   OR
  \(       #   match the character '('
  [^)]*    #   match any character not from the set {')'} and repeat it zero or more times
  \)       #   match the character ')'
)*         # end non-capture group 1 and repeat it zero or more times
\)         # match the character ')'

Версия для 3 заставит ваши глаза кровоточить! Вы могли бы использовать функцию рекурсивного сопоставления регулярных выражений в .NET, но я бы лично не пошел: разбрызгивание рекурсии внутри регулярных выражений приводит к безумию! (не совсем, конечно, но регулярные выражения достаточно сложны, чтобы понять и смешать рекурсию в миксе, не делает это более понятным IMO)

Я бы просто написал небольшой метод, который мог бы выглядеть так: Python:

def find_parens(str):

    matches = []
    parens = 0
    start_index = -1
    index = 0

    for char in str:
        if char == '(':
            parens = parens+1
            if start_index == -1:
                start_index = index
        if char == ')':
            parens = parens-1
            if parens == 0 and start_index > -1:
                matches.append(str[start_index:index+1])
                start_index = -1
        index = index+1

    return matches

for m in find_parens("dfgdgdfg (aaa.bbb) sfd (c) fdsdfg (   ,ddd   (eee) )"):
    print(m)

который печатает:

(aaa.bbb)
(c)
(   ,ddd   (eee) )

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

0 голосов
/ 06 апреля 2010

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

(\([^()]*\))
(\([^()]*\([^()]*\)[^()]*\))

Или вы можете пропустить регулярные выражения и просто разобрать строку напрямую. Увеличить переменную состояния на (, уменьшить на) и напечатать строку, когда она вернется к нулю.

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