2010-06-05 43 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