验证字符串是否符合以下格式:/^(#\d\s*)+$/
(例如#1 #2
)。
使用散列来获取所有数字,例如#<MatchData "1234" 1:"#1" 2:"#2">
。它不必是MatchData对象,任何可枚举的数组类型都可以使用。
使用match
时,它刚好匹配上次出现的情况:
/^(#\d\s*)+$/.match "#1 #2"
# => #<MatchData "#1 #2" 1:"#2">
当我使用扫描时,它可以正常工作:
"#1 #2".scan /#\d/
# => ["#1", "#2"]
但是我不相信我可以验证字符串的格式,因为它会为"aaa #1 #2"
返回相同的字符串。
我可以仅用1个方法调用来验证我的字符串与/^(#\d\s*)+$/
匹配并捕获#number
的所有实例吗?
因为我已经使用宝石红了一段时间,所以我对此问题感到很难过。看起来很简单,但我无法正常工作。
答案 0 :(得分:3)
是的,您可以使用
s.scan(/(?:\G(?!\A)|\A(?=(?:#\d\s*)*\z))\s*\K#\d/)
请参见regex demo
详细信息
(?:\G(?!\A)|\A(?=(?:#\d\s*)*\z))
-两种选择:
\G(?!\A)
-上一次成功比赛的结束|
-或\A(?=(?:#\d\s*)*\z)
-字符串(\A
的开头,其后是0个或多个#
+数字+ 0+空格的重复,然后是字符串的结尾\s*
-0 +空格字符\K
-匹配重置运算符丢弃到目前为止已匹配的文本#\d
-一个#
字符,然后是一个数字简而言之:字符串位置的开头首先匹配,但前提是右边的字符串(即整个字符串)与所需的模式匹配。由于该检查是先行执行的,因此正则表达式索引会停留在原先的位置,然后仅在有效匹配之后一直进行匹配,这要归功于\G
运算符(它匹配字符串的开头或上一个匹配的结尾,因此(?!\A)
用于减去起始字符串的位置。
rx = /(?:\G(?!\A)|\A(?=(?:#\d\s*)*\z))\s*\K#\d/
p "#1 #2".scan(rx)
# => ["#1", "#2"]
p "#1 NO #2".scan(rx)
# => []
答案 1 :(得分:1)
def doit(str)
r = /\A#{"(#\\d)\\s*"*str.count('#')}\z/
str.match(r)&.captures
end
doit "#1#2 #3 " #=> ["#1", "#2", "#3"]
doit " #1#2 #3 " #=> nil
请注意,正则表达式仅取决于字符串中字符'#'
的实例数。由于在两个示例中该数字均为3,因此各自的正则表达式相等,即:
/\A(#\d)\s*(#\d)\s*(#\d)\s*\z/
此正则表达式的构造如下。
str = "#1#2 #3 "
n = str.count('#')
#=> 3
s = "(#\\d)\\s*"*n
#=> "(#\\d)\\s*(#\\d)\\s*(#\\d)\\s*"
/\A#{s}\z/
#=> /\A(#\d)\s*(#\d)\s*(#\d)\s*\z/
正则表达式为:“匹配字符串的开头,然后是三个相同的捕获组,每个捕获组可选地后面跟空格,然后是字符串的末尾。因此,正则表达式既测试字符串的有效性,又提取字符串。捕获组中所需的匹配项。
如果没有匹配项(&
返回match
),则需要safe navigation operator,nil
。
OP的注释是对问题的概括,其中井号('#'
)是可选的。这可以通过如下修改正则表达式来解决。
def doit(str)
r = /\A#{"(?:#?(\\d)(?=#|\\s+|\\z)\\s*)"*str.count('0123456789')}\z/
str.match(r)&.captures
end
doit "1 2 #3 " #=> ["1", "2", "3"]
doit "1 2 #3 " #=> ["1", "2", "3"]
doit "1#2" #=> ["1", "2"]
doit " #1 2 #3 " #=> nil
doit "#1 2# 3 " #=> nil
doit " #1 23 #3 " #=> nil
对于包含三位数字的字符串,正则表达式为:
/\A(?:#?(\d)(?=#|\s+|\z)\s*)(?:#?(\d)(?=#|\s+|\z)\s*)(?:#?(\d)(?=#|\s+|\z)\s*)\z/
虽然此正则表达式可能会很长,但这并不一定意味着它的效率相对较低,因为前瞻非常局限。