Разделить строку в Ruby, игнорируя содержимое скобок?

Мне нужно разбить строку на список частей в Ruby, но мне нужно игнорировать то, что находится внутри скобок. Например:

A +4, B +6, C (hello, goodbye) +5, D +3

Я хотел бы, чтобы результирующий список был:

[0]A +4
[1]B +6
[2]C (hello, goodbye) +5
[3]D +3

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

Спасибо.


person Colen    schedule 06.01.2010    source источник
comment
Остерегайтесь хмурых лиц :-( испортите синтаксический анализ!   -  person Myrddin Emrys    schedule 06.01.2010


Ответы (2)


Попробуй это:

s = 'A +4, B +6, C (hello, goodbye) +5, D +3'
tokens = s.scan(/(?:\(.*?\)|[^,])+/)
tokens.each {|t| puts t.strip}

Выход:

A +4
B +6
C (hello, goodbye) +5
D +3

Краткое объяснение:

(?:        # open non-capturing group 1
  \(       #   match '('
  .*?      #   reluctatly match zero or more character other than line breaks
  \)       #   match ')'
  |        #   OR
  [^,]     #   match something other than a comma
)+         # close non-capturing group 1 and repeat it one or more times

Другой вариант - разделить запятую, за которой следуют некоторые пробелы, только если первая скобка, которую можно увидеть при просмотре вперед, является открывающей скобкой (или вообще без скобки: т. Е. Конец строки):

s = 'A +4, B +6, C (hello, goodbye) +5, D +3'
tokens = s.split(/,\s*(?=[^()]*(?:\(|$))/)
tokens.each {|t| puts t}

будет производить тот же вывод, но я считаю, что метод scan чище.

person Bart Kiers    schedule 06.01.2010
comment
# =› [A +4, B +6, C (привет, до свидания) +5, D +3] По-моему, идеально. Возможно, вы захотите #trim, чтобы удалить окружающие пробелы. - person Myrddin Emrys; 06.01.2010
comment
это не работает для A +4, B +6, C (hello, (how are you?, bad)goodbye) +5, D +3. Любая идея, как это исправить, пожалуйста? - person rochb; 30.01.2011
comment
@rochb, когда в игру вступает произвольное количество вложенных скобок, используйте правильный синтаксический анализатор, не взламывайте регулярные выражения. - person Bart Kiers; 30.01.2011

string = "A +4, B +6, C (hello, goodbye) +5, D +3"
string.split(/ *, *(?=[^\)]*?(?:\(|$))/)
# => ["A +4", "B +6", "C (hello, goodbye) +5", "D +3"]

Как работает это регулярное выражение:

/
   *, *        # find comma, ignoring leading and trailing spaces.
  (?=          # (Pattern in here is matched against but is not returned as part of the match.)
    [^\)]*?    #   optionally, find a sequence of zero or more characters that are not ')'
    (?:        #   <non-capturing parentheses group>
      \(       #     left paren ')'
      |        #     - OR -
      $        #     (end of string)
    )
  )
/
person gabriel    schedule 06.01.2010
comment
Это может быть немного загадочно без объяснения для слабонервного энтузиаста регулярных выражений, вероятно, OP! :). Но тем не менее хорошее решение. - person Bart Kiers; 06.01.2010
comment
Как это работает? Я не смог найти никакой хорошей документации о том, как регулярное выражение работало с разделением - как Барт К. говорит, что я не очень хорошо разбираюсь в регулярных выражениях. - person Colen; 06.01.2010
comment
@Colen, я опубликовал очень похожее регулярное выражение в качестве второго решения, включая объяснение. - person Bart Kiers; 06.01.2010