A regular expression can do this if your curly braces are not nested.
For example:
(require '[clojure.string :as str])
(def s "a,b,{c,d},e,{f,g,h},i")
(str/split s #",(?![^{}]*\})")
;; => ["a" "b" "{c,d}" "e" "{f,g,h}" "i"]
How it works
The regex:
,(?![^{}]*\})
matches a comma only if it is not followed by a closing } before another {.
, — match a comma.
(?!...) — negative lookahead.
[^{}]*\} — "zero or more non-brace characters followed by }."
This effectively says: "don't split on commas that are inside a single pair of braces."
Limitation
This only works for non-nested braces.
For example, it fails on:
a,{b,{c,d},e},f
because regular expressions (including Java's, which Clojure uses) cannot match arbitrarily nested structures.
If braces can be nested
A simple parser is more robust:
(defn split-outside-braces [s]
(loop [chars (seq s)
depth 0
current []
result []]
(if-let [c (first chars)]
(case c
\{ (recur (next chars)
(inc depth)
(conj current c)
result)
\} (recur (next chars)
(dec depth)
(conj current c)
result)
\, (if (zero? depth)
(recur (next chars)
depth
[]
(conj result (apply str current)))
(recur (next chars)
depth
(conj current c)
result))
(recur (next chars)
depth
(conj current c)
result))
(conj result (apply str current)))))
(split-outside-braces "a,b,{c,d},e,{f,{g,h},i},j")
;; => ["a" "b" "{c,d}" "e" "{f,{g,h},i}" "j"]
This approach correctly handles nested braces of arbitrary depth and is generally preferable if the input can contain nesting.