最近 ,我一直在使用Clojure和Enlive抓取网页 ,作为其中的一部分,我不得不编写正则表达式来提取我感兴趣的数据。
在旅行中,我遇到了一些不同的功能,而且我不确定要使用哪个功能,因此我想我会记录一下为将来我所尝试的功能。
检查正则表达式是否匹配
我写的第一个正则表达式是在从Rec.Sport.Soccer Statistics Foundation废弃欧洲冠军联赛的结果时 ,我想确定哪些跨度包含比赛结果,哪些没有。
匹配的行如下所示:
Real Madrid-Juventus Turijn 2 - 1
和这样的不匹配:
53’Nedved 0-1, 66'Xavi Hernández 1-1, 114’Zalayeta 1-2
我编写了以下正则表达式来检测匹配结果:
[a-zA-Z\s]+-[a-zA-Z\s]+ [0-9][\s]?.[\s]?[0-9]
然后,我使用重新匹配编写了以下函数,根据输入,该匹配将返回true或false:
(defn recognise-match? [row]
(not (clojure.string/blank? (re-matches #"[a-zA-Z\s]+-[a-zA-Z\s]+ [0-9][\s]?.[\s]?[0-9]" row))))
> (recognise-match? "Real Madrid-Juventus Turijn 2 - 1")
true
> (recognise-match? "53’Nedved 0-1, 66'Xavi Hernández 1-1, 114’Zalayeta 1-2")
false
仅当整个字符串与模式匹配时, 重新匹配才返回匹配,这意味着如果在分数之后我们有一行带有一些虚假文本的行将不匹配:
> (recognise-match? "Real Madrid-Juventus Turijn 2 - 1 abc")
false
如果我们不介意,而只希望字符串的某些部分与我们的模式匹配,则可以使用re-find代替:
(defn recognise-match? [row]
(not (clojure.string/blank? (re-find #"[a-zA-Z\s]+-[a-zA-Z\s]+ [0-9][\s]?.[\s]?[0-9]" row))))
> (recognise-match? "Real Madrid-Juventus Turijn 2 - 1 abc")
true
提取捕获组
我接下来要做的是捕获最初使用re-seq进行的比赛的队伍和得分:
> (first (re-seq #"([a-zA-Z\s]+)-([a-zA-Z\s]+) ([0-9])[\s]?.[\s]?([0-9])" "FC Valencia-Internazionale Milaan 2 - 1"))
["FC Valencia-Internazionale Milaan 2 - 1" "FC Valencia" "Internazionale Milaan" "2" "1"]
然后,我像这样提取各个部分:
> (def result (first (re-seq #"([a-zA-Z\s]+)-([a-zA-Z\s]+) ([0-9])[\s]?.[\s]?([0-9])" "FC Valencia-Internazionale Milaan 2 - 1")))
> result
["FC Valencia-Internazionale Milaan 2 - 1" "FC Valencia" "Internazionale Milaan" "2" "1"]
> (nth result 1)
"FC Valencia"
> (nth result 2)
"Internazionale Milaan"
re-seq返回一个包含正则表达式连续匹配项的列表。 如果不指定捕获组,则列表将包含字符串,或者包含匹配模式和每个捕获组的向量。
例如,如果我们现在仅匹配AZ或空格序列,并从上方删除其余模式,则将得到以下结果:
> (re-seq #"([a-zA-Z\s]+)" "FC Valencia-Internazionale Milaan 2 - 1")
(["FC Valencia" "FC Valencia"] ["Internazionale Milaan " "Internazionale Milaan "] [" " " "] [" " " "])
> (re-seq #"[a-zA-Z\s]+" "FC Valencia-Internazionale Milaan 2 - 1")
("FC Valencia" "Internazionale Milaan " " " " ")
在我们的情况下, 重新查找或重新匹配实际上更有意义,因为我们只想匹配一次模式。 如果之后有其他匹配项,则不包含在结果中。 例如
> (re-find #"[a-zA-Z\s]+" "FC Valencia-Internazionale Milaan 2 - 1")
"FC Valencia"
> (re-matches #"[a-zA-Z\s]*" "FC Valencia-Internazionale Milaan 2 - 1")
nil
重新匹配在此处返回nil,因为字符串中存在与模式不匹配的字符,即两个乐谱之间的连字符。
如果我们将其与捕获组联系在一起,则会得到以下结果:
> (def result
(re-find #"([a-zA-Z\s]+)-([a-zA-Z\s]+) ([0-9])[\s]?.[\s]?([0-9])" "FC Valencia-Internazionale Milaan 2 - 1"))
> result
["FC Valencia-Internazionale Milaan 2 - 1" "FC Valencia" "Internazionale Milaan" "2" "1"]
> (nth result 1)
"FC Valencia"
> (nth result 2)
"Internazionale Milaan"
我还遇到了re-pattern函数,该函数提供了一种更详细的方式来创建模式,然后使用re-find对其进行评估:
> (re-find (re-pattern "([a-zA-Z\\s]+)-([a-zA-Z\\s]+) ([0-9])[\\s]?.[\\s]?([0-9])") "FC Valencia-Internazionale Milaan 2 - 1")
["FC Valencia-Internazionale Milaan 2 - 1" "FC Valencia" "Internazionale Milaan" "2" "1"]
这里的区别是我必须转义特殊的序列'\ s'否则我将收到以下异常:
RuntimeException Unsupported escape character: \s clojure.lang.Util.runtimeException (Util.java:170)
我也想和重组一起玩,但是当我期望它正常工作时,这似乎经常会引发异常。
我看过的最后一个函数是重新匹配器 ,它似乎是本文前面定义匹配器的“#””语法的惯用语:
> (re-find (re-matcher #"([a-zA-Z\s]+)-([a-zA-Z\s]+) ([0-9])[\s]?.[\s]?([0-9])" "FC Valencia-Internazionale Milaan 2 - 1"))
["FC Valencia-Internazionale Milaan 2 - 1" "FC Valencia" "Internazionale Milaan" "2" "1"]
综上所述
因此,总而言之,我认为大多数用例都包含在重新查找和重新匹配以及在特殊情况下可能重新排序的范围内 。 我看不到要在哪里使用其他功能,但很高兴能被证明是错误的。
翻译自: https://www.javacodegeeks.com/2013/09/clojure-all-things-regex.html