EmacsTalk

使用颜色增加二进制文件的阅读体验

Jiacai Liu

文章目录

我之前使用 hexl-mode 的体验很差,一直以为是自己没看习惯十六进制数据,知道最近看到一篇很有启发的文章 your hex editor should color-code bytes,这才意识到可以通过增加工具来提供阅读体验。

那篇文章中提供了多个对比示例,为不同分类的字符加上不同的颜色后,可以极大提高阅读效果,推荐阅读先去看看原文,我这里提供两个自己写的工具:

  1. 命令行工具 hexdump
  2. Emacs 包 colored-hexl-mode ,用来给 hexl-mode 增加颜色。

hexdump

hexdump 是我给自己的项目 zigcli 新增的一个命令,主要是方便在命令行里面直接使用,颜色定义:

1//! Bytes are colored by semantic category:
2//!   bright_black — null (0x00)
3//!   green        — ASCII control chars and whitespace (0x01-0x20, 0x7F)
4//!   cyan         — printable ASCII (0x21-0x7E)
5//!   yellow       — non-ASCII (0x80-0xFF)

效果如下:

安装方式:

1curl -fsSL https://zigcli.liujiacai.net/install.sh | sh -s -- --bins "hexdump"

colored-hexl-mode

这个包主要是针对 emacs 自带的 hexl-mode 设计,效果如下:

源码如下:

  1;;; colored-hexl.el --- Semantic color coding for hexl-mode -*- lexical-binding: t; -*-
  2
  3;; Author: Jiacai Liu
  4;; Version: 1.0
  5;; Keywords: hex, colors, tools
  6
  7;;; Commentary:
  8;; This package provides `colored-hexl-mode`, which colors hexadecimal
  9;; bytes based on their semantic categories:
 10;; - Null (0x00): gray40
 11;; - Control & Whitespace (0x01-0x20, 0x7F): forest green
 12;; - Printable ASCII (0x21-0x7E): DeepSkyBlue
 13;; - Non-ASCII (0x80-0xFF): gold1
 14;;
 15;; --- USAGE ---
 16;;
 17;; 1. Manual Loading:
 18;;    Open this file and run `M-x eval-buffer`. Then in any hexl-mode
 19;;    buffer, run `M-x colored-hexl-mode`.
 20;;
 21;; 2. Permanent Setup (init.el):
 22;;    Add the following to your Emacs configuration:
 23;;
 24;;    (add-to-list 'load-path "/path/to/directory-containing-this-file/")
 25;;    (require 'colored-hexl)
 26;;    (add-hook 'hexl-mode-hook #'colored-hexl-mode)
 27;;
 28;; 3. Customization:
 29;;    You can change colors by running `M-x customize-group RET colored-hexl`.
 30;;
 31;;; Code:
 32
 33(defgroup colored-hexl nil
 34  "Semantic color coding for hex bytes."
 35  :group 'faces)
 36
 37;; --- Face Definitions ---
 38
 39(defface colored-hexl-null-face
 40  '((t :foreground "gray40"))
 41  "Face for null bytes (0x00): bright_black (gray40).")
 42
 43(defface colored-hexl-control-face
 44  '((t :foreground "forest green"))
 45  "Face for ASCII control chars and whitespace (0x01-0x20, 0x7F): forest green.")
 46
 47;; '((t :foreground "dark cyan"))
 48(defface colored-hexl-printable-face
 49  '((t :foreground "DeepSkyBlue"))
 50  "Face for printable ASCII (0x21-0x7E): DeepSkyBlue.")
 51
 52(defface colored-hexl-non-ascii-face
 53  '((t :foreground "gold1"))
 54  "Face for non-ASCII (0x80-0xFF): gold1.")
 55
 56;; --- Core Logic ---
 57
 58(defun colored-hexl--matcher (limit)
 59  "Search for hex bytes and apply faces based on semantic categories until LIMIT."
 60  (let (found)
 61    (while (and (not found)
 62                (re-search-forward "[0-9a-fA-F]\\{2\\}" limit t))
 63      (let* ((hex (match-string 0))
 64             (val (string-to-number hex 16))
 65             ;; Boundary check for hexl-mode columns (address vs hex data vs ascii)
 66             (is-hex-column (save-match-data
 67                              (let ((col (current-column)))
 68                                (and (>= col 10) (<= col 48))))))
 69        (when (or is-hex-column (not (derived-mode-p 'hexl-mode)))
 70          (let ((face (cond
 71                       ((= val 0) 'colored-hexl-null-face)               ; 0x00
 72                       ((or (and (>= val 1) (<= val 32)) (= val 127))    ; 0x01-0x20, 0x7F
 73                        'colored-hexl-control-face)
 74                       ((and (>= val 33) (<= val 126))                   ; 0x21-0x7E
 75                        'colored-hexl-printable-face)
 76                       ((>= val 128) 'colored-hexl-non-ascii-face)       ; 0x80-0xFF
 77                       (t nil))))
 78            (when face
 79              (setq found t)
 80              (put-text-property (match-beginning 0) (match-end 0) 'face face))))))
 81    found))
 82
 83(defvar colored-hexl-keywords
 84  '((colored-hexl--matcher . 0)))
 85
 86;; --- Minor Mode Definition ---
 87
 88;;;###autoload
 89(define-minor-mode colored-hexl-mode
 90  "Minor mode to colorize hexadecimal bytes by semantic category."
 91  :lighter " ColorHex"
 92  (if colored-hexl-mode
 93      (progn
 94        (font-lock-add-keywords nil colored-hexl-keywords t)
 95        (setq-local font-lock-multiline t)
 96        (font-lock-mode 1))
 97    (font-lock-remove-keywords nil colored-hexl-keywords))
 98  (font-lock-flush))
 99
100(provide 'colored-hexl)
101
102;;; colored-hexl.el ends here

推荐的使用方式:

1(add-hook 'hexl-mode-hook 'colored-hexl-mode)

为了更好的体验,可以给 find-file 加上下面的 hook,保证当文件是二进制时,自动进入 hexl-mode

1(defun my/hexl-if-binary-native ()
2  (when (and (eq buffer-file-coding-system 'no-conversion)
3             (not (eq major-mode 'hexl-mode)))
4    (hexl-mode)))
5
6(add-hook 'find-file-hook 'my/hexl-if-binary-native)

希望这两个工具对读者阅读二进制文件有所帮助,如果你有更好的颜色搭配,欢迎留言指出

收听方式

反馈