Ruby 字符串中重复模式的匹配
我在文件中有街道名称和编号,如下所示:Ruby 字符串中重复模式的匹配,ruby,regex,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
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+
捕获结果(…)
在此处查找零个或多个内容,但不要捕获它们(?:…)*
一个或多个数字(0-9)\d+
零个或多个小写字母[a-z]*
一个或多个逗号和/或空白字符[,\s]+
后跟一个或多个数字\d+
和零个或多个小写字母[a-z]*
scan
或split
或同等工具
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">
re=/(\d+)+/
txt=“你好11 2 3 44 5 6 77世界”
p txt.match(re)
#=> #
整个正则表达式匹配整个字符串,但每个捕获只保存最后看到的实例。在这种情况下,外部捕获只得到“77”,而内部捕获只得到“77”
为什么您不喜欢使用
扫描
?这就是它的用途。您可以使用正则表达式查找所有数字及其分隔符:
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+
捕获结果(…)
在此处查找零个或多个内容,但不要捕获它们(?:…)*
一个或多个数字(0-9)\d+
零个或多个小写字母[a-z]*
一个或多个逗号和/或空白字符[,\s]+
后跟一个或多个数字\d+
和零个或多个小写字母[a-z]*
scan
或split
或同等工具
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">
re=/(\d+)+/
txt=“你好11 2 3 44 5 6 77世界”
p txt.match(re)
#=> #
整个正则表达式匹配整个字符串,但每个捕获只保存最后看到的实例。在这种情况下,外部捕获只得到“77”,而内部捕获只得到“77”
为什么您不喜欢使用
扫描
?这就是它的用途。如果您想让第三个示例起作用,您需要更改[a-d]
以将e
包含在范围内。更改后,您可以使用(\D{2,})\s+(\D{1,3}[a-e]?(?:[,\s]{1,3})**)*
。用你举的例子,我做了一些
使用更多的分组,您可以重复最后几个条件(这似乎非常棘手。这样,在最初使用空格后,结尾的空格和逗号将被捕获在重复中。如果您希望第三个示例能够工作,您需要使用
[a-d]
更改以将e
包含在范围内。更改后,您可以使用(\D{2,})\s+(\D{1,3}[a-e]?(?:[,\s]{1,3})**)*
。使用您给出的示例,我做了一些修改
使用更多分组,您可以重复最后几个条件(这似乎很棘手。这样,在最初使用空格后,结尾的空格和逗号将在重复中被捕获。唯一可以克服只能捕获重复表达式的最后一个实例的限制的方法是为单个实例编写正则表达式,并让正则表达式计算机为其执行重复操作与全局替换选项一样,您承认与扫描类似。不幸的是,在这种情况下,您必须匹配街道名称或街道编号,然后无法轻松地将捕获的编号与捕获的名称关联
Regex在这方面做得很好,但当您试图将其应用程序扩展到超出其自然限制之外时,它并不漂亮。;-)要克服只能捕获重复表达式的最后一个实例的限制,唯一的方法是为单个实例编写正则表达式,并让正则表达式计算机为您执行重复操作,正如全局替换选项所发生的那样,当然类似于扫描。不幸的是,在这种情况下,您必须匹配任意一个street名称或街道编号,然后无法轻松将捕获的编号与捕获的名称关联 Regex在这方面做得很好,但当您试图将其应用程序扩展到超出其自然限制之外时,它并不漂亮。;-) 我想要一个能够找到并匹配的正则表达式……
- 街道名称是否也包含
、其他数字(0-9)
字符