5

我有一个很长的“二进制字符串”,比如 PHPs pack 函数的输出。

如何将此值转换为 base62 (0-9a-zA-Z)?内置的数学函数会溢出如此长的输入,并且 BCmath 没有 base_convert 函数或任何特定的函数。我还需要一个匹配的“pack base62”函数。

4

3 回答 3

8

我认为这个问题背后存在误解。基本转换和编码/解码是不同的。输出base64_encode(...)不是一个大的 base64 数。它是一系列离散的 base64 值,对应于压缩函数。这就是为什么 BC Math 不起作用的原因,因为 BC Math 关注的是单个大数,而不是实际上代表二进制数据的小数组的字符串。

这是一个示例来说明差异:

base64_encode(1234) = "MTIzNA=="
base64_convert(1234) = "TS" //if the base64_convert function existed

base64 编码将输入分成 3 个字节组(3*8 = 24 位),然后将每个 6 位子段(2^6 = 64,因此为“base64”)转换为相应的 base64 字符(值为“ ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",其中 A = 0,/ = 63)。

在我们的示例中,base64_encode()将“1234”视为 4 个字符的字符串,而不是整数(因为base64_encode()不对整数进行操作)。因此它输出“MTIzNA==”,因为(在 US-ASCII/UTF-8/ISO-8859-1 中)“1234”是二进制的 00110001 00110010 00110011 00110100。这分为 001100(十进制 12,字符“M”)010011(十进制 19,字符“T”)001000(“I”)110011(“z”)001101(“N”)00。不完整,它用 0 填充,值为 000000(“A”)。因为一切都是由 3 个输入字符组完成的,所以有 2 个组:“123”和“4”。最后一组用 = 填充以使其长度为 3 个字符,因此整个输出变为“MTIzNA==”。

另一方面,转换为 base64采用单个整数值并将其转换为单个 base64 值。对于我们的示例,如果我们使用与上面相同的 base64 值字符串,则 1234(十进制)是“TS”(base64)。从左到右向后工作:T = 19(第 1 列),S = 18(第 0 列),所以 (19 * 64^1) + (18 * 64^0) = 19 * 64 + 18 = 1234 (十进制)。相同的数字可以用十六进制(base16)表示为“4D2”:(4 * 16^2) + (D * 16^1) + (2 * 16^0) = (4 * 256) + (13 * 16 ) + (2 * 1) = 1234(十进制)。

encoding不同,它接受字符串并对其进行更改,基本转换不会更改实际数字,只会更改其表示形式。十六进制(base16)“FF”与十进制(base10)“255”是相同的数字,与二进制(base2)中的“11111111”相同把它想象成货币兑换,如果汇率从未改变:1 美元与 0.79 英镑的价值相同(今天的汇率,但假装它从未改变)。

在计算中,整数通常作为二进制值进行运算(因为很容易构建 1 位算术单元,然后将它们堆叠在一起以形成 32 位/等算术单元)。要做像“255 + 255”(十进制)这样简单的事情,计算机需要先将数字转换为二进制(“11111111”+“11111111”),然后在算术逻辑单元(ALU)中执行运算。

几乎所有其他基数的使用都纯粹是为了方便人类(演示) - 计算机将其内部值 11111111(二进制)显示为 255(十进制),因为人类受过对十进制数进行运算的训练。该函数base64_convert()不作为标准 PHP 曲目的一部分存在,因为它通常对任何人都没有用:没有多少人本机读取 base64 数字。相比之下,二进制 1 和 0 有时对程序员很有用(我们可以像使用开/关开关一样使用它们!),而十六进制便于人类编辑二进制数据,因为整个 8 位字节可以明确表示为 00 到 FF,不会浪费太多空间。

您可能会问,“如果基础转换只是为了演示,那么为什么存在 BC Math?” 这是一个公平的问题,也是我说“几乎”纯粹是为了演示的原因:典型的计算机仅限于 32 位或 64 位宽的数字,这些数字通常足够大。有时您需要对不适合这些寄存器的非常非常大的数字(例如 RSA 模数)进行操作。BC Math 通过充当抽象层来解决这个问题:它将大量数字转换为长文本字符串。当需要进行一些操作时,BC Math 会煞费苦心地将长长的文本字符串分解成计算机可以处理的小块。它比本机操作慢得多,但它可以处理任意大小的数字。

于 2009-11-16T17:06:23.007 回答
2

除非你真的,真的必须有 base62,否则为什么不去:

base64_encode()
base64_decode()

唯一添加的其他字符是“+”和“=”,这是一种非常著名的方法,可以使用许多其他语言中的可用函数来打包和解包二进制字符串。

于 2008-12-13T01:26:33.950 回答
2

这是一个base_conv()可以在完全任意的基数之间转换的函数,表示为字符串数组;每个数组元素代表该基数中的单个“数字”,因此也允许多字符值(避免歧义是您的责任)。

function base_conv($val, &$baseTo, &$baseFrom)
    {
    return base_arr_to_str(base_conv_arr(base_str_to_arr((string) $val, $baseFrom), count($baseTo), count($baseFrom)), $baseTo);
    }

function base_conv_arr($val, $baseToDigits, $baseFromDigits)
    {
    $valCount = count($val);
    $result = array();
    do
        {
        $divide = 0;
        $newlen = 0;
        for ($i = 0; $i < $valCount; ++$i)
            {
            $divide = $divide * $baseFromDigits + $val[$i];
            if ($divide >= $baseToDigits)
                {
                $val[$newlen ++] = (int) ($divide / $baseToDigits);
                $divide = $divide % $baseToDigits;
                }
            else if ($newlen > 0)
                {
                $val[$newlen ++] = 0;
                }
            }
        $valCount = $newlen;
        array_unshift($result, $divide);
        }
        while ($newlen != 0);
    return $result;
    }

function base_arr_to_str($arr, &$base)
    {
    $str = '';
    foreach ($arr as $digit)
        {
        $str .= $base[$digit];
        }
    return $str;
    }

function base_str_to_arr($str, &$base)
    {
    $arr = array();
    while ($str === '0' || !empty($str))
        {
        foreach ($base as $index => $digit)
            {
            if (mb_substr($str, 0, $digitLen = mb_strlen($digit)) === $digit)
                {
                $arr[] = $index;
                $str = mb_substr($str, $digitLen);
                continue 2;
                }
            }
        throw new Exception();
        }
    return $arr;
    }

例子:

$baseDec = str_split('0123456789');
$baseHex = str_split('0123456789abcdef');

echo base_conv(255, $baseHex, $baseDec); // ff
echo base_conv('ff', $baseDec, $baseHex); // 255

// multi-character base:
$baseHelloworld = array('hello ', 'world ');
echo base_conv(37, $baseHelloworld, $baseDec); // world hello hello world hello world 
echo base_conv('world hello hello world hello world ', $baseDec, $baseHelloworld); // 37

// ambiguous base:
// don't do this! base_str_to_arr() won't know how to decode e.g. '11111'
// (well it does, but the result might not be what you'd expect;
// It matches digits sequentially so '11111' would be array(0, 0, 1)
// here (matched as '11', '11', '1' since they come first in the array))
$baseAmbiguous = array('11', '1', '111');
于 2011-01-31T07:28:44.387 回答