Базовое преобразование чисел произвольного размера (PHP)

У меня есть длинная «двоичная строка», например, вывод функции пакета PHP.

Как преобразовать это значение в base62 (0-9a-zA-Z)? Встроенные математические функции переполняются такими длинными входными данными, а в BCmath нет функции base_convert или чего-то подобного. Мне также понадобится соответствующая функция "pack base62".


person Community    schedule 09.12.2008    source источник


Ответы (3)


Я думаю, что за этим вопросом стоит недоразумение. Базовое преобразование и кодирование/декодирование разные. Результатом 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. Начиная с последней группы не завершен, он дополняется нулями и значением 000000 ("A"). Т.к. все делается группами по 3 ввода символов, то есть 2 группы: "123" и "4". Последняя группа дополнена =, чтобы сделать ее длиной 3 символа, поэтому весь вывод становится «MTIzNA==».

преобразование в base64, с другой стороны, принимает одно целочисленное значение и преобразует его в одно значение base64. В нашем примере 1234 (десятичное) — это «TS» (base64), если мы используем ту же строку значений base64, что и выше. Работая в обратном порядке и слева направо: T = 19 (столбец 1), S = 18 (столбец 0), поэтому (19 * 64 ^ 1) + (18 * 64 ^ 0) = 19 * 64 + 18 = 1234 (десятичный). То же число может быть представлено как "4D2" в шестнадцатеричном формате (с основанием 16): (4 * 16^2) + (D * 16^1) + (2 * 16^0) = (4 * 256) + (13*16) + (2*1) = 1234 (десятичное число).

В отличие от кодирования, которое берет строку символов и изменяет ее, базовое преобразование не изменяет фактическое число, а просто изменяет его представление. Шестнадцатеричное (с основанием 16) "FF" — это то же число, что и десятичное (с основанием 10) "255", то есть то же число, что и "11111111" в двоичном формате (с основанием 2). Думайте об этом как об обмене валюты, если обменный курс никогда не менялся: 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 кропотливо разбивает длинные строки текста на небольшие фрагменты, которые может обработать компьютер. Это намного, намного медленнее, чем собственные операции, но может обрабатывать числа произвольного размера.

person Jay Dansand    schedule 16.11.2009
comment
Кто-нибудь писал о функции base64_convert, о которой вы теоретизировали (@Jay)? Я не могу найти никого, кто говорил бы о том, что преобразование base64 отличается от кодирования base64 за пределами этого поста. - person Bing; 13.12.2016
comment
В PHP есть встроенный base_convert(), но он ограничен базами от 2 до 36. Подавляющее большинство результатов поиска для base64 будут кодировать base64, то есть сохранять произвольные двоичные строки как строки символов base64. Это то, что большинство людей хотят делать, сохраняя двоичные данные, такие как изображения и ключи шифрования, в базах данных и т. д. Преобразование просто изменяет систему счисления (например, десятичная [base10] ‹› шестнадцатеричная [base16] ‹› двоичная [ основание2]). В base64 мало практического применения в качестве числовой базы (которая в вычислительной технике полностью презентабельна для пользователя): его не легче читать, чем шестнадцатеричный. - person Jay Dansand; 14.12.2016

Если вам действительно не нужно иметь base62, почему бы не пойти на:

base64_encode()
base64_decode()

Единственными другими добавленными символами являются "+" и "=", и это очень известный метод упаковки и распаковки двоичных строк с помощью функций, доступных на многих других языках.

person ruquay    schedule 13.12.2008

Вот функция 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');
person Core Xii    schedule 31.01.2011