diff options
author | Stuart Halloway <stu@thinkrelevance.com> | 2010-06-03 16:36:55 -0400 |
---|---|---|
committer | Stuart Halloway <stu@thinkrelevance.com> | 2010-06-03 21:21:00 -0400 |
commit | fde4f64bb5eb5c87b618b37e3d69ac37f1531524 (patch) | |
tree | 9fbcc70226b3eedaea7dbf726f5056957f2b5df4 | |
parent | fc8af8cb67b2240dd3fd0f391fcd1f718f3d90e3 (diff) |
#359 request from Stuart Sierra: split-lines, blank?, escape
Signed-off-by: Stuart Halloway <stu@thinkrelevance.com>
-rw-r--r-- | src/clj/clojure/string.clj | 59 | ||||
-rw-r--r-- | test/clojure/test_clojure/string.clj | 27 |
2 files changed, 79 insertions, 7 deletions
diff --git a/src/clj/clojure/string.clj b/src/clj/clojure/string.clj index dc061631..fc601638 100644 --- a/src/clj/clojure/string.clj +++ b/src/clj/clojure/string.clj @@ -8,17 +8,29 @@ (ns ^{:doc "Clojure String utilities -clojure.string adheres to the following design principles: +It is poor form to (:use clojure.string). Instead, use require +with :as to specify a prefix, e.g. + +(ns your.namespace.here + (:require '[clojure.string :as str])) + +Design notes for clojure.string: 1. Strings are objects (as opposed to sequences). As such, the string being manipulated is the first argument to a function; - passing nil will result in a NullPointerException. If you - want sequence-y behavior instead, use a sequence. + passing nil will result in a NullPointerException unless + documented otherwise. If you want sequence-y behavior instead, + use a sequence. 2. Functions are generally not lazy, and call straight to host methods where those are available and efficient. -3. When a function is documented to accept a string argument, it +3. Functions take advantage of String implementation details to + write high-performing loop/recurs instead of using higher-order + functions. (This is not idiomatic in general-purpose application + code.) + +4. When a function is documented to accept a string argument, it will take any implementation of the correct *interface* on the host platform. In Java, this is CharSequence, which is more general than String. In ordinary usage you will almost always @@ -66,7 +78,7 @@ clojure.string adheres to the following design principles: (instance? CharSequence match) (.replace s ^CharSequence match ^CharSequence replacement) (instance? Pattern match) (if (instance? CharSequence replacement) (.replaceAll (re-matcher ^Pattern match s) - (.toString replacement)) + (.toString ^CharSequence replacement)) (replace-by s match replacement)) :else (throw (IllegalArgumentException. (str "Invalid match arg: " match)))))) @@ -163,6 +175,12 @@ clojure.string adheres to the following design principles: ([ ^CharSequence s ^Pattern re limit] (LazilyPersistentVector/createOwning (.split re s limit)))) +(defn split-lines + "Splits s on \\n or \\r\\n." + {:added "1.2"} + [^CharSequence s] + (split s #"\r?\n")) + (defn ^String trim "Removes whitespace from both ends of string." {:added "1.2"} @@ -204,4 +222,33 @@ clojure.string adheres to the following design principles: (recur (dec index)) (.. s (subSequence 0 index) toString)))))) - +(defn blank? + "True if s is nil, empty, or contains only whitespace." + {:added "1.2"} + [^CharSequence s] + (if s + (loop [index (int 0)] + (if (= (.length s) index) + true + (if (Character/isWhitespace (.charAt s index)) + (recur (inc index)) + false))) + true)) + +(defn ^String escape + "Return a new string, using cmap to escape each character ch + from s as follows: + + If (cmap ch) is nil, append ch to the new string. + If (cmap ch) is non-nil, append (str (cmap ch)) instead." + {:added "1.2"} + [^CharSequence s cmap] + (loop [index (int 0) + buffer (StringBuilder. (.length s))] + (if (= (.length s) index) + (.toString buffer) + (let [ch (.charAt s index)] + (if-let [replacement (cmap ch)] + (.append buffer replacement) + (.append buffer ch)) + (recur (inc index) buffer))))) diff --git a/test/clojure/test_clojure/string.clj b/test/clojure/test_clojure/string.clj index ac5f5d1a..d6f6469d 100644 --- a/test/clojure/test_clojure/string.clj +++ b/test/clojure/test_clojure/string.clj @@ -92,4 +92,29 @@ "calvino" s/trim [" calvino "] "calvino " s/triml [" calvino "] " calvino" s/trimr [" calvino "] - "the end" s/trim-newline ["the end\r\n\r\r\n"])) + "the end" s/trim-newline ["the end\r\n\r\r\n"] + true s/blank? [" "] + ["a" "b"] s/split-lines ["a\nb"] + "fa la la" s/escape ["fo lo lo" {\o \a}])) + +(deftest t-escape + (is (= "<foo&bar>" + (s/escape "<foo&bar>" {\& "&" \< "<" \> ">"}))) + (is (= " \\\"foo\\\" " + (s/escape " \"foo\" " {\" "\\\""}))) + (is (= "faabor" + (s/escape "foobar" {\a \o, \o \a})))) + +(deftest t-blank + (is (s/blank? nil)) + (is (s/blank? "")) + (is (s/blank? " ")) + (is (s/blank? " \t \n \r ")) + (is (not (s/blank? " foo ")))) + +(deftest t-split-lines + (let [result (s/split-lines "one\ntwo\r\nthree")] + (is (= ["one" "two" "three"] result)) + (is (vector? result))) + (is (= (list "foo") (s/split-lines "foo")))) + |