我是红宝石的新手,我想知道我是否只能使用一行来完成这项工作.
以此网站的"搜索"为例.当用户输入时[ruby] regex
,我可以使用以下代码来获取标记和关键字
'[ruby] regex' =~ /\[(.*?)\](.*)/
tag, keyword = $1, $2
Run Code Online (Sandbox Code Playgroud)
我们可以把它写成一行吗?
UPDATE
非常感谢!我可以更难,更有趣的是,输入可能包含多个标签,例如:
[ruby] [regex] [rails] one line
Run Code Online (Sandbox Code Playgroud)
是否可以使用一行代码来获取tags数组和关键字?我试过了,但都失败了.
Ant*_*sky 45
你需要这个Regexp#match
方法.如果你写/\[(.*?)\](.*)/.match('[ruby] regex')
,这将返回一个MatchData
对象.如果我们称之为该对象matches
,那么,除其他外:
matches[0]
返回整个匹配的字符串.matches[n]
返回第n个捕获组($n
).matches.to_a
返回一个由matches[0]
through 组成的数组matches[N]
.matches.captures
返回一个只包含捕获组(matches[1]
通过matches[N]
)的数组.matches.pre_match
返回匹配字符串之前的所有内容matches.post_match
返回匹配字符串后的所有内容.有更多的方法,对应于其他特殊变量等; 你可以检查一下MatchData
文档了解更多.因此,在这种特定情况下,您需要编写的所有内容都是
tag, keyword = /\[(.*?)\](.*)/.match('[ruby] regex').captures
Run Code Online (Sandbox Code Playgroud)
编辑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
Run Code Online (Sandbox Code Playgroud)
在input.scan(tag_and_text)
将返回标签的搜索文本对的列表:
[ ["ruby", ""], ["regex", ""], ["rails", "one line "]
, ["foo", ""], ["bar", "baz"] ]
Run Code Online (Sandbox Code Playgroud)
该transpose
调用将翻转,以便您有一对由标记列表和搜索文本列表组成:
[["ruby", "regex", "rails", "foo", "bar"], ["", "", "one line ", "", "baz"]]
Run Code Online (Sandbox Code Playgroud)
然后,您可以根据结果执行任何操作.例如,我可能会建议
search_str = texts.join(' ').strip.gsub(/\s+/, ' ')
Run Code Online (Sandbox Code Playgroud)
这将连接单个空格的搜索片段,摆脱前导和尾随空格,并用单个空格替换多个空格的运行.
The*_*heo 11
'[ruby] regex'.scan(/\[(.*?)\](.*)/)
Run Code Online (Sandbox Code Playgroud)
将返回
[["ruby", " regex"]]
Run Code Online (Sandbox Code Playgroud)
你可以在这里阅读更多关于String#scan的内容:http://ruby-doc.org/core/classes/String.html#M000812(简而言之,它返回所有连续匹配的数组,在这种情况下,外部数组是数组匹配,内部是一个匹配的捕获组).
你可以像这样重写它(假设你在字符串中只有一个匹配):
tag, keyword = '[ruby] regex'.scan(/\[(.*?)\](.*)/).flatten
Run Code Online (Sandbox Code Playgroud)
根据您想要完成的内容,您可能希望将正则表达式更改为
/^\s*\[(.*?)\]\s*(.+)\s*$/
Run Code Online (Sandbox Code Playgroud)
它匹配整个输入字符串,并修剪第二个捕获组中的一些空格.将模式锚定到开始和结束将使它更有效,并且它将避免在某些情况下获得错误或重复匹配(但这在很大程度上取决于输入) - 它还保证您可以安全地使用返回数组在赋值,因为它永远不会有多个匹配.
关于后续问题,这就是我要做的:
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"
Run Code Online (Sandbox Code Playgroud)
它可以在一行中重写,但我不会:
tags, keyword = catch(:match) { input.scan(/^\s*\[(.+)\]\s+(.+)\s*$/) { |match| throw :match, [match[0].split(/\]\s*\[/), match[1]] } }
Run Code Online (Sandbox Code Playgroud)
我的解决方案假设所有标记都位于关键字之前,并且每个输入中只有一个标记/关键字表达式.第一个捕获全部标记,但随后我拆分了该字符串,所以这是一个两步过程(正如@Tim在他的评论中所写,除非你有一个能够递归匹配的引擎).
归档时间: |
|
查看次数: |
33234 次 |
最近记录: |