У меня есть длинная «двоичная строка», например, выход функции PHPs pack.
Как преобразовать это значение в base62 (0-9a-zA-Z)? Встроенные математические функции переполняются с такими длинными входами, а BCmath не имеет функции base_convert или что-то конкретное. Мне также нужна соответствующая функция «pack base62».
Я думаю, что за этим вопросом возникает недоразумение. Базовое преобразование и кодирование / декодирование различны . Вывод 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 в двоичном формате. 001200 («I») 000001 («z») 001101 («N») 00. Поскольку последняя группа не является полным, он заполняется 0, а значение равно 000000 («A»). Поскольку все выполняется группами из 3 входных символов, есть две группы: «123» и «4». Последняя группа заполняется с помощью = 's, чтобы сделать ее длиной 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» в шестнадцатеричном виде (base16): (4 * 16 ^ 2) + (D * 16 ^ 1) + (2 * 16 ^ 0) = (4 * 256) + (13 * 16 ) + (2 * 1) = 1234 (десятичный).
В отличие от кодировки , которая берет строку символов и меняет ее, базовое преобразование не изменяет фактическое число, просто изменяет его представление. Шестнадцатеричный (base16) «FF» имеет такое же число, что и десятичный (base10) «255», который является таким же числом, что и «11111111» в двоичном формате (base2). Подумайте об этом, как обмен валюты, если обменный курс не изменился: 1 доллар США имеет такое же значение, как и 0,79 фунта стерлингов (обменный курс на сегодняшний день, но притворяться, что он никогда не меняется).
При вычислении целые числа обычно управляются как двоичные значения (потому что легко построить 1-битные арифметические единицы, а затем собрать их вместе, чтобы создать 32-разрядные / и т. Д. Арифметические единицы). Чтобы сделать что-то столь же простое, как «255 + 255» (десятичное), компьютеру необходимо сначала преобразовать числа в двоичные («11111111» + «11111111»), а затем выполнить операцию в Арифметическом логическом блоке (ALU).
Почти все другие виды использования оснований предназначены исключительно для удобства людей (презентационные) – компьютеры отображают свое внутреннее значение 11111111 (двоичное) как 255 (десятичное), потому что люди обучены работе с десятичными числами. Функция base64_convert()
не существует как часть стандартного репертуара PHP, потому что это не часто полезно для кого-либо: не многие люди читают номера base64 изначально. Напротив, бинарные 1 и 0 иногда полезны для программистов (мы можем использовать их как переключатели вкл / выкл!), А шестнадцатеричные удобны для людей, редактирующих двоичные данные, потому что весь 8-разрядный байт может быть представлен однозначно с 00 по FF, не тратя слишком много места.
Вы можете спросить: «Если базовое преобразование предназначено только для презентации, почему существует БМ-математика?» Это справедливый вопрос, а также то, почему я сказал «почти» исключительно для презентации: типичные компьютеры ограничены 32-битными или 64-битными номерами, которые обычно достаточно велики. Иногда вам нужно работать на действительно, действительно больших числах (например, на модулях RSA), которые не вписываются в эти регистры. BC Math решает эту проблему, действуя как слой абстракции: она преобразует огромные числа в длинные строки текста. Когда пришло время выполнить некоторую операцию, BC Math тщательно разрывает длинные строки текста на небольшие куски, которые компьютер может обрабатывать. Это намного, намного медленнее, чем исходные операции, но он может обрабатывать цифры произвольного размера.
Если вы действительно, действительно должны иметь base62, почему бы не пойти:
base64_encode() base64_decode()
Другими добавленными символами являются «+» и «=», и это очень известный метод для пакетной и распаковки двоичных строк с доступными функциями на многих других языках.
Вот функция 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 eg '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');