Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Escaping brackets in Clojure

If I try this

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

or this

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

I have Clojure complaining that there is an unmatched / unclosed ). Why are brackets evaluated within this simple string? How to escape them? Thanks

EDIT: While escaping works in the clojure-specific syntax (#""), it doesn't work with the Pattern/compile syntax that I do need because I have to compile the regex patter dynamically from a string.

I've tried with re-pattern, but I can't escape properly for some reason:

(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

EDIT 2 This little function may help:

(defn escape-all [x]
    (str "\\" (reduce #(str  %1 "\\" %2) x)))
like image 804
pistacchio Avatar asked Jul 26 '12 14:07

pistacchio


2 Answers

I got it working by double escaping everything. Oh the joys of double escaping.

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

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

I would recommend writing a helper function str-to-pattern (or whatever you want to call it), that takes a string, double escapes everything it needs to, and then calls re-pattern on it.

Edit: making a string to pattern function
There are plenty of ways to do this, below is just one example. I start by making an smap of regex escape chars to their string replacement. An "smap" isn't an actual type, but functionally it's a map we will use to swap "old values" with "new values", where "old values" are members of the keys of the smap, and "new values" are corresponding members of the vals of smap. In our case, this smap looks like {\( "\\(", \) "\\)" ...}.

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

Next is the actual function. I use the above smap to replace items in the string passed to it, then convert that back into a string and make a regex pattern out of it. I think the ->> macro makes the code more readable, but that's just a personal preference.

(defn str-to-pattern
  [string]
  (->> string
       (replace regex-char-esc-smap)
       (reduce str)
       re-pattern))
like image 195
Omri Bernstein Avatar answered Oct 15 '22 18:10

Omri Bernstein


are you sure the error is from the reader (ie from clojure itself)?

regexps use parentheses, and they have to match there too. i would guess the error is cominng from the code trying to compile the regexp.

if you want to escape a paren in a regexp, use a backquote: (def p #"\)!@#$%^&*\(\)")

[update] ah, sorry, you probably need double escapes as Omri days.

like image 32
andrew cooke Avatar answered Oct 15 '22 18:10

andrew cooke