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

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

Возьмите 'поиск' этого сайта, например. Когда пользователь вводится [ruby] regex, Я могу использовать следующий код для получения тега и ключевого слова

'[ruby] regex' =~ /\[(.*?)\](.*)/
tag, keyword = $1, $2

Мы можем записать это только в одной строке?


ОБНОВЛЕНИЕ

Огромное спасибо! Позвольте мне мешать и более интересный, что вход может содержать больше чем один тег, как:

[ruby] [regex] [rails] one line

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

29
задан Freewind 22 June 2010 в 07:22
поделиться

2 ответа

Вам понадобится метод Regexp # match . Если вы напишете / \ [(. *?) \] (. *) /. Match ('[ruby] regex') , это вернет объект MatchData . Если мы вызываем этот объект match , то, среди прочего:

  • matches [0] возвращает всю согласованную строку.
  • match [n] возвращает n-ю группу захвата ( $ n ).
  • match.to_a возвращает массив, состоящий из совпадений с [0] по совпадений [N] .
  • match.captures возвращает массив, состоящий только из группы захвата ( соответствует [1] - соответствует [N] ).
  • match.pre_match возвращает все до совпадающей строки.
  • match.post_match возвращает все, что находится после совпавшей строки.

Есть другие методы, которые соответствуют другим специальным переменным и т.д .; вы можете проверить документацию MatchData для получения дополнительной информации. Таким образом, в этом конкретном случае все, что вам нужно написать, это

tag, keyword = /\[(.*?)\](.*)/.match('[ruby] regex').captures

Edit 1: Хорошо, для вашей более сложной задачи вместо этого вам понадобится метод String # scan , который @Theo использовано; однако мы собираемся использовать другое регулярное выражение. Следующий код должен работать:

# You could inline the regex, but comments would probably be nice.
tag_and_text = / \[([^\]]*)\] # Match a bracket-delimited tag,
                 \s*          # ignore spaces,
                 ([^\[]*) /x  # and match non-tag search text.
input        = '[ruby] [regex] [rails] one line [foo] [bar] baz'
tags, texts  = input.scan(tag_and_text).transpose

input.scan (tag_and_text) вернет список пар тег – поиск-текст:

[ ["ruby", ""], ["regex", ""], ["rails", "one line "]
, ["foo", ""], ["bar", "baz"] ]

Вызов transpose переворачивает это, поэтому что у вас есть пара, состоящая из списка тегов и списка текста для поиска:

[["ruby", "regex", "rails", "foo", "bar"], ["", "", "one line ", "", "baz"]]

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

search_str = texts.join(' ').strip.gsub(/\s+/, ' ')

Это объединит поисковые фрагменты с отдельными пробелами, избавится от начальных и конечных пробелов и заменит пробелы из нескольких пробелов одним пробелом.

45
ответ дан 28 November 2019 в 01:27
поделиться
'[ruby] regex'.scan(/\[(.*?)\](.*)/)

вернет

[["ruby", " regex"]]

, вы можете узнать больше о сканировании String # здесь: http://ruby-doc.org/core/classes/String.html#M000812 (короче говоря, он возвращает массив всех последовательных совпадений, внешний массив в этом случае является массивом совпадений, а внутренний - группами захвата одного совпадения).

, чтобы выполнить присвоение, вы можете переписать его следующим образом (при условии, что у вас будет только одно совпадение в строке):

tag, keyword = '[ruby] regex'.scan(/\[(.*?)\](.*)/).flatten

в зависимости от того, что именно вы хотите выполнить, вы можете изменить регулярное выражение на

/^\s*\[(.*?)\]\s*(.+)\s*$/

, которое соответствует всей входной строке и удаляет некоторые пробелы из второй группы захвата. Привязка шаблона к началу и концу сделает его немного более эффективным, и в некоторых случаях это позволит избежать ложных или повторяющихся совпадений (но это очень сильно зависит от ввода) - это также гарантирует, что вы можете безопасно использовать возвращенный массив в назначении, потому что он никогда не будет иметь более одного совпадения.

Что касается следующего вопроса, я бы сделал следующее:

def tags_and_keyword(input)
  input.scan(/^\s*\[(.+)\]\s+(.+)\s*$/) do |match|
    tags = match[0].split(/\]\s*\[/)
    line = match[1]
    return tags, line
  end
end

tags, keyword = tags_and_keyword('[ruby] [regex] [rails] one line')
tags # => ["ruby", "regex", "rails"]
keyword # => "one line"

его можно переписать в одну строку, но я бы не стал:

tags, keyword = catch(:match) { input.scan(/^\s*\[(.+)\]\s+(.+)\s*$/) { |match| throw :match, [match[0].split(/\]\s*\[/), match[1]] } }

Мое решение предполагает, что все теги идут перед ключевым словом, и что там только одно выражение тегов / ключевого слова в каждом вводе. Первый захват объединяет все теги, но затем я разделяю эту строку, поэтому это двухэтапный процесс (который, как написал @Tim в своем комментарии, необходим, если у вас нет движка, способного к рекурсивному сопоставлению).

11
ответ дан 28 November 2019 в 01:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: