您可以使用正则表达式查找所有数字及其分隔符:
re = /\A(.+?)\s+((?:\d+[a-z]*[,\s]+)*\d+[a-z]*)/
txt = "Sokolov 19, 20, 23 ,25
Hertzl 80,82,84,86
Hertzl 80a,82b,84e,90
Aba Hillel Silver 2,3,5,6,
Weizman 8
Ahad Ha'am 9 13 29"
matches = txt.lines.map{ |line| line.match(re).to_a[1..-1] }
p matches
#=> [["Sokolov", "19, 20, 23 ,25"],
#=> ["Hertzl", "80,82,84,86"],
#=> ["Hertzl", "80a,82b,84e,90"],
#=> ["Aba Hillel Silver", "2,3,5,6"],
#=> ["Weizman", "8"],
#=> ["Ahad Ha'am", "9 13 29"]]
上面的正则表达式说:
\A
从字符串的前面开始
(…)
捕获结果
.+?
找到一个或多个字符,尽可能少地使该模式的其余部分匹配。
\s+
后跟一个或多个空白字符(我们不捕获)
(…)
捕获结果
(?:…)*
找到零个或多个这里的东西,但不要捕获它们
\d+
一位或多位数字 (0–9)
[a-z]*
零个或多个小写字母
[,\s]+
一个或多个逗号和/或空格字符
\d+
后跟一位或多位数字
[a-z]*
和零个或多个小写字母
但是,如果您想将数字分成几部分,则需要使用scan
orsplit
或等效项。
result = matches.map{ |name,numbers| [name,numbers.scan(/[^,\s]+/)] }
p result
#=> [["Sokolov", ["19", "20", "23", "25"]],
#=> ["Hertzl", ["80", "82", "84", "86"]],
#=> ["Hertzl", ["80a", "82b", "84e", "90"]],
#=> ["Aba Hillel Silver", ["2", "3", "5", "6"]],
#=> ["Weizman", ["8"]],
#=> ["Ahad Ha'am", ["9", "13", "29"]]]
这是因为重复组内的正则表达式捕获不会捕获每个重复。例如:
re = /((\d+) )+/
txt = "hello 11 2 3 44 5 6 77 world"
p txt.match(re)
#=> #<MatchData "11 2 3 44 5 6 77 " 1:"77 " 2:"77">
整个正则表达式匹配整个字符串,但每次捕获只保存最后一次看到的实例。在这种情况下,外部捕获仅获得“77”,内部捕获仅获得“77”。
为什么你不喜欢使用scan
?这就是它的用途。