How to convert hex string to ASCII using elisp?

Today I received a response to one of my letters as a string of hex bytes:

"686170707920333974682068617665206120676f6f64206f6e6521" 

And I was thinking of the most efficient clean way to convert a string to the ASCII equivalent. I will add my answer to the question, but I did not feel that it was as elegant as it could be.

+4
source share
7 answers

Here's an iterative solution

 (defun decode-hex-string (hex-string) (let ((res nil)) (dotimes (i (/ (length hex-string) 2) (apply #'concat (reverse res))) (let ((hex-byte (substring hex-string (* 2 i) (* 2 (+ i 1))))) (push (format "%c" (string-to-number hex-byte 16)) res))))) 

And using loop if you want to avoid side effects (you might need (require 'cl) to use this):

 (defun decode-hex-string (hex-string) (apply #'concat (loop for i from 0 to (- (/ (length hex-string) 2) 1) for hex-byte = (substring hex-string (* 2 i) (* 2 (+ i 1))) collect (format "%c" (string-to-number hex-byte 16))))) 

In general, it is better to avoid recursion in Elisp and Common Lisp; your stack will keel with a sufficiently large amount of input, and none of the languages ​​guarantees tail recursion (which you do not use, but still). In Scheme, this is a different story.

By the way, Happy 39th.

+8
source

If you are using the Magnar Sveen dash.el (and you should), try:

 (concat (--map (string-to-number (concat it) 16) (-partition 2 (string-to-list "686170707920333974682068617665206120676f6f64206f6e6521")))) 

the solution uses the Emacs functions string-to-number , string-to-list and concat and dash.el function and the anaphoric version -map . What's good about concat is that it combines not only strings, but also lists or character vectors. We can rewrite this code with the macro ->> . It takes the result of the 1st argument, then applies it to the arguments of the 2nd, 3rd, etc., just like a Unix pipe .

 (->> (string-to-list "686170707920333974682068617665206120676f6f64206f6e6521") (-partition 2) (--map (string-to-number (concat it) 16)) concat) 
+4
source

Here is mine. I do not claim to be particularly idiomatic or elegant. Maybe a little old fashioned.

 (defun hex-string-decode (str) "Decode STR of the form \"4153434949\" to corresponding \"ASCII\"." (let (decoded sub) (while (> (length str) 0) (setq sub (substring str 0 2) decoded (cons (string-to-number sub 16) decoded) str (substring str 2) ) ) (when (not (zerop (length str))) (error "residue %s" str)) (mapconcat #'char-to-string (nreverse decoded) "") ) ) 
+1
source

For those who come here search ...

While developing a bit on the Inaimathi answer, here is the code to replace the selected area with a decoded six:

 (defun decode-hex-string (hex-string) (apply #'concat (loop for i from 0 to (- (/ (length hex-string) 2) 1) for hex-byte = (substring hex-string (* 2 i) (* 2 (+ i 1))) collect (format "%c" (string-to-number hex-byte 16))))) (defun hex-decode-region (start end) "Decode a hex string in the selected region." (interactive "r") (save-excursion (let* ((decoded-text (decode-hex-string (buffer-substring start end)))) (delete-region start end) (insert decoded-text)))) (provide 'decode-hex-string) (provide 'hex-decode-region) 

Save this in a file and then in the Mx load file. Or put on ~ / emacs.d or whatever. Then select the area with the contents of hexa and Mx hex-decode-region. Enjoy it!

+1
source

This was the solution I came up with, and it seemed a little ugly to me:

 (defun decode-hex-string(string) "Decode a hex string into ASCII" (let* ((hex-byte (substring string 0 2)) (rest (substring string 2)) (rest-as-string (if (> (length rest) 2) (decode-hex-string rest) ""))) (format "%c%s" (string-to-number hex-byte 16) rest-as-string))) 
0
source

At first, I didn’t see the requirement that this be Elisp, so I did it interactively, and the code below follows my interactive procedure.

  (defun decode-hex-string (hex-string) (with-temp-buffer (insert-char 32 (/ (length hex-string) 2)) (beginning-of-buffer) (hexl-mode) (hexl-insert-hex-string hex-string 1) (hexl-mode-exit) (buffer-string))) 
0
source

Building the answers provided by Inaimathi and Shrain, I also added an encoding function. The following is an implementation of both encoding and decoding for string and region arguments:

 ;; ASCII-HEX converion (defun my/hex-decode-string (hex-string) (let ((res nil)) (dotimes (i (/ (length hex-string) 2) (apply #'concat (reverse res))) (let ((hex-byte (substring hex-string (* 2 i) (* 2 (+ i 1))))) (push (format "%c" (string-to-number hex-byte 16)) res))))) (defun my/hex-encode-string (ascii-string) (let ((res nil)) (dotimes (i (length ascii-string) (apply #'concat (reverse res))) (let ((ascii-char (substring ascii-string i (+ i 1)))) (push (format "%x" (string-to-char ascii-char)) res))))) (defun my/hex-decode-region (start end) "Decode a hex string in the selected region." (interactive "r") (save-excursion (let* ((decoded-text (my/hex-decode-string (buffer-substring start end)))) (delete-region start end) (insert decoded-text)))) (defun my/hex-encode-region (start end) "Encode a hex string in the selected region." (interactive "r") (save-excursion (let* ((encoded-text (my/hex-encode-string (buffer-substring start end)))) (delete-region start end) (insert encoded-text)))) 
0
source

All Articles