任意大小数字的基本转换(PHP)

时间:2008-12-09 11:25:39

标签: php math base-conversion

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

如何将此值转换为base62(0-9a-zA-Z)? 内置的数学函数溢出了这么长的输入,而BCmath没有base_convert函数,或者没有特定的东西。我还需要一个匹配的“pack base62”功能。

3 个答案:

答案 0 :(得分:7)

我认为这个问题背后存在误解。基本转换和编码/解码不同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”。最后一组填充='s使其长3个字符,因此整个输出变为“MTIzNA ==”。

另一方面,

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

编码不同,它采用一串字符并对其进行更改,基本转换不会改变实际数字,只是更改其显示方式。十六进制(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会将长串的文本细分成计算机可以处理的小块。它比本机操作慢得多,很多,但它可以处理任意大小的数字。

答案 1 :(得分:2)

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

base64_encode()
base64_decode()

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

答案 2 :(得分:1)

这是一个函数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');