繁体   English   中英

匹配字符串中的重复模式

[英]Matching repeated pattern in string

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

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而只在一个正则表达式中使用它。

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

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)
    • [az]*零个或多个小写字母
    • [,\\s]+一个或多个逗号和/或空格字符
    • \\d+后跟一位或多位数字
    • [az]*以及零个或多个小写字母

但是,如果您想将数字分成几部分,则需要使用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 这就是它的目的。

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

使用更多的分组,您可以在最后几个条件下进行重复(这似乎很棘手。这样,最初占用空间后,末尾的空格和逗号将被重复所捕获)。

唯一只能捕获重复表达式的最后一个实例的局限性是,为单个实例编写正则表达式,然后让正则表达式机器为您进行重复,这与全局替换选项一样,诚然类似于scan 。 不幸的是,在这种情况下,你必须匹配街道名称或门牌号码,然后就没有办法轻易捕获的数字与捕获的名字联系在一起。

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

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

  • 街道名称中是否还包含digits (0-9)和撇号以外的其他characters
  • 街道号码是否基于任意数据? 它总是只是可选的abcd吗?
  • 您是否需要最小和最大的字符串长度限制?

以下是一些可能的选项:

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

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

查看工作演示

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

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

查看工作演示

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

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

查看工作演示

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM