объединить регулярное выражение в рубине - PullRequest
1 голос
/ 17 марта 2009

Учитывая этот текст:

    /* F004 (0309)00 */  
    /* field 1 */  
    /* field 2 */  
    /* F004 (0409)00 */  
    /* field 1 */  
    /* field 2 */  

как мне разобрать его в этот массив:
[<br> ["F004"],["0309"],["/* field 1 */\n/* field 2 */"],<br> ["F004"],["0409"],["/* field 1 */\n/* field 2 */"]<br> ]

У меня есть код, работающий для разбора первых двух элементов:

form = /\/\*\s+(\w+)\s+\((\d{4})\)[0]{2}\s+\*\//m
text.scan(form)

[<br> ["F004"],["0309"],<br> ["F004"],["0409"]<br> ]

А вот код, где я пытаюсь проанализировать все три и потерпеть неудачу с ошибкой регулярного выражения:

form = /\/\*\s+(\w+)\s+\((\d{4})\)[0]{2}\s+\*\//m
form_and_fields = /#{form}(.[^#{form}]+)/m
text.scan(form_and_fields)


редактировать: Это то, что сработало для меня, благодаря как rampion, так и singpolyma:
form = /
  \/\*\s+(\w+)\s+\((\d+)\)\d+\s+\*\/    #formId & edDate
  (.+?)                                 #fieldText
  (?=\/\*\s+\w+\s+\(\d+\)\d+\s+\*\/|\Z) #stop at beginning of next form
                                        # or the end of the string
/mx
text.scan(form)

Ответы [ 3 ]

6 голосов
/ 17 марта 2009

Вы, похоже, неправильно понимаете, как работают классы персонажей (например, [a-f0-9] или [^aeiouy]). /[^abcd]/ не отменяет шаблон abcd, он говорит "соответствует любому символу, который не 'a' или 'b' или 'c' или 'd'".

Если вы хотите сопоставить отрицание шаблона, используйте конструкцию /(?!pattern)/. Это совпадение с нулевой шириной - это означает, что оно на самом деле не соответствует ни одному символу, оно соответствует позиции. Аналогично тому, как /^/ и /$/ соответствуют началу и концу строки, или /\b/ соответствует границе слова. Например: /(?!xx)/ соответствует каждой позиции, где шаблон «xx» не начинается.

Как правило, после использования отрицания шаблона вам необходимо сопоставить некоторый символ для продвижения вперед в строке.

Итак, чтобы использовать ваш шаблон:

form = /\/\*\s+(\w+)\s+\((\d{4})\)[0]{2}\s+\*\//m
form_and_fields = /#{form}((?:(?!#{form}).)+)/m
text.scan(form_and_fields)

Изнутри (я буду использовать (?#comments))

  • (?!#{form}) отменяет исходный рисунок, поэтому он соответствует любой позиции, с которой исходный рисунок не может начаться.
  • (?:(?!#{form}).)+ означает сопоставить один символ после этого и повторить попытку столько раз, сколько возможно, но хотя бы один раз. (?:(?#whatever)) - это круглые скобки без записи - хорошо для группировки.

В irb это дает:

irb> text.scan(form_and_fields)
=> [["F004", "0309", "  \n    /* field 1 */  \n    /* field 2 */  \n    ", nil, nil], ["F004", "0409", "  \n    /* field 1 */  \n    /* field 2 */  \n", nil, nil]]

Дополнительные nil получены от групп захвата в form, которые используются в шаблоне с отрицанием (?!#{form}) и поэтому ничего не фиксируют при успешном совпадении.

Это можно почистить немного:

form_and_fields = /#{form}\s*(.+?)\s*(?:(?=#{form})|\Z)/m
text.scan(form_and_fields)

Теперь вместо отрицательного просмотра с нулевой шириной мы используем положительный взгляд с нулевой шириной (?=#{form}), чтобы соответствовать положению следующего вхождения form. Таким образом, в этом регулярном выражении мы сопоставляем все до следующего вхождения form ( без , включая следующее вхождение в нашем матче). Это позволяет нам обрезать некоторые пробелы вокруг полей. Мы также должны проверить случай, когда мы достигли конца строки - /\Z/, поскольку это тоже может произойти.

In irb:

irb> text.scan(form_and_fields)
=> [["F004", "0309", "/* field 1 */  \n    /* field 2 */", "F004", "0409"], ["F004", "0409", "/* field 1 */  \n    /* field 2 */", nil, nil]]

Обратите внимание, что последние два поля заполняются в первый раз - b / c захват паренсов в положительном прогнозе нулевой ширины соответствовал чему-то, даже если он не был помечен как «использованный» во время процесса - вот почему этот бит можно повторить во второй раз.

2 голосов
/ 17 марта 2009
a.scan(/\/\*\s+(\S+)\s+\((\d+)\)\d+\s+\*\/\s+(\/\*.+\*\/\s+\n\s+\/\*.+\*\/)/)
=> [["F004", "0309", "/* field 1 */  \n    /* field 2 */"], ["F004", "0409", "/* field 1 */  \n    /* field 2 */"]]
0 голосов
/ 17 марта 2009

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

  transformed_lines = []

  text.each_line do |line|
    if line =~ /(\w|\d)+\s\(\d+)\)/
      transformed_lines << [ $1, $2, "" ]
    else
      transformed_lines.last.last << line.strip
    end
  end

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

  transformed_lines << OpenStruct.new :thingy_one => $1, :thingy_two => $2, :fields => ""
  ...
  transformed_lines.last.fields << line.strip
...