如何使用Regexp.union匹配字符串开头的字符

时间:2017-03-27 19:09:29

标签: ruby regex string union

我使用的是Ruby 2.4。我想匹配一个可选的" a"或" b"字符,后跟一个任意数量的空格,然后是一个或多个数字,但我的正则表达式无法匹配其中任何一个:

2.4.0 :017 > MY_TOKENS = ["a", "b"]
 => ["a", "b"]
2.4.0 :018 > str = "40"
 => "40"
2.4.0 :019 > str =~ Regexp.new("^[#{Regexp.union(MY_TOKENS)}]?[[:space:]]*\d+[^a-z^0-9]*$")
 => nil
2.4.0 :020 > str =~ Regexp.new("^#{Regexp.union(MY_TOKENS)}?[[:space:]]*\d+[^a-z^0-9]*$")
 => nil
2.4.0 :021 > str =~ Regexp.new("^#{Regexp.union(MY_TOKENS)}?[[:space:]]*\d+$")
 => nil

我对我做错了什么感到难过。

2 个答案:

答案 0 :(得分:3)

如果它们是单个字符,只需在字符类中使用MY_TOKENS.join

MY_TOKENS = ["a", "b"]
str = "40"
first_regex = /^[#{MY_TOKENS.join}]?[[:space:]]*\d+[^a-z0-9]*$/
# /^[ab]?[[:space:]]*\d+[^a-z0-9]*$/ 
puts str =~ first_regex
# 0

你也可以集成Regexp.union,它可能会导致some unexpected bugs,因为外部正则表达式的标志不适用于内部正则表达式:

second_regex = /^#{Regexp.union(MY_TOKENS)}?[[:space:]]*\d+[^a-z0-9]*$/
# /^(?-mix:a|b)?[[:space:]]*\d+[^a-z0-9]*$/
puts str =~ second_regex
# 0

上面的正则表达式看起来很像你所做的,但使用//而不是Regexp.new可以防止你不得不逃避反斜杠。

您可以使用Regexp#source来避免此行为:

third_regex = /^(?:#{Regexp.union(MY_TOKENS).source})?[[:space:]]*\d+[^a-z0-9]*$/
# /^(?:a|b)?[[:space:]]*\d+[^a-z0-9]*$/
puts str =~ third_regex
# 0

或简单地构建你的正则表达式联合:

fourth_regex = /^(?:#{MY_TOKENS.join('|')})?[[:space:]]*\d+[^a-z0-9]*$/
# /^(?:a|b)?[[:space:]]*\d+[^a-z0-9]*$/
puts str =~ fourth_regex
# 0

如果MY_TOKENS包含单词而非单词,则最后3个示例应该可以正常工作。

first_regexthird_regexfourth_regex应该可以正常使用/i标记。

举个例子:

first_regex = /^[#{MY_TOKENS.join}]?[[:space:]]*\d+[^a-z0-9]*$/i
"A 40" =~ first_regex
# 0

答案 1 :(得分:1)

我相信您希望匹配一个字符串,该字符串可能包含您在MY_TOKENS中定义的任何替代字符,然后是0 +空格,然后是一个或多个数字,直到字符串的结尾。

然后你需要使用

Regexp.new("\\A#{Regexp.union(MY_TOKENS)}?[[:space:]]*\\d+\\z").match?(s)

/\A#{Regexp.union(MY_TOKENS)}?[[:space:]]*\d+\z/.match?(s)

使用Regexp.new时,您应该记住双重转义反斜杠以定义文字反斜杠(例如“\ d”是数字匹配模式)。在正则表达式文字表示法中,您可以使用单个反斜杠(/\d/)。

不要忘记将字符串的开头与\A匹配,将字符串的结尾与\z个锚点匹配。

请注意[...]创建一个匹配其中定义的任何字符的字符类:[ab]匹配ab[program]将匹配一个字符,program。如果MY_TOKENS中有多字符序列,则需要从模式中删除[...]

要使正则表达式不区分大小写,请将不区分大小写的修饰符传递给模式,并确保使用.source创建的正则表达式的Regex.union属性来删除标记(谢谢,Eric) :

Regexp.new("(?i)\\A#{Regexp.union(MY_TOKENS).source}?[[:space:]]*\\d+\\z")

/\A#{Regexp.union(MY_TOKENS).source}?[[:space:]]*\d+\z/i

创建的正则表达式为/(?i-mx:\Aa|b?[[:space:]]*\d+\z)/,其中(?i-mx)表示不区分大小写的模式已启用且多行(点匹配换行符和详细模式已关闭)。