我在使用正则表达式从字符串中分割字符时遇到问题,假设匹配。
我想分开一个" m"或者" f"字符串的第一部分中的字符,假设下一个字符是一个或多个数字,后跟可选的空格字符,后面是我拥有的数组中的字符串。
我试过了:
2.4.0 :006 > MY_SEPARATOR_TOKENS = ["-", " to "]
=> ["-", " to "]
2.4.0 :008 > str = "M14-19"
=> "M14-19"
2.4.0 :011 > str.split(/^(m|f)\d+[[:space:]]*#{Regexp.union(MY_SEPARATOR_TOKENS)}/i)
=> ["", "M", "19"]
注意无关的""我的数组开头的元素,并注意到最后一个表达式只是" 19"而我想要字符串中的其他所有内容(" 14-19")。
如何调整正则表达式,以便只有表达式的部分才能在数组中结束?
答案 0 :(得分:4)
当我从Ruby中的正则表达式中提取字符时,我发现match
更加优雅:
string = "M14-19"
string.match(/\A(?<m>[M|F])(?<digits>\d{2}(-| to )\d{2})/)[1, 2]
=> ["M", "14-19"]
# also can extract the symbols from match
extract_string = string.match(/\A(?<m>[M|F])(?<digits>\d{2}(-| to )\d{2})/)
[[extract_string[:m], extract_string[:digits]]
=> ["M", "14-19"]
string = 'M14 to 14'
extract_string = string.match(/\A(?<m>[M|F])(?<digits>\d{2}(-| to )\d{2})/)[1, 2]
=> ["M", "14 to 14"]
答案 1 :(得分:3)
您的代码中存在一个错误。不要养成这样做的习惯:
#{Regexp.union(MY_SEPARATOR_TOKENS)}
你正在为自己设置一个非常难以调试的问题。
以下是发生的事情:
regex = Regexp.union(%w(a b)) # => /a|b/
/#{regex}/ # => /(?-mix:a|b)/
/#{regex.source}/ # => /a|b/
/(?-mix:a|b)/
是一个嵌入式子模式,其正则表示标志m
,i
和x
的集合与周围模式的设置无关。
考虑这种情况:
'CAT'[/#{regex}/i] # => nil
我们期望正则表达式i
标志匹配,因为它忽略大小写,但子表达式仍然只允许小写,导致匹配失败。
使用裸(a|b)
或添加source
成功,因为内部表达式获取主表达式i
:
'CAT'[/(a|b)/i] # => "A"
'CAT'[/#{regex.source}/i] # => "A"
有关此问题的详细讨论,请参阅“How to embed regular expressions in other regular expressions in Ruby”。
答案 2 :(得分:3)
TOKENS = ["-", " to "]
r = /
(?<=\A[mMfF]) # match the beginning of the string and then one
# of the 4 characters in a positive lookbehind
(?= # begin positive lookahead
\d+ # match one or more digits
[[:space:]]* # match zero or more spaces
(?:#{TOKENS.join('|')}) # match one of the tokens
) # close the positive lookahead
/x # free-spacing regex definition mode
(?:#{TOKENS.join('|')})
已替换为(?:-| to )
。
这当然可以用通常的方式写出来。
r = /(?<=\A[mMfF])(?=\d+[[:space:]]*(?:#{TOKENS.join('|')}))/
在r
分割时,你在两个字符之间分割(在正向后观和正向前瞻之间),所以不会消耗任何字符。
"M14-19".split r
#=> ["M", "14-19"]
"M14 to 19".split r
#=> ["M", "14 to 19"]
"M14 To 19".split r
#=> ["M14 To 19"]
如果希望在上一个示例中返回["M", "14 To 19"]
,请将[mMfF]
更改为[mf]
,将/x
更改为/xi
。
答案 3 :(得分:0)
如果得到匹配,空元素将始终存在,因为捕获的部分出现在字符串的开头,并且字符串的开头和匹配之间的字符串被添加到结果数组中,无论是空的或非空字符串。获得匹配后,可以使用shift
/ drop
,也可以使用.reject { |c| c.empty? }
删除所有空数组元素(请参阅How do I remove blank elements from an array?)。
然后,14-
模式部分将\d+[[:space:]]...
吃掉(消耗) - 将其放入(?=...)
预测中,该前瞻只会检查模式匹配,但赢得了&#39}。消耗字符。
使用类似
的内容MY_SEPARATOR_TOKENS = ["-", " to "]
s = "M14-19"
puts s.split(/^(m|f)(?=\d+[[:space:]]*#{Regexp.union(MY_SEPARATOR_TOKENS)})/i).drop(1)
#=> ["M", "14-19"]
请参阅Ruby demo