用 Clojure 地图中的一组字符替换字符串中的字符

Replacing characters in a string, with a set of characters inside of a map in Clojure

我有一个接受字符串 s 和字符映射 charmap 的函数。如果字符串 s 中的任何字符在 charmap 内,请将字符替换为映射的值。

注意,映射中的键必须是字符串,而不是字符。

例如:

(replace-characters "Hi!" {"!" "Exclamation Mark"}) ;; => "HiExclamation Mark"

这是我目前使用的代码:

(defn- replace-characters
  "Replaces any characters in a string that are mapped in the given charmap"
  [s charmap]
  (let [character-set (set s)
        characters (filter #(contains? charmap (str %)) character-set)]
    (string/replace s (re-pattern (string/join "|" characters)) charmap)))

但是,我得到了一个 NullPointerException,我很困惑为什么。

java.lang.NullPointerException: Cannot invoke "String.indexOf(int)" because "s" is null

我想最好在纯 Clojure 中解决这个问题,而不是 Java,等等

Update: 所以上面的代码在 repl 中有效。这很好。但由于某种原因,以下是导致错误的原因:

(-> s
     (string/replace #"[:/?#\[\]@!$&'()*+,;=]" "")
     (replace-characters charmap)) ;; Where charmap is a large map of key value characters.

这是导致错误的表达式:

(str/replace "Hi" #"" {"!" "Exclamation Mark"})

("!" 被正则表达式替换为 ""character-set 的值为 #{\H \i}characters(),因此模式已创建re-pattern#""。)

空正则表达式匹配字母之间的每个 space:

(str/replace "Hi" #"" " ")

=> " H i "

因此,replace 正在 hash-map {"!" "Exclamation Mark"} 中寻找替代品,但没有找到任何东西 - 没有密钥 "":

(str/replace "Hi" #"" {"!" "Exclamation Mark"})
=> error

(str/replace "Hi" #"" {"" " "})
=> " H i "

一种可能的解决方案是简化 replace-characters 的定义(此解决方案仅适用于 non-empty charmap):

(defn replace-characters [s charmap]
  (str/replace s (re-pattern (str/join "|" (keys charmap)))
               charmap))

测试:

(replace-characters "Hi!" {"!" "Exclamation Mark"})

=> "HiExclamation Mark"

(-> "Hi!"
    (str/replace #"[:/?#\[\]@!$&'()*+,;=]" "")
    (replace-characters {"!" "Exclamation Mark"}))

=> "Hi"

我会选择像这样简单的东西:

(apply str (replace {"!" "[exclamation mark]"
                         "?" "[question mark]"}
                       (map str "is it possible? sure!")))

;;=> "is it possible[question mark] sure[exclamation mark]"

或者这样使用传感器:

(apply str (eduction (map str) (replace {"!" "[exclamation mark]"
                                                "?" "[question mark]"})
                        "is it possible? sure!"))

或者像这样:

(defn replace-characters [s rep]
  (apply str (map #(rep (str %) %) s)))

user> (replace-characters "is it possible? sure!" {"!" "[exclamation mark]"
                                                            "?" "[question mark]"})

;;=> "is it possible[question mark] sure[exclamation mark]"

string/escape 完全按照您的意愿行事:

(string/escape "Hi!" {\! "Exclamation Mark"})
;; => "HiExclamation Mark"

该函数使用从字符到替换的映射来替换字符。

如果你想替换正则表达式或字符串,你可以使用 reduce-kv in combination with string/replace:

(def replacements
  (array-map
   "!" "Exclamation Mark"
   #"[:/?#\[\]@!$&'()*+,;=]" ""
   #_more_replacements))

(defn replace [s replacements]
  (reduce-kv string/replace s replacements))

因此,您循环(按顺序,当 replacementsarray-map 时)替换的键和值,并使用 [=16= 将它们应用于字符串 s ].

(replace "Hi!" replacements)
;; => "HiExclamation Mark"

如果 array-map 中元素的顺序颠倒,则替换 !使用 "" (因为 ! 在正则表达式中)会成功:

(replace "Hi!" (into (array-map) (reverse replacements)))
;; => "Hi"