我目前在用emacs lisp写bindat相关的东西,需要有一个类似xxd的函数来看看数据的详细情况,但是找了一圈网上搜到的都是hexl-mode的。 我的目标能达到这样就够了:
(my-xxd “string”)
7374 7269 6e67
总之我搞不懂要怎么一个字节一个字节的读。 请问这个函数要怎么写?
我目前在用emacs lisp写bindat相关的东西,需要有一个类似xxd的函数来看看数据的详细情况,但是找了一圈网上搜到的都是hexl-mode的。 我的目标能达到这样就够了:
(my-xxd “string”)
7374 7269 6e67
总之我搞不懂要怎么一个字节一个字节的读。 请问这个函数要怎么写?
没人回。。自己勉强搞了一个:
(seq-map (lambda (b) (format “%02X” b)) (vconcat response))
方法很多。
vconcat
显示 10 进制,人人都熟悉:
(vconcat "hello")
;; => [104 101 108 108 111]
如果你的数据已经打印在 buffer 里了,用 nhexl-mode 最好了,还可以直接用 xxd、hexdump 等工具,也很容易(比如用 M-|)。
(call-process-region "hello" nil "xxd" nil t)
00000000: 6865 6c6c 6f hello
想从 Lisp 处理的话,就用你提到的方法,把字符串或者 Vector 当作 bytes。
(mapcar (lambda (x) (format "%02X" x)) "hello")
;; => ("68" "65" "6C" "6C" "6F")
顺便附上关于 bindat 的几个例子,来自我的 elisp-demos 项目 :
#+BEGIN_SRC elisp
(bindat-unpack '((dest-ip ip)
(src-ip ip)
(dest-port u16)
(src-port u16))
[192 168 1 100 192 168 1 101 4 210 17 215])
#+END_SRC
#+RESULTS:
: ((src-port . 4567)
: (dest-port . 1234)
: (src-ip .
: [192 168 1 101])
: (dest-ip .
: [192 168 1 100]))
#+BEGIN_SRC elisp
;; socks4: first packet to server
(bindat-unpack
'((VER u8)
(CMD u8)
(DSTPORT u16)
(DSTIP ip)
(ID strz (eval (- (length bindat-raw) bindat-idx))))
[#x04 #x01 #x00 #x50 #x5d #xb8 #xd8 #x22 #x46 #x72 #x65 #x64 #x00])
#+END_SRC
#+RESULTS:
: ((ID . "Fred")
: (DSTIP .
: [93 184 216 34])
: (DSTPORT . 80)
: (CMD . 1)
: (VER . 4))
#+BEGIN_SRC elisp
(string-to-vector
(bindat-pack '((dest-ip ip)
(src-ip ip)
(dest-port u16)
(src-port u16))
'((dest-ip . [192 168 1 100])
(src-ip . [192 168 1 101])
(dest-port . 1234)
(src-port . 4567))))
#+END_SRC
#+RESULTS:
: [192 168 1 100 192 168 1 101 4 210 17 215]
可以直接seq-map,不用转vector。另外建议先encode成binary
Emacs会按字符的方式处理多字节文本,这意味着多字节直接转vector不是bytes而是vector of unicode codepoint。
(vconcat "我可以吞下玻璃")
;; => [25105 21487 20197 21534 19979 29627 29827]
(vconcat (encode-coding-string "我可以吞下玻璃" 'binary))
;; => [230 136 145 229 143 175 228 187 165 229 144 158 ...]
所以最好应该用下面的实现
(defun xxd (str)
(seq-map (lambda (char) (format "%02x" char))
(encode-coding-string str 'binary)))
(xxd "我好了")
;; => ("e6" "88" "91" "e5" "a5" "bd" "e4" "ba" "86")
鉴于你提到处理bindat,那么顺带说一句,从文件读取bindata的最佳实现是用f.el
的f-read-bytes
https://github.com/rejeep/f.el/blob/master/README.md#f-read-bytes-path
哇,大佬们出现了。
其实我是要写一个rcon协议的实现,用来控制MC服务端的。这个协议难度我觉得作为入门刚好。
为了查看服务端的回应我还把返回的数据再重新发到本地用xxd看,真的太麻烦了。
我之后重写一个函数再发上来。
update: 1. 换成循环 2. 去掉显示spec功能 一个临时版本,后续加上根据spec显示对应字段的进制功能。
(cl-defun my-xxd (data &optional &key (groupsize 2))
"This function is used to check packet made by `rcon-make-payload'.
It act like xxd in linux, and support an option: groupsize."
(if (stringp data) (my-xxd (seq-map (lambda (byte) (format "%02X" byte))
(encode-coding-string data 'binary))
:groupsize groupsize)
(when (not (null data))
(let ((len (length data))
(line 0) (gctr 0) (base 16))
(princ (format "%08X: " line))
(dotimes (ctr len)
(cl-incf gctr)
(let ((rmd (mod ctr base)))
(princ (format "%s" (nth ctr data)))
(when (= gctr groupsize)
(princ " ")
(setq gctr 0))
(when (= rmd (1- base))
(setq gctr 0 line (1+ line))
(princ (format "\n%08X: " line)))))))))
调用与输出:
(my-xxd (rcon-make-payload :body "passwrd" :type rcon-type-auth) :groupsize 4)
00000000: 11000000 4A000000 03000000 70617373
00000001: 77726400 00