我想提出一種解決像這樣的問題的方法。主要的任務是可以用與其他Ruby代碼相同的方式構造複雜的正則表達式:創建可輕鬆測試的小代碼模塊,然後組合這些模塊。
考慮必須匹配正則表達式的第一個字符串。
s = "201-Grandview-Dr_Early_TX_76802"
與此字符串不包含字符需要進行轉義,我們可以創建一個正則表達式,將正好由僅僅用斜槓替換雙引號並添加啓動的字符串此字符串匹配(\A
)和結束串(\z
)主播:
r = /\A201-Grandview-Dr_Early_TX_76802\z/
#=> /\A201-Grandview-Dr_Early_TX_76802\z/
s =~ r
#=> 0
這是我們所擁有的:
/\A201-Grandview\-Dr_Early_TX_76802\z/
⬆︎street number
⬆︎street name
⬆︎street name suffix
⬆︎city
⬆︎state
⬆︎zip
想必正則表達式應該墊查找字符串當且僅當字符串包含這六個字段中的每一個的允許值並在相鄰字段之間顯示格式。
讓我們先爲六個字段中的每一個規定一個單獨的正則表達式。自然,所有這些正則表達式可能都需要進行修改以適應需求。
門牌號碼
典型街道數字可能是 「221」, 「221B」, 「221B」。假設我們也可能有「A19」或「221BZ」而不是「221-B」。然後,我們可能會這樣寫:
number = /[[:alnum:]]+/
(在Regexp搜索「POSIX」)
街道名稱
我假設的街道名稱由一個分隔的單個詞或多個單詞的單個空格,其中每個單詞都是小寫,除了首字母大寫。
street = /[[:upper:]][[:lower:]]+(?:\s[[:upper:]][[:lower:]]+)*/
/[[:upper:]][[:lower:]]+
的第一個字相匹配,(?:\s[[:upper:]][[:lower:]])*
的空間,隨後通過一個大寫字相匹配時,重複零次或更多次((?:...)
是非捕獲基團。尾隨*
裝置重複零次或多次。)
街道名稱後綴
我承擔的街道名稱後綴(如,「街」,「聖「)是一個字,除了第一個字符,這是大寫,任選一個週期結束全部小寫:
suffix = /[[:upper:]][[:lower:]]+\.?/
市
我假定城市的名字有相同的要求做的街道名稱:
city = street
#=> /[[:upper:]][[:lower:]]+(?:\s[[:upper:]][[:lower:]]+)*/
國家
個國家由兩個大寫字母給出:
state = /[[:upper:]]{2}/
我們可以通過書面形式更精確:
state = Regexp.union %w| AL AK AZ ... |
但後來我們不得不每一個領土成爲一個新的狀態或時間進行更新(可能是由於最近的事件)一個國家從工會中脫身。
郵編
郵政編碼是五位數字或九個數字與第一四位數字之後的破折號或連字符。
zip = /\d{5}(?:-\d{4})?/
使用
/\A201-Grandview-Dr_Early_TX_76802\z/
爲我們的模式,我們的整體正則表達式因此如下:
r1 =/
\A # match start of string
#{number}
-
#{street}
-
#{suffix}
_
#{city}
_
#{state}
_
#{zip}
\z # match end of string
/x # free-spacing regex definition mode
#=>/
# \A # match start of string
# /(?-mix:[[:alnum:]]+)
# -
# (?-mix:[[:upper:]][[:lower:]]+(?:\s[[:upper:]][[:lower:]]+)*)
# -
# (?-mix:[[:upper:]][[:lower:]]+\.?)
# _
# (?-mix:[[:upper:]][[:lower:]]+(?:\s[[:upper:]][[:lower:]]+)*)
# _
# (?-mix:[[:upper:]]{2})
# _
# (?-mix:\d{5}(?:-\d{4})?)
# \z # match start of string
/x
讓我們試試它的第一個字符串和變體:
"201-Grandview-Dr_Early_TX_76802" =~ r1
#=> 0
"221B-Grand View-Dr._El Paso_TX_76802-0000" =~ r1
#=> 0
"2A0B1-Grandview-Dr_Early_ZZ_76802" =~ r1
#=> 0
"201-GrandView-Dr_Early_TX_76802" =~ r1
#=> nil
"201-Grandview-Dr_Early_TX_7680" =~ r1
#=> nil
"201-Pi11ar-St_Early_TX_76802" =~ r1
#=> nil
"I live at 201-Grandview-Dr_Early_TX_76802" =~ r1
#=> nil
"201-mg Circle-Lane_Early_TX_76802" =~ r1
#=> nil
現在考慮第二個爲此應該有一個匹配的字符串,例如:
"/50-Washington-St"
我們看到了正則表達式這簡直是
r2 =/
\A
\/
#{number}
-
#{street}
-
#{suffix}
\z
/x
#=>/
# \A
# \/
# (?-mix:[[:alnum:]]+)
# -
# (?-mix:[[:upper:]][[:lower:]]+(?:\s[[:upper:]][[:lower:]]+)*)
# -
# (?-mix:[[:upper:]][[:lower:]]+\.?)
# \z
# /x
讓我們試試吧。
"/50-Washington-St" =~ r2
#=> 0
"50-Washington-St" =~ r2
#=> nil
"/50-Washington-St_Early" =~ r2
#=> nil
所以,現在我們的整體正則表達式是簡單
r = Regexp.union(r1,r2)
#=> /(?x-mi:
# \A # match start of string
# (?-mix:[[:alnum:]]+)
# -
# (?-mix:[[:upper:]][[:lower:]]+(?:\s[[:upper:]][[:lower:]]+)*)
# -
# (?-mix:[[:upper:]][[:lower:]]+\.?)
# _
# (?-mix:[[:upper:]][[:lower:]]+(?:\s[[:upper:]][[:lower:]]+)*)
# _
# (?-mix:[[:upper:]]{2})
# _
# (?-mix:\d{5}(?:-\d{4})?)
# \z # match end of string
# )|(?x-mi:
# \A
# \/
# (?-mix:[[:alnum:]]+)
# -
# (?-mix:[[:upper:]][[:lower:]]+(?:\s[[:upper:]][[:lower:]]+)*)
# -
# (?-mix:[[:upper:]][[:lower:]]+\.?)
# \z
# )/
"201-Grandview-Dr_Early_TX_76802" =~ r
#=> 0
"/50-Washington-St" =~ r
#=> 0
什麼是規則,模式? –
你想要匹配什麼? –
@NickolayKondratenko我想匹配這兩個字符串。即帶有連字符的字符串,也帶有下劃線和末尾的數字字符串 – rubyist