BCD(Binary-Coded Decimal,二進位編碼十進位) 是一種將十進位數字的每一位(0-9)以 4 位元二進位(半個位元組)儲存的編碼方式。例如十進位 91 在 BCD 中表示為 1001 0001(十六進位 0x91),比純二進位儲存更直觀,常用於金融、電信、嵌入式系統等需精確十進位運算的領域。
本文示範 PHP 實作 BCD 壓縮與還原:將字串形式的十進位數字(如 "0091")壓縮為位元組陣列([0, 145] 即 0x00, 0x91),並支援反向還原。
<?php
/**
* PHP BCD 編碼工具類
* 功能:十進位字串 ⇄ 十六進位位元組陣列 互轉
*
* 範例:"0091" → [0, 145] (0x00, 0x91) → 還原 "0091"
*/
class Bytes {
/**
* 字串轉 ASCII 碼陣列
* @param string $string 輸入字串
* @return array ASCII 碼陣列
*/
public static function getBytes($string) {
$bytes = array();
for ($i = 0; $i < strlen($string); $i++) {
$bytes[] = ord($string[$i]);
}
return $bytes;
}
/**
* 位元組陣列轉十六進位字串
* @param array $bytes 位元組陣列
* @return string 十六進位字串
*/
public static function toStr($bytes) {
$str = '';
foreach ($bytes as $ch) {
$str .= bin2hex(chr($ch));
}
return $str;
}
/**
* ASCII 碼陣列轉 BCD 十六進位陣列(壓縮)
* 每 2 個 ASCII 字元壓縮為 1 個位元組
*
* @param array $asc ASCII 碼陣列
* @param int $AscLen 陣列長度
* @return array BCD 十六進位陣列
*/
public static function AscToHex($asc, $AscLen) {
$i = 0;
$Hex = array();
for ($i = 0; 2 * $i < $AscLen; $i++) {
// 高 4 位:第一個字元
$Hex[$i] = (chr($asc[2 * $i]) << 4);
// 處理 A-F 字元(ASCII 65-70, 97-102)
if (!(chr($asc[2 * $i]) >= '0' && chr($asc[2 * $i]) <= '9')) {
$Hex[$i] += 0x90;
}
// 若無第二個字元則結束
if (2 * $i + 1 >= $AscLen) {
break;
}
// 低 4 位:第二個字元
$Hex[$i] |= (chr($asc[2 * $i + 1]) & 0x0f);
if (!(chr($asc[2 * $i + 1]) >= '0' && chr($asc[2 * $i + 1]) <= '9')) {
$Hex[$i] += 0x09;
}
}
return $Hex;
}
/**
* BCD 十六進位陣列轉 ASCII 碼陣列(還原)
* 每 1 個位元組還原為 2 個 ASCII 字元
*
* @param array $Hex BCD 十六進位陣列
* @param int $HexLen 陣列長度
* @return array ASCII 碼陣列
*/
public static function HexToAsc($Hex, $HexLen) {
$i = 0;
$Temp = 0;
$Asc = array();
for ($i = 0; $i < $HexLen; $i++) {
// 取高 4 位
$Temp = ($Hex[$i] & 0xf0) >> 4;
if ($Temp < 10) {
$Asc[2 * $i] = (0x30 + $Temp); // '0'-'9'
} else {
$Asc[2 * $i] = (0x37 + $Temp); // 'A'-'F'
}
// 取低 4 位
$Temp = $Hex[$i] & 0x0f;
if ($Temp < 10) {
$Asc[2 * $i + 1] = (0x30 + $Temp);
} else {
$Asc[2 * $i + 1] = (0x37 + $Temp);
}
}
return $Asc;
}
}
?>
<?php
require_once 'Bytes.php';
$string = '0091';
// 1. 字串轉 ASCII 碼陣列
$bytes = Bytes::getBytes($string);
// 輸出: Array ( [0]=>48 [1]=>48 [2]=>57 [3]=>49 ) // '0','0','9','1' 的 ASCII
// 2. ASCII 壓縮為 BCD(4 位字元 → 2 位元組)
$asc = Bytes::AscToHex($bytes, 4);
// 輸出: Array ( [0]=>0 [1]=>145 ) // 0x00, 0x91
// 3. BCD 轉十六進位字串顯示
echo Bytes::toStr($asc);
// 輸出: 0091
// 4. 反向還原:BCD → ASCII(2 位元組 → 4 位字元)
$hex = Bytes::HexToAsc($asc, 2);
// 輸出: Array ( [0]=>48 [1]=>48 [2]=>57 [3]=>49 )
// 5. ASCII 轉回字串
echo Bytes::toStr($hex);
// 輸出: 0091
?>
| 步驟 | 輸入 | 處理 | 結果 |
|---|---|---|---|
| 1 | '0'(48), '0'(48) | 高4位: 48<<4=768(0x300) → '0'屬0-9不加0x90 | 0x00 |
| 2 | '9'(57), '1'(49) | 高4位: 57<<4=912(0x390) → 低4位: 49&0x0f=1 → 0x391 | 0x91 |
註:實際程式碼中對非數字字元(A-F)有額外處理(+0x90/+0x09),支援完整十六進位字元集。
將每個位元組拆解為高 4 位與低 4 位,分別轉回對應 ASCII 碼(0-9 對應 0x30-0x39,A-F 對應 0x41-0x46)。
pack()/unpack() 內建函式處理二進位資料,效能更佳gmp 或 bcmath 擴充套件進行高精度運算<?php
// 使用 PHP 內建函式簡化 BCD 處理 (PHP 5.6+)
/** 字串壓縮為 BCD 二進位字串 */
function bcdPack($str) {
// 確保偶數長度
if (strlen($str) % 2 !== 0) $str = '0' . $str;
return pack('H*', $str);
}
/** BCD 二進位字串還原 */
function bcdUnpack($bcd) {
return unpack('H*', $bcd)[1];
}
// 測試
$original = '0091';
$packed = bcdPack($original); // 二進位字串 "\x00\x91"
$unpacked = bcdUnpack($packed); // '0091'
echo $unpacked; // 0091
?>
pack('H*', ...) 直接將十六進位字串轉為二進位資料,unpack('H*', ...) 反向操作,程式碼更簡潔且效能更好。