这里简单地说一下base32的编码规则。base32是将原数据按5个字节分为一组(即 5 * 8 = 40bits),然后将每一组的二进制序列按5bits为一段进行重新分割,并转化为对应的十进制数值,此值的范围就是 0 ~ 2^5-1 , 一共32个数字,然后到编码表中查找对应此值的字符(大写字母加2-7六个数字共32个字符),将这些字符拼接起来就成了base32编码了。原数据的字节总数不是5的整数倍的话,最后一个分块将不足5位,这类数据可以在右边填充0以补足到5位。
按说编码也就到此为止了。那为什么会出现“=”号呢?其实=号也可以不需要的,但是为了数据有更好的通用性和标准化,当原数据的字节数不是5的整数倍时,最后一组数据转化为二进制时不能按字节为单位进行传输,对一些网络传输来说这会造成一些额外的处理规则。所以就干脆用=符号补足成完整的一组数据。
PHP实现base32相对来说比较简单,演示代码如下:
function base32Encode($str, $padding = true){ $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; $binary = ''; foreach (str_split($str) as $char) { $binary .= str_pad(base_convert(ord($char), 10, 2), 8, '0', STR_PAD_LEFT); } $encoded = ''; foreach (str_split($binary, 5) as $fiveBits) { $encoded .= $chars[base_convert(str_pad($fiveBits, 5, '0', STR_PAD_RIGHT), 2, 10)]; } //需要5个字节为一组,不足5个字节的分组要用=补足 $remainder = count($bytes) % 5; if ($padding && $remainder > 0) { $encoded .= str_repeat('=', 8 - ceil($remainder * 8 / 5)); } return $encoded; }
解码:
function base32Decode($input){ if (empty($input)) return ''; if (!preg_match('/^[A-Z234567]+\=*$/i', $input)) { throw new \Exception('Input is not a valid base32 string.'); } $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; $input = str_replace('=', '', strtoupper($input)); $binary = ''; foreach (str_split($input) as $char) { $binary .= str_pad(base_convert(strpos($chars, $char), 10, 2), 5, '0', STR_PAD_LEFT); } $decoded = ''; foreach (str_split($binary, 8) as $byte) { $decoded .= chr(base_convert($byte, 2, 10)); } return $decoded; }以上两个函数仅仅是演示性算法,实践当中效率并不高,建议使用github上大神的实现:
class Base32 { const BITS_5_RIGHT = 31; protected static $CHARS = 'abcdefghijklmnopqrstuvwxyz234567'; public static function encode($data) { $dataSize = strlen($data); $res = ''; $remainder = 0; $remainderSize = 0; for ($i = 0; $i < $dataSize; $i++) { $b = ord($data[$i]); $remainder = ($remainder << 8) | $b; $remainderSize += 8; while ($remainderSize > 4) { $remainderSize -= 5; $c = $remainder & (self::BITS_5_RIGHT << $remainderSize); $c >>= $remainderSize; $res .= self::$CHARS[$c]; } } if ($remainderSize > 0) { // remainderSize < 5: $remainder <<= (5 - $remainderSize); $c = $remainder & self::BITS_5_RIGHT; $res .= self::$CHARS[$c]; } return $res; } public static function decode($data) { $data = strtolower($data); $dataSize = strlen($data); $buf = 0; $bufSize = 0; $res = ''; for ($i = 0; $i < $dataSize; $i++) { $c = $data[$i]; $b = strpos(self::$CHARS, $c); if ($b === false) { throw new \Exception('Encoded string is invalid, it contains unknown char #'.ord($c)); } $buf = ($buf << 5) | $b; $bufSize += 5; if ($bufSize > 7) { $bufSize -= 8; $b = ($buf & (0xff << $bufSize)) >> $bufSize; $res .= chr($b); } } return $res; } }Original source: https://github.com/bbars/utils/tree/master/php-base32-encode-decode