什麼是二進位編輯器?

二進位編輯器是一種能直接以 16 進位或 ASCII 形式編輯檔案的工具。一般的文字編輯器把「字串」視為編輯對象,二進位編輯器則能存取檔案的「原始位元組序列」,並在任意位置直接修改位元或位元組。

在軟體解析、資料救援、協定調查等情境中,它是不可或缺的工具,GUI 應用如 HxDBinary Ninja 等都相當知名。不過這次的目標是「只靠瀏覽器就能運作的單一檔案二進位編輯器」。


實作方針

  • 完全於客戶端執行(無外部傳輸)
  • 使用 FileReader API 讀取本機檔案
  • 左側顯示十六進位(Hex),右側顯示 ASCII
  • 點擊即可進入編輯模式,允許直接改寫數值
  • 已編輯的格子以紅色字體高亮
  • 可切換 HEX 編輯/ASCII 編輯模式
  • 編輯結果可再次匯出為檔案下載

實際踩坑與修正

1. 編輯框偏移

在初版實作中,當對目標格顯示黃色框線時,框線會稍微偏離中心,看起來很不自然。這是因為 CSS 的 line-heightflex 排版產生影響,顯示文字與框線的基準點錯開了。雖然假設採用等寬字型,但瀏覽器的繪製流程會讓不同字型擁有不同基線,最終呈現上下偏移。

透過強制 vertical-align: middle,並把元素設定為 display: inline-block 以拉齊高度,就能解決問題。

.hex-cell.editing {
  outline: 2px solid yellow;
  vertical-align: middle;
}

2. 按下 ESC 會輸入文字

在最初版本,只要顯示編輯框時按下 ESC,就會把「EE」之類的字串寫進格子裡。

原因在於 沒有攔截 keydown 事件,瀏覽器便把按鍵的字元編碼直接送進格子的輸入流程。原本只預期 0–9、A–F,但 Escape 也有鍵碼(舊規格為 keyCode=27,現在則是 key="Escape"),因此同樣會被當成 "E"

解法相當直接,在 keydown 事件中明確捕捉 Escape,改為執行取消編輯。

document.addEventListener("keydown", e => {
  if (e.key === "Escape") {
    cancelEdit();
    e.preventDefault(); // 阻斷原本的輸入流程
  }
});

如此一來,ESC 只負責「取消編輯」,不會再在格子裡留下任何字元。

3. 重新載入時紅字殘留

讀取檔案並修改後,再載入另一個檔案時,之前的紅色高亮仍會殘留。

主因是 追蹤已編輯格子的陣列或狀態,在載入新檔時沒有初始化。即便清掉顯示區域,內部的「變更旗標」仍保留,新檔案就繼承了紅字狀態。

修正方式是在檔案讀取完成後立刻呼叫 clearModifiedState(),徹底重置變更旗標。

4. HEX/ASCII 模式切換

最初允許同時編輯 HEX 與 ASCII,導致「哪邊才是正確值」變得模糊。特別是一個位元組被兩個輸入表單同時處理時,同一格透過不同路徑寫入會發生競爭。ASCII 輸入會立刻覆寫 HEX 表示,但在輸入過程中短暫出現不一致。

為避免混亂,改成透過下拉選單明確選擇模式,並把預設設為 HEX 編輯。使用者能清楚知道自己正在輸入 HEX,同步處理也變得更單純。


程式碼片段

實際的編輯流程如下所示。

function applyEdit(offset, newValue) {
  if (mode === "hex") {
    buffer[offset] = parseInt(newValue, 16);
  } else {
    buffer[offset] = newValue.charCodeAt(0);
  }
  markModified(offset);
  render();
}

markModified(offset) 用於紅色高亮,只要與原值不同就加上對應的類別,是非常簡單的實作。


總結

這款瀏覽器版二進位編輯器能在離線環境運作,同時作為「完全客戶端工具」也符合安全需求。

  • 想快速修改少量二進位資料時
  • 在無法安裝專用應用的環境中進行調查
  • 教學或學習時,希望更直觀地理解「位元組序列」

以上場景都能派上用場。