在clojure中将set转换为regex模式

时间:2016-04-01 02:44:34

标签: regex clojure

如果我有这套

(def my-set #{"foo.clj" "bar.clj" "baz.clj"})

如何将其转换为此模式字符串:

"foo\.clj|bar\.clj|baz\.clj"

我的尝试:

(defn set->pattern-str [coll] 
  (-> (clojure.string/join "|" coll) 
      (clojure.string/replace #"\." "\\\\.")))

(set->pattern-str my-set) 
=> "foo\\.clj|baz\\.clj|bar\\.clj" ;I get the double backslash

更好的想法?

3 个答案:

答案 0 :(得分:3)

如果您的字符串集合中可能包含其他元字符而不仅仅是.,则更常规的方法是ask the underlying java.util.regex.Pattern implementation to escape everything for us

(import 'java.util.regex.Pattern)

(defn set->pattern-str [coll] 
  (->> coll
    (map #(Pattern/quote %))
    (clojure.string/join \|)
     re-pattern))

IDEone link here。请记住,IDEone不是REPL,你必须告诉它将值放在stdout上,例如println之后才能看到它们。

答案 1 :(得分:2)

您接近最终解决方案。显示双反斜杠,因为它显示为已转义。当您将其变为seq时,您将看到单个字符:

(seq "foo\\.clj")
;;=> (\f \o \o \\ \. \c \l \j)

工作解决方案:

(def my-set #{"foo.clj" "bar.clj" "baz.clj"})

(def my-set-pattern
  (-> (clojure.string/join "|" my-set)
    (clojure.string/replace "." "\\.")
    (re-pattern)))

(re-matches my-set-pattern "foo.clj")
;;=> "foo.clj"

(re-matches my-set-pattern "bar.clj")
;;=> "bar.clj"

(re-matches my-set-pattern "baz.clj")
;;=> "baz.clj"

(re-matches my-set-pattern "foo-clj")
;;=> nil

答案 2 :(得分:0)

编辑:好的,这个确实有效。可能想要将它分开一点,如果它意味着长寿代码,但这是我能找到的最简单的方法,用最少的字符串重写。

(defn is-matching-file-name [target-string]
  (re-matches 
    (re-pattern (clojure.string/escape (String/join "|" my-set) {\. "\\."}))
    target-string))

clojure.string / escape在这里有两个参数:要转义的字符串,以及要转义到替换字符串的字符的映射。这个映射中的键是文字\.,并且值需要两个反斜杠,因为我们希望在最终字符串中包含任意.之前的一个反斜杠,以用作重新模式函数的参数。