匹配字符串中的重复模式

时间:2013-09-26 13:57:12

标签: ruby regex

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

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 个答案:

答案 0 :(得分: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]*零个或多个小写字母

但是,如果您希望将数字分成几部分,则需要使用scansplit或等效内容。

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

答案 1 :(得分:1)

如果您希望第3个示例有效,则需要进行[a-d]更改以在范围中包含e。更改后,您可以使用(\D{2,})\s+(\d{1,3}[a-e]?(?:[,\s]{1,3})*)*。使用您给出的示例我做了一些testing using Rubular

使用更多的分组,你可以在最后几个条件下重复(这看起来非常棘手。这样,最后消耗空间后,最后的间距和逗号会被重复捕获。

答案 2 :(得分:1)

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

正则表达式在它的功能方面非常出色,但是当你尝试将其应用程序扩展到它的自然限制之外时,它并不漂亮。 ; - )

答案 3 :(得分:1)

  

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

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

以下是一些可能的选择:

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

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

请参阅working demo

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

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

请参阅working demo

如果您的街道名称和街道号码模式始终一致,您可以轻松完成。

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

请参阅working demo