@@ -36,11 +36,11 @@ Design notes for clojure.string:
3636 general than String. In ordinary usage you will almost always
3737 pass concrete strings. If you are doing something unusual,
3838 e.g. passing a mutable implementation of CharSequence, then
39- thead -safety is your responsibility."
39+ thread -safety is your responsibility."
4040 :author " Stuart Sierra, Stuart Halloway, David Liebke" }
4141 clojure.string
4242 (:refer-clojure :exclude (replace reverse))
43- (:import (java.util.regex Pattern)
43+ (:import (java.util.regex Pattern Matcher )
4444 clojure.lang.LazilyPersistentVector))
4545
4646(defn ^String reverse
@@ -49,16 +49,26 @@ Design notes for clojure.string:
4949 [^CharSequence s]
5050 (.toString (.reverse (StringBuilder. s))))
5151
52+ (defn ^String re-quote-replacement
53+ " Given a replacement string that you wish to be a literal
54+ replacement for a pattern match in replace or replace-first, do the
55+ necessary escaping of special characters in the replacement."
56+ {:added " 1.4" }
57+ [^CharSequence replacement]
58+ (Matcher/quoteReplacement (.toString ^CharSequence replacement)))
59+
5260(defn- replace-by
5361 [^CharSequence s re f]
5462 (let [m (re-matcher re s)]
55- (let [buffer (StringBuffer. (.length s))]
56- (loop []
57- (if (.find m)
58- (do (.appendReplacement m buffer (f (re-groups m)))
59- (recur ))
60- (do (.appendTail m buffer)
61- (.toString buffer)))))))
63+ (if (.find m)
64+ (let [buffer (StringBuffer. (.length s))]
65+ (loop [found true ]
66+ (if found
67+ (do (.appendReplacement m buffer (Matcher/quoteReplacement (f (re-groups m))))
68+ (recur (.find m)))
69+ (do (.appendTail m buffer)
70+ (.toString buffer)))))
71+ s)))
6272
6373(defn ^String replace
6474 " Replaces all instance of match with replacement in s.
@@ -69,7 +79,21 @@ Design notes for clojure.string:
6979 char / char
7080 pattern / (string or function of match).
7181
72- See also replace-first."
82+ See also replace-first.
83+
84+ The replacement is literal (i.e. none of its characters are treated
85+ specially) for all cases above except pattern / string.
86+
87+ For pattern / string, $1, $2, etc. in the replacement string are
88+ substituted with the string that matched the corresponding
89+ parenthesized group in the pattern. If you wish your replacement
90+ string r to be used literally, use (re-quote-replacement r) as the
91+ replacement argument. See also documentation for
92+ java.util.regex.Matcher's appendReplacement method.
93+
94+ Example:
95+ (clojure.string/replace \" Almost Pig Latin\" #\"\\ b(\\ w)(\\ w+)\\ b\" \" $2$1ay\" )
96+ -> \" lmostAay igPay atinLay\" "
7397 {:added " 1.2" }
7498 [^CharSequence s match replacement]
7599 (let [s (.toString s)]
@@ -85,12 +109,13 @@ Design notes for clojure.string:
85109(defn- replace-first-by
86110 [^CharSequence s ^Pattern re f]
87111 (let [m (re-matcher re s)]
88- (let [buffer (StringBuffer. (.length s))]
89- (if (.find m)
90- (let [rep (f (re-groups m))]
91- (.appendReplacement m buffer rep)
92- (.appendTail m buffer)
93- (str buffer))))))
112+ (if (.find m)
113+ (let [buffer (StringBuffer. (.length s))
114+ rep (Matcher/quoteReplacement (f (re-groups m)))]
115+ (.appendReplacement m buffer rep)
116+ (.appendTail m buffer)
117+ (str buffer))
118+ s)))
94119
95120(defn- replace-first-char
96121 [^CharSequence s ^Character match replace]
@@ -100,6 +125,14 @@ Design notes for clojure.string:
100125 s
101126 (str (subs s 0 i) replace (subs s (inc i))))))
102127
128+ (defn- replace-first-str
129+ [^CharSequence s ^String match ^String replace]
130+ (let [^String s (.toString s)
131+ i (.indexOf s match)]
132+ (if (= -1 i)
133+ s
134+ (str (subs s 0 i) replace (subs s (+ i (.length match)))))))
135+
103136(defn ^String replace-first
104137 " Replaces the first instance of match with replacement in s.
105138
@@ -109,16 +142,31 @@ Design notes for clojure.string:
109142 string / string
110143 pattern / (string or function of match).
111144
112- See also replace-all."
145+ See also replace.
146+
147+ The replacement is literal (i.e. none of its characters are treated
148+ specially) for all cases above except pattern / string.
149+
150+ For pattern / string, $1, $2, etc. in the replacement string are
151+ substituted with the string that matched the corresponding
152+ parenthesized group in the pattern. If you wish your replacement
153+ string r to be used literally, use (re-quote-replacement r) as the
154+ replacement argument. See also documentation for
155+ java.util.regex.Matcher's appendReplacement method.
156+
157+ Example:
158+ (clojure.string/replace-first \" swap first two words\"
159+ #\" (\\ w+)(\\ s+)(\\ w+)\" \" $3$2$1\" )
160+ -> \" first swap two words\" "
113161 {:added " 1.2" }
114162 [^CharSequence s match replacement]
115163 (let [s (.toString s)]
116164 (cond
117165 (instance? Character match)
118166 (replace-first-char s match replacement)
119167 (instance? CharSequence match)
120- (.replaceFirst s (Pattern/quote ( .toString ^CharSequence match) )
121- (.toString ^CharSequence replacement))
168+ (replace-first-str s (.toString ^CharSequence match)
169+ (.toString ^CharSequence replacement))
122170 (instance? Pattern match)
123171 (if (instance? CharSequence replacement)
124172 (.replaceFirst (re-matcher ^Pattern match s)
0 commit comments