如何使用一行正则表达式来获取匹配的内容

我是ruby的新手,我想知道我是否只能用一条线来完成这项工作。

以此网站的“搜索”为例。 当用户键入[ruby] regex ,我可以使用以下代码来获取标记和关键字

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

我们可以把它写成一行吗?


UPDATE

非常感谢! 我可以更难,更有趣的是,输入可能包含多个标签,例如:

 [ruby] [regex] [rails] one line 

是否可以使用一行代码来获取tags数组和关键字? 我试过了,但都失败了。

你需要Regexp#match方法。 如果你写/ /\[(.*?)\](.*)/.match('[ruby] regex')/\[(.*?)\](.*)/.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 

编辑1:好的,对于你更难的任务,你会想要使用@Theo使用的String#scan方法; 但是,我们将使用不同的正则表达式。 以下代码应该工作:

 # 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#scan的信息: 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在他的评论中写的那样,除非你有一个能够递归匹配的引擎,否则是必需的)。

把它放到你的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