提问人:Pekka 提问时间:6/6/2010 最后编辑:Pekka 更新时间:1/23/2011 访问量:6165
将字节数据编码为数字
Encoding byte data into digits
问:
有没有一种通用的方法来编码和解码任意数据,以便编码的最终结果仅由数字组成 - 如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"
答:
嗯,这将是“Base 8”编码,而不是Base 64。这更广为人知的是 Octal。
Base64 所做的只是将位流转换为 6 位块 (0-63),并从 64 个字符的字符集中分配一个字符。八进制使用 3 位,0-7。因此,它可以使用 ABCDEFGH,但改用 0-7。您不能(轻松)使用 0-9,因为 0-9 最多为 4 位,但不完全是 4 位。这就是使它成为二进制数据糟糕的编码的原因。
评论
非常简单的例子 - 它将每个输入字节表示为 3 位十进制数:
function data2numbers ($data) {
$out = "";
for ($i = 0; $i < strlen ($data); $i++) {
$out .= sprintf ("%03d", ord ($data[$i]));
}
return $out;
}
缺点是它使任何输入数据的大小增加三倍(每个输入字节表示为三个输出字节)。
解码功能留给读者作为练习;
评论
无论您如何编码,您最终都会回到较小的基数。通过一些 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;
}
评论
implode('', array_map('chr', str_split($s, 3)));
您可以将(单字节字符)字符串视为以 256 为基数的编码数字,其中“\x00”表示 0,“”(空格,即“\x20”)表示 32,依此类推,直到“\xFF”表示 255。
仅使用数字 0-9 的表示可以通过将表示更改为以 10 为基数来完成。
请注意,“base64 编码”实际上并不是基本转换。base64 将输入分成 3 个字节(24 位)的组,并分别对这些组进行基本转换。这很有效,因为一个 24 位的数字可以用 64 基数的四位数字表示 (2^24 = 64^4)。
这或多或少是 el.pescado 所做的——他将输入数据分成 8 位部分,然后将数字转换为以 10 为基数。但是,相对于 base 64 编码,这种技术有一个缺点——它不能与字节边界正确对齐。要用 8 位表示一个数字(无符号时为 0-255),我们需要以 10 为基数的三位数字。但是,最左边的数字比其他数字的信息少。它可以是 0、1 或 2(对于无符号数字)。
以 10 为基数的数字存储 log(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
由于每个数字只编码位,因此预计数字往往会长 140%(而不是像 el.pescado 的答案那样长 200%)。log(10)/log(2)=~3.32193
评论