如何拆分字符串而不在数组中插入空字符串

时间:2017-03-10 22:56:52

标签: ruby regex split

我在使用正则表达式从字符串中分割字符时遇到问题,假设匹配。

我想分开一个" 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")。

如何调整正则表达式,以便只有表达式的部分才能在数组中结束?

4 个答案:

答案 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)/是一个嵌入式子模式,其正则表示标志mix的集合与周围模式的设置无关。

考虑这种情况:

'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