開發瀏覽器端二進位編輯器的經過
什麼是二進位編輯器?
二進位編輯器是一種能直接以 16 進位或 ASCII 形式編輯檔案的工具。一般的文字編輯器把「字串」視為編輯對象,二進位編輯器則能存取檔案的「原始位元組序列」,並在任意位置直接修改位元或位元組。
在軟體解析、資料救援、協定調查等情境中,它是不可或缺的工具,GUI 應用如 HxD
、Binary Ninja
等都相當知名。不過這次的目標是「只靠瀏覽器就能運作的單一檔案二進位編輯器」。
實作方針
- 完全於客戶端執行(無外部傳輸)
- 使用
FileReader
API 讀取本機檔案 - 左側顯示十六進位(Hex),右側顯示 ASCII
- 點擊即可進入編輯模式,允許直接改寫數值
- 已編輯的格子以紅色字體高亮
- 可切換 HEX 編輯/ASCII 編輯模式
- 編輯結果可再次匯出為檔案下載
實際踩坑與修正
1. 編輯框偏移
在初版實作中,當對目標格顯示黃色框線時,框線會稍微偏離中心,看起來很不自然。這是因為 CSS 的 line-height
與 flex
排版產生影響,顯示文字與框線的基準點錯開了。雖然假設採用等寬字型,但瀏覽器的繪製流程會讓不同字型擁有不同基線,最終呈現上下偏移。
透過強制 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)
用於紅色高亮,只要與原值不同就加上對應的類別,是非常簡單的實作。
總結
這款瀏覽器版二進位編輯器能在離線環境運作,同時作為「完全客戶端工具」也符合安全需求。
- 想快速修改少量二進位資料時
- 在無法安裝專用應用的環境中進行調查
- 教學或學習時,希望更直觀地理解「位元組序列」
以上場景都能派上用場。