Я - новичок к рубину, я хочу знать, могу ли я использовать всего одну строку, чтобы сделать задание.
Возьмите 'поиск' этого сайта, например. Когда пользователь вводится [ruby] regex
, Я могу использовать следующий код для получения тега и ключевого слова
'[ruby] regex' =~ /\[(.*?)\](.*)/
tag, keyword = $1, $2
Мы можем записать это только в одной строке?
ОБНОВЛЕНИЕ
Огромное спасибо! Позвольте мне мешать и более интересный, что вход может содержать больше чем один тег, как:
[ruby] [regex] [rails] one line
Действительно ли возможно использовать один линейный код для получения массива тегов и ключевого слова? Я попробовал, но перестал работать.
Вам понадобится метод 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+/, ' ')
Это объединит поисковые фрагменты с отдельными пробелами, избавится от начальных и конечных пробелов и заменит пробелы из нескольких пробелов одним пробелом.
'[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 в своем комментарии, необходим, если у вас нет движка, способного к рекурсивному сопоставлению).