今天我收到了一封回复邮件,内容是一串十六进制字节:
"686170707920333974682068617665206120676f6f64206f6e6521"
我正在考虑一种将字符串转换为其ASCII等效项的最有效和干净的方法。我会在问题中添加我的答案,但我觉得它可能不够优雅。
这是一种迭代解决方案
(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)))))
如果您想避免副作用操作,可以使用一个使用 loop
的方法(您可能需要(require 'cl)
来使用它):
(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)))))
一般情况下,在Elisp和Common Lisp中最好避免使用递归;如果输入足够大,您的堆栈会翻倒,而且两种语言都不保证尾递归(虽然您没有使用)。 对Scheme来说,情况就不同了。
顺便说一句,39周年快乐。
loop
DSL完美地解决了一般迭代问题,而其他人似乎要么忽略了这个问题,要么没有深入思考,或者决定map
是唯一正确的迭代方式™©(在具有单一函数/变量命名空间的惰性/函数式语言中,这是有道理的)。 - Inaimathi对于那些在这里搜索的人……
在Inaimathi的答案上进行详细说明,下面是用于用解码后的十六进制替换所选区域的代码:
(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)
将其保存到文件中,然后执行 M-x load-file 命令。或者放置在 ~/emacs.d 或其他位置。然后选择包含十六进制内容的区域并执行 M-x hex-decode-region 命令。享受吧!
dash.el
列表API(并且您应该这样做),请尝试:(concat (--map (string-to-number (concat it) 16) (-partition 2 (string-to-list "686170707920333974682068617665206120676f6f64206f6e6521"))))
string-to-number
、string-to-list
以及concat
,同时还使用了dash.el
的-partition
和句法版本的-map
函数。concat
的好处在于它不仅可以连接字符串,还可以连接字符列表或向量。我们可以使用->>
线程宏来重写此代码。它将第一个参数的结果应用于第二个,第三个等参数,就像Unix管道一样。(->> (string-to-list "686170707920333974682068617665206120676f6f64206f6e6521")
(-partition 2)
(--map (string-to-number (concat it) 16))
concat)
在Inaimathi和Shrein提供的答案基础上,我也添加了一个编码函数。以下是对字符串和区域参数实现编码和解码的代码:
;; 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))))
(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)))
(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) "") ) )
setq
在 let
表达式内部。 - tripleee这是我想出来的解决方案,但我觉得有点丑陋:
(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)))