在Clojure中转义括号

时间:2012-07-26 14:45:35

标签: regex string clojure

如果我试试这个

(import java.util.regex.Pattern)
(Pattern/compile ")!@#$%^&*()")

或者

(def p #")!@#$%^&*()")

我有Clojure抱怨有一个无与伦比的/未闭合的)。为什么在这个简单的字符串中评估括号?怎么逃避他们?感谢

编辑:虽然转义符合特定于语法的语法(#""),但它无法使用我需要的Pattern/compile语法,因为我必须从字符串动态编译正则表达式模式。

我尝试过使用re-pattern,但出于某种原因我无法正常逃脱:

(re-pattern "\)!@#$%^&*\(\)")
    java.lang.Exception: Unsupported escape character: \)
    java.lang.Exception: Unable to resolve symbol: ! in this context (NO_SOURCE_FILE:0)
    java.lang.Exception: No dispatch macro for: $
    java.lang.Exception: Unable to resolve symbol: % in this context (NO_SOURCE_FILE:0)
    java.lang.IllegalArgumentException: Metadata can only be applied to IMetas

编辑2 这个小功能可能会有所帮助:

(defn escape-all [x]
    (str "\\" (reduce #(str  %1 "\\" %2) x)))

3 个答案:

答案 0 :(得分:10)

我通过双重逃避一切来实现它。哦,双重逃避的乐趣。

=> (re-pattern "\\)\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)")
=> #"\)\!\@\#\$\%\^\&\*\(\)"

=> (re-find (re-pattern "\\)\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)")
            ")!@#$%^&*()")
=> ")!@#$%^&*()"

我建议编写一个辅助函数str-to-pattern(或者你想要的任何东西),它接受一个字符串,双重转义所需的一切,然后在其上调用re-pattern

编辑:制作字符串以模式化功能
有很多方法可以做到这一点,下面只是一个例子。我首先将正则表达式转义为字符串替换。 “smap”不是实际类型,但从功能上来说,它是一个我们将用“旧值”与“新值”交换的地图,其中“旧值”是smap键的成员,以及“新值”是相应的smap的成员。在我们的例子中,这个smap看起来像{\( "\\(", \) "\\)" ...}

(def regex-char-esc-smap
  (let [esc-chars "()*&^%$#!"]
    (zipmap esc-chars
            (map #(str "\\" %) esc-chars))))

接下来是实际功能。我使用上面的smap替换传递给它的字符串中的项目,然后将其转换回字符串并从中生成正则表达式模式。我认为->>宏使代码更具可读性,但这只是个人偏好。

(defn str-to-pattern
  [string]
  (->> string
       (replace regex-char-esc-smap)
       (reduce str)
       re-pattern))

答案 1 :(得分:3)

你确定错误来自读者(即来自clojure本身)吗?

regexps使用括号,它们也必须匹配。我猜这个错误来自试图编译正则表达式的代码。

如果你想在正则表达式中逃避paren,请使用反引号:(def p #"\)!@#$%^&*\(\)")

[更新]啊,对不起,你可能需要像Omri那样双重逃脱。

答案 2 :(得分:2)

Clojure支持的所有Java版本都会识别\Q以启动引用区域,\E来结束引用区域。这允许你做这样的事情:

(re-find #"\Q)!@#$%^&*()\E" ")!@#$%^&*()")

如果你正在使用(re-pattern),那么这将有效:

(re-find (re-pattern "\\Q)!@#$%^&*()\\E") ")!@#$%^&*()")

如果您正在组装一个您不知道其内容的字符串的正则表达式,那么您可以使用quote中的java.util.regex.Pattern方法:

(re-find (re-pattern (java.util.regex.Pattern/quote some-str)) some-other-str)

以下是我的REPL的一个例子:

user> (def the-string ")!@#$%^&*()")
#'user/the-string
user> (re-find (re-pattern (java.util.regex.Pattern/quote the-string)) the-string)
")!@#$%^&*()"