2012-04-08 186 views
11

有沒有一種在Clojure中以十六進制編碼和解碼字符串的習慣用法?從Python的例子:Clojure等價於Python的編碼('十六進制')和解碼('十六進制')

'Clojure'.encode('hex') 
# ⇒ '436c6f6a757265' 
'436c6f6a757265'.decode('hex') 
# ⇒ 'Clojure' 

爲了表示對我而言一些努力:

(defn hexify [s] 
    (apply str 
    (map #(format "%02x" (int %)) s))) 

(defn unhexify [hex] 
    (apply str 
    (map 
     (fn [[x y]] (char (Integer/parseInt (str x y) 16))) 
     (partition 2 hex)))) 

(hexify "Clojure") 
;; ⇒ "436c6f6a757265" 

(unhexify "436c6f6a757265") 
;; ⇒ "Clojure" 
+0

使用Java庫前導零? – Marcin 2012-04-08 13:02:30

+0

你已經擁有它 – Ankur 2012-04-08 14:30:17

+0

@Ankur:顯然不是sw1nn的答案顯示 - 這就是爲什麼我想要一個現有的功能,如果可能的話。 – 2012-04-15 18:30:54

回答

5

我相信你unhexify功能是地道的,因爲它可以。然而,hexify可以寫成一個簡單的方法:

(defn hexify [s] 
    (format "%x" (new java.math.BigInteger (.getBytes s)))) 
+0

好的,謝謝,如果沒有內置的處理方法,我會使用你的建議 – 2012-04-10 06:54:11

+2

這種格式的字節數組的第一位是1,爲「負數」。 – 2015-03-22 18:44:55

+0

我也習慣使用這種方法,直到我意識到前導零被移除。 – mattias 2017-05-23 07:09:40

14

你的實現(S)不用於非ASCII字符工作,

(defn hexify [s] 
    (apply str 
    (map #(format "%02x" (int %)) s))) 

(defn unhexify [hex] 
    (apply str 
    (map 
     (fn [[x y]] (char (Integer/parseInt (str x y) 16))) 
     (partition 2 hex)))) 

(= "\u2195" (unhexify(hexify "\u2195"))) 
false ; should be true 

爲了克服這一點,你需要序列化的字節該字符串使用所需的字符編碼,每個字符可以是多字節。

這有幾個「問題」。

  • 請記住,所有數字類型都在JVM中籤名。
  • 沒有無符號字節。

在慣用的java中,你會使用一個整數的低位字節並將它掩蓋起來,就像你使用它的地方一樣。

int intValue = 0x80; 
    byte byteValue = (byte)(intValue & 0xff); -- use only low byte 

    System.out.println("int:\t" + intValue); 
    System.out.println("byte:\t" + byteValue); 

    -- output: 
    -- int: 128 
    -- byte: -128 

clojure有(unchecked-byte)有效地做同樣的事情。

例如,使用UTF-8,你可以這樣做:

(defn hexify [s] 
    (apply str (map #(format "%02x" %) (.getBytes s "UTF-8")))) 

(defn unhexify [s] 
    (let [bytes (into-array Byte/TYPE 
       (map (fn [[x y]] 
        (unchecked-byte (Integer/parseInt (str x y) 16))) 
         (partition 2 s)))] 
    (String. bytes "UTF-8"))) 

; with the above implementation: 

;=> (hexify "\u2195") 
"e28695" 
;=> (unhexify "e28695") 
"↕" 
;=> (= "\u2195" (unhexify (hexify "\u2195"))) 
true 
+0

只要性能沒有問題,所有這一切都很好 - 我敢打賭,Python示例將在任何更長的字符串上超過這些解決方案。如果你需要表現,還有很多工作要做。 – 2012-04-10 13:35:18

10

由於所有發佈的解決方案有一些缺陷,我分享我自己:

(defn hexify "Convert byte sequence to hex string" [coll] 
    (let [hex [\0 \1 \2 \3 \4 \5 \6 \7 \8 \9 \a \b \c \d \e \f]] 
     (letfn [(hexify-byte [b] 
     (let [v (bit-and b 0xFF)] 
      [(hex (bit-shift-right v 4)) (hex (bit-and v 0x0F))]))] 
     (apply str (mapcat hexify-byte coll))))) 

(defn hexify-str [s] 
    (hexify (.getBytes s))) 

(defn unhexify "Convert hex string to byte sequence" [s] 
     (letfn [(unhexify-2 [c1 c2] 
       (unchecked-byte 
        (+ (bit-shift-left (Character/digit c1 16) 4) 
         (Character/digit c2 16))))] 
    (map #(apply unhexify-2 %) (partition 2 s)))) 

(defn unhexify-str [s] 
    (apply str (map char (unhexify s)))) 

優點:

  • 高性能
  • 通用的字節流< - >字符串轉換有專門的包裝
  • 處理以十六進制結果