資料遷移、加密解密、異構系統整合常需在 **ASCII 字串** 與 **HEX 十六進制** 之間互轉。Oracle 內建 RAWTOHEX/HEXTORAW 僅支援 RAW 型別,對 VARCHAR2 需自行封裝,且中文雙位元編碼需特別處理。
CREATE OR REPLACE FUNCTION ascii_to_hex (p_str IN VARCHAR2) RETURN VARCHAR2
IS
v_hex VARCHAR2(4000);
BEGIN
FOR i IN 1 .. LENGTH(p_str) LOOP
v_hex := v_hex || TRIM(TO_CHAR(ASCII(SUBSTR(p_str, i, 1)), 'XXXX'));
END LOOP;
RETURN UPPER(v_hex);
END;
/
CREATE OR REPLACE FUNCTION hex_to_ascii_zh (p_hex IN VARCHAR2) RETURN VARCHAR2
IS
v_str VARCHAR2(4000);
v_pos NUMBER := 1;
v_byte NUMBER;
BEGIN
WHILE v_pos <= LENGTH(p_hex) LOOP
v_byte := TO_NUMBER(SUBSTR(p_hex, v_pos, 2), 'XXXX');
IF v_byte > 128 THEN -- 雙位元字元 (中文)
v_str := v_str || CHR(TO_NUMBER(SUBSTR(p_hex, v_pos, 4), 'XXXX'));
v_pos := v_pos + 4;
ELSE -- 單位元字元 (ASCII)
v_str := v_str || CHR(v_byte);
v_pos := v_pos + 2;
END IF;
END LOOP;
RETURN v_str;
END;
/
CREATE OR REPLACE FUNCTION hex_to_ascii_en (p_hex IN VARCHAR2) RETURN VARCHAR2
IS
v_str VARCHAR2(4000);
BEGIN
FOR i IN 1 .. LENGTH(p_hex)/2 LOOP
v_str := v_str || CHR(TO_NUMBER(SUBSTR(p_hex, (i-1)*2+1, 2), 'XXXX'));
END LOOP;
RETURN v_str;
END;
/
-- 中文環境測試
SELECT hex_to_ascii_zh(ascii_to_hex('你好ABC嗎')) FROM dual;
-- 結果: 你好ABC嗎
-- 純英文環境測試
SELECT hex_to_ascii_en(ascii_to_hex('Hello World')) FROM dual;
-- 結果: Hello World
-- ASCII -> HEX (支援任意字元集)
SELECT UTL_RAW.CAST_TO_VARCHAR2(UTL_RAW.CAST_TO_RAW('你好ABC嗎')) FROM dual; -- 錯誤示範
-- 正確做法:
SELECT RAWTOHEX(UTL_I18N.STRING_TO_RAW('你好ABC嗎', 'AL32UTF8')) FROM dual;
-- HEX -> ASCII
SELECT UTL_I18N.RAW_TO_CHAR(HEXTORAW('E4BDA0E5A5BD414243E5978E'), 'AL32UTF8') FROM dual;
| 方案 | 優點 | 缺點 | 建議場景 |
|---|---|---|---|
| 自訂 PL/SQL 函數 | 邏輯透明、可移植舊版本 | 效能較差、需自行處理字元集 | 8i/9i 老舊環境、特殊編碼邏輯 |
| UTL_I18N + RAWTOHEX | 原生支援 Unicode、效能佳、標準化 | 10g+ 才完整支援 | 現代專案 (11g/12c/19c/21c) 首選 |
字串與 HEX 互轉看似簡單,**字元集處理才是核心陷阱**。新專案請直接使用 UTL_I18N 套件;維護舊系統時,務必確認資料庫 NLS_CHARACTERSET 選用對應函數,並建立完整測試案例覆蓋中英混雜、特殊符號、空字串等邊界情況。