首页 PHP 正文
842

PHP实现base32编码与解码

base64编码大家都不陌生,其实除了base64还有base32,base16。这一系列的编码规则都是差不多。base64的规则可以另行参考。
这里简单地说一下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

正在加载评论...