2012-04-08 8 views
11

Esiste un modo idiomatico di codificare e decodificare una stringa in Clojure come esadecimale? Esempio da Python:L'equivalente di Clojure alla codifica di Python ('hex') e decodifica ('hex')

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

Per mostrare un certo sforzo da parte mia:

(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

Utilizzare una libreria Java? – Marcin

+0

Lo hai già – Ankur

+0

@Ankur: Apparentemente non come la risposta di sw1nn mostra: è per questo che vorrei una funzione esistente se possibile. –

risposta

5

Credo che la vostra funzione unhexify è idiomatica come può essere. Tuttavia, hexify può essere scritto in un modo più semplice:

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

OK grazie, userò il tuo suggerimento se non c'è un modo integrato di gestirlo. –

+2

Questo formatta array di byte dove il primo bit è 1 come "negativo". –

+0

Ho usato anche questo metodo fino a quando non ho realizzato che gli zeri iniziali sono stati rimossi. – mattias

14

L'implementazione (s) non funzionano per i caratteri non 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 

Per superare questo è necessario serializzare i byte di la stringa che utilizza la codifica dei caratteri richiesta, che può essere multi-byte per carattere.

Ci sono alcuni "problemi" con questo.

  • Ricordare che tutti i tipi numerici sono firmati nella JVM.
  • Non c'è un byte senza segno.

In java idiomatico si utilizza il byte basso di un numero intero e lo si maschera in questo modo ovunque lo si utilizzi.

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 ha (unchecked-byte) di fare effettivamente la stessa.

Ad esempio, utilizzando UTF-8 si può fare questo:

(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

Tutto questo va bene fintanto che le prestazioni non sono un problema: scommetto che l'esempio Python supererà queste soluzioni su una stringa più lunga. Se hai bisogno di prestazioni, c'è molto più lavoro da fare. –

10

Poiché tutte le soluzioni postate hanno alcuni difetti, sto condividendo la mia:

(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))) 

e

(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)))) 

Pro:

  • < alte prestazioni
  • flusso di byte Generico - conversioni> stringa con involucri specializzati
  • Manipolazione zero nel risultato esadecimale