2013-09-26 99 views
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,但只是在一個正則表達式中。

+0

是否有你可以有一個給定名稱的街道號碼的數量有限制嗎? –

+0

街道名稱中是否有數字? IE「3rd Street」或「5th Ave」? – AGS

+0

第三個例子中的'84e'怎麼處理? – Walls

回答

2

您可以使用正則表達式來找到所有的數字,與他們分隔符:

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在字符串
  • (…)奪的前開始結果
    • .+?查找一個或多個字符,少至po可以讓這個模式的其餘部分相匹配。
  • \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?這是它的目的。

+0

我不想使用掃描,除了有一個正則表達式的原因,只是因爲如果我可以在一個階段而不是兩個做一些事情,我寧願。當然不是不惜一切代價。 @ PeterAlfvin的回答告訴我,用正則表達式來做這件事會是一回事。 – mjnissim

1

如果您希望第三個示例正常工作,則需要更改[a-d]以在該範圍內包含e。改變後你可以使用(\D{2,})\s+(\d{1,3}[a-e]?(?:[,\s]{1,3})*)*。用你給我的例子做了一些testing using Rubular

使用一些分組,您可以對那些過去幾年的條件(這似乎是相當棘手的重複。這樣在最後的間距和逗號將陷入在重複之後最初消耗的空間。

1

限制只能捕獲重複表達式的最後一個實例的唯一方法是爲單個實例編寫正則表達式,並讓正則表達式計算機爲您執行重複操作,就像全局替換選項所發生的那樣,公認地類似於掃描,不幸的是,在這種情況下,您必須匹配街道名稱或街道號碼,然後無法輕鬆將捕獲的數字與捕獲的名稱關聯起來

正則表達式在它的功能上很出色,但是當你嘗試擴展它的應用程序超出它的自然限制時,它並不漂亮。 ;-)

1

我希望有一個正則表達式,將發現和匹配....

  • 執行街道名稱也包含digits (0-9),其他characters撇號旁邊?
  • 街道號碼是否基於任意數據?它總是隻是一個可選的a,b,cd
  • 您是否需要字符串長度的最小和最大限制?

這裏有一些可能的選項:

如果你不確定什麼街道名稱中包含的,但知道您的號碼模式將是一個可選的字母,逗號或空格的數字。

/^(.*?)\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