代码之家  ›  专栏  ›  技术社区  ›  Pekka

将字节数据编码为数字

  •  8
  • Pekka  · 技术社区  · 14 年前

    有没有一种通用的方法来编码和解码任意数据,这样编码后的最终结果只包含数字——比如base64 _编码,但不包含字母?

    虚构的例子:

    $encoded = numbers_encode("Mary had a little lamb");
    
    echo $encoded; // outputs e.g. 12238433742239423742322 (fictitious result)
    
    $decoded = numbers_decode("12238433742239423742322");
    
    echo $decoded; // outputs "Mary had a little lamb"
    
    4 回复  |  直到 13 年前
        1
  •  12
  •   Community Egal    7 年前

    可以将(单字节字符)字符串视为base-256编码的数字,其中“\x00”表示0,“(空格,即“\x20”)表示32,依此类推,直到“\xff”,即表示255。

    只使用数字0-9的表示可以通过将表示更改为基数10来完成。

    注意,“base64编码”实际上不是 base conversion . base64将输入分成3个字节(24位)的组,并分别对这些组进行基本转换。这很好地工作,因为24位的数字可以用以64为基数的四位数来表示(2^24=64^4)。

    这或多或少是什么 el.pescado _“他将输入数据拆分为8位片段,然后将数字转换为基数10。然而,这种技术相对于base 64编码有一个缺点,即它不能与字节边界正确对齐。要用8位(无符号时为0-255)表示数字,我们需要以10为基数的三位数字。但是,最左边的数字的信息比其他数字少。它可以是0、1或2(对于无符号数字)。

    以10为基数的数字存储对数(10)/对数(2)位。无论您选择的块大小是什么,您都无法将表示与8位字节对齐(在前面段落中描述的“对齐”意义上)。因此,最紧凑的表示形式是基转换(您可以看到它好像是一个只有一个大块的“基编码”)。

    下面是一个例子 bcmath .

    bcscale(0);
    function base256ToBase10(string $string) {
        //argument is little-endian
        $result = "0";
        for ($i = strlen($string)-1; $i >= 0; $i--) {
            $result = bcadd($result,
                bcmul(ord($string[$i]), bcpow(256, $i)));
        }
        return $result;
    }
    function base10ToBase256(string $number) {
        $result = "";
        $n = $number;
        do {
            $remainder = bcmod($n, 256);
            $n = bcdiv($n, 256);
            $result .= chr($remainder);
        } while ($n > 0);
    
        return $result;
    }
    

    为了

    $string = "Mary had a little lamb";
    $base10 = base256ToBase10($string);
    echo $base10,"\n";
    $base256 = base10ToBase256($base10);
    echo $base256;
    

    我们得到

    36826012939234118013885831603834892771924668323094861
    Mary had a little lamb
    

    因为每个数字只编码 log(10)/log(2)=~3.32193 比特预计数字可能是 140% longer (不超过200%,就像El.Pescado的答案一样)。

        2
  •  7
  •   Will Hartung    14 年前

    好吧,那将是“base 8”编码,而不是base 64。这最好叫做八进制。

    base64所做的全部工作是将位流转换为6位块(0-63),并从64个字符集分配一个字符。八进制使用3位,0-7。所以它可以使用abcdefgh,而使用0-7。您不能(很容易)使用0-9,因为0-9最多4位,但不完全是4位。这就是为什么它是一个糟糕的二进制数据编码。

        3
  •  2
  •   el.pescado - нет войне    14 年前

    非常简单的例子-它将每个输入字节表示为3位十进制数:

    function data2numbers ($data) {
        $out = "";
        for ($i = 0; $i < strlen ($data); $i++) {
            $out .= sprintf ("%03d", ord ($data[$i]));
        }
        return $out;
    }
    

    缺点是它将任何输入数据的大小增加了三倍(每个输入字节表示为三个输出字节)。

    解码功能留给读者作为练习;)

        4
  •  2
  •   allnightgrocery    14 年前

    不管你如何编码,你总是会以较小的基数结束备份。通过一些dechex()转换,可能会将结果整数缩小一点,但最终只会节省几个字符。也就是说,当您开始用0-9表示多字节字符时,这个数字就真的膨胀了。

    我想知道整数作为ID、表示单词或完整字符串是否不能提供更小的占用空间。不是直接编码,而是可行的选择。

    @佩斯卡多在上半场获得了荣誉,但他确实向读者提出了挑战。所以,我回答(主要是因为我想了解发生了什么)。

    function pekka_encode($s) {
        $out = '';
        for ($i=0;$i<strlen($s); $i++) {
            $out .= sprintf("%03d", ord($s[$i]));     
        }
        return $out;
    }
    
    function pekka_decode($s) {
        $out = '';
        for ($i=0;$i<strlen($s);$i+=3) {
            $out .= chr($s[$i].$s[$i+1].$s[$i+2]);
        }
        return $out;
    }