2010-06-05 44 views
8

是否有一种常见的方法来对任意数据进行编码和解码,以便编码的最终结果只包含数字 - 比如base64_encode,但没有字母?将字节数据编码为数字

虚拟的例子:

$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" 
+2

字符串只是一组映射到人类可读字符的数字。告诉我们更多关于为什么你想要做这样的事情,你可能会得到一个很好的答案。你想能够将数字转换回原始字符串吗?否则,哈希函数可能就足够了。 – 2010-06-05 21:18:52

+0

@William在我当前的情况下,我想将包含数字和字母(内部ID,看起来丑陋)的16个字符的URL标识符转换为“仅用于数字”表示形式,以便更易于理解,用作锚点访问CMS中的不同内容块。 – 2010-06-05 21:20:05

+0

@Pekka:你的虚构结果似乎有点乐观,不是吗?它比原始字符串短一个字符! ;-) – 2010-06-05 21:24:55

回答

11

你可以把一个(单字节字符)串的作为碱-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)/ log(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%更长的时间,如将与埃尔.pescado的回答)。

+0

好东西,这听起来完全正确。将测试它并回来。 – 2010-08-04 09:21:30

7

嗯,这将是“基地8”的编码,而不是基地64。这是好知道的八进制。

所有Base64都将位流转换为6位块(0-63),并从64个字符字符集中分配一个字符。八进制使用3位,0-7。所以它可以使用ABCDEFGH,但使用0-7。你不能(容易地)使用0-9,因为0-9最多4位,但不能完全4位。这就是二进制数据的糟糕编码。

+0

我明白了,为背景欢呼。我需要从丑陋的(但只有16个字符)标识符来构建URL,因此效率方面并不重要。在用户贡献的注释中有一个实现:http://de.php.net/manual/en/function.base64-encode.php#78765我将尝试让它在基8中工作。 – 2010-06-05 21:16:40

+1

它不会必须以8为基数 - 也可以以10为底。 – 2010-06-05 22:43:58

2

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

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

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

解码功能是作为练习留给读者;)不管你如何编码你永远结束备份在基数较小

+0

聪明!我曾考虑过这个问题。它会占用比必要更多的空间,但它会为我的目的。我会等待,看看是否有人提出了一个真正的“base8”实现问题的精神:) – 2010-06-05 21:24:53

2

。通过一些dechex()转换,可能会缩小得到的整数,但最终只能保存几个字符。话虽如此,但在您开始使用0-9代表多字节字符的那一刻,这个数字真的很有趣。

我不知道整数作为ID,代表单词或完整的字符串,不会提供更小的占用空间。不是一个真正的直接编码,而是一个可行的选择。

@ el.pescado获得了上半年的荣誉,但他确实挑战了读者。所以,我回应了(主要是因为我想了解发生了什么)。

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; 
} 
+0

Chhers @Inkspeak!这对我来说都能很好地工作。 – 2010-06-06 10:00:19

+0

+1,解码函数:'implode('',array_map('chr',str_split($ s,3)));' – 2010-06-08 13:18:13