как использовать регулярное выражение в одну строку, чтобы получить соответствующий контент - PullRequest
29 голосов
/ 22 июня 2010

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

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

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

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


UPDATE

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

[ruby] [regex] [rails] one line

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

Ответы [ 3 ]

43 голосов
/ 22 июня 2010

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

  • matches[0] возвращает всю совпадающую строку.
  • matches[n] возвращает n-ую группу захвата ($n).
  • matches.to_a возвращает массив, состоящий из matches[0] - matches[N].
  • matches.captures возвращает массив, состоящий только из группы захвата (от matches[1] до matches[N]).
  • matches.pre_match возвращает все до соответствующей строки.
  • matches.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+/, ' ')

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

11 голосов
/ 22 июня 2010
'[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 в своем комментарии, требуется, если у вас нет механизма, способного к рекурсивному сопоставлению).

0 голосов
/ 10 октября 2018

Поместите это в ApplicationHelper или куда-то еще, что вам нужно

def element_id_for(f, element)
  matcher   = /id=(".*"|'.*')/
  el_string = f.hidden_field(element.to_sym)
  id_string = matcher.match(el_string)[0].gsub(/id="/, '').chomp('"')
  return    id_string
end

Наконец, вы можете использовать этот метод следующим образом:

form_for :test_form do |f|
  my_id = element_id_for(f, :start_date)
  # => "text_form_start_date"
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...