0

我在文件中有街道名称和号码,如下所示:

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、b、c、d 的街道号码。

我想出了这个意思:

/(\D{2,})\s+(\d{1,3}[a-d|א-ד]?)(?:[,\s]{1,3})?/

它找到街道名称和第一个号码。我需要找到所有的数字。

如果可能的话,我不想使用两个单独的正则表达式,而且我更喜欢不使用 Ruby,scan而只是将它放在一个正则表达式中。

4

4 回答 4

3

您可以使用正则表达式查找所有数字及其分隔符:

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]*和零个或多个小写字母

但是,如果您想将数字分成几部分,则需要使用scanorsplit或等效项。

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?这就是它的用途。

于 2013-09-26T14:24:42.317 回答
1

我想要一个可以找到并匹配的正则表达式......

  • 街道名称是否还包含,撇号旁边的digits (0-9)其他?characters
  • 街道号码是否基于任意数据?它总是只是一个可选的a, b, c, ord吗?
  • 您是否需要字符串长度的最小和最大限制?

以下是一些可能的选项:

如果您不确定街道名称包含什么,但知道您的街道编号模式将是带有可选字母、逗号或空格的数字。

/^(.*?)\s+(\d+(?:[a-z]?[, ]+\d+)*)(?=,|$)/

查看工作演示

如果街道名称仅包含带有可选撇号的字母,并且街道编号包含带有可选字母的数字,则逗号。

/^([a-zA-Z' ]+)\s+(\d+(?:[a-z]?[, ]+\d+)*)(?=,|$)/

查看工作演示

如果您的街道名称和街道编号模式始终一致,您可以轻松做到。

/^([a-zA-Z' ]+)\s+([0-9a-z, ]+)$/

查看工作演示

于 2013-09-26T17:25:29.363 回答
1

绕过只能捕获重复表达式的最后一个实例的限制的唯一方法是为单个实例编写正则表达式,并让正则表达式机器为您执行重复,就像全局替换选项一样,诚然类似于扫描. 不幸的是,在这种情况下,您必须匹配街道名称街道号码,然后无法轻松地将捕获的号码与捕获的名称相关联。

Regex 擅长于它的功能,但是当您尝试将其应用程序扩展到它的自然限制之外时,它并不漂亮。;-)

于 2013-09-26T16:10:34.767 回答
1

如果您希望您的第三个示例正常工作,您需要进行[a-d]更改以将 包含e在范围内。更改后,您可以使用(\D{2,})\s+(\d{1,3}[a-e]?(?:[,\s]{1,3})*)*. 使用您提供的示例,我使用 Rubular 进行了一些测试

使用更多的分组,您可以在最后几个条件上重复(这似乎很棘手。这样,在最初消耗空间后,最后的空格和逗号就会陷入重复。

于 2013-09-26T14:27:36.260 回答