Я хочу создать уникальный идентификатор, но uniqid() дает что-то вроде '492607b0ee414'. Я бы хотел что-то похожее на то, что дает tinyurl: '64k8ra'. Чем короче, тем лучше. Единственное требование - в нем не должно быть очевидного порядка и он должен выглядеть красивее, чем кажущаяся случайной последовательность чисел. Буквы предпочтительнее цифр, и в идеале это не было бы смешанным регистром. Поскольку количество записей не будет таким большим (до 10000 или около того), риск столкновения не является большим фактором.
Любые предложения приветствуются.
Да, я пошел с тем, что предложил lpfavreau, хотя и немного изменил. Поскольку список элементов довольно мал, я могу выполнить проверку памяти на предмет коллизий.
Поскольку uniqid основан на метке времени, первые 6 символов будут одинаковыми в течение довольно долгого времени;) Даже если я взял последние x символов или скомбинировал их каким-то образом, я думаю, что все еще есть более чистый подход. Что-нибудь вроде "x1f" было бы неплохо.






Есть два способа получить надежно уникальный идентификатор: сделать его настолько длинным и переменным, чтобы шансы столкновения были чрезвычайно малы (как в случае с GUID), или сохранить все сгенерированные идентификаторы в таблице для поиска (либо в памяти, либо в базе данных). или файл) для проверки уникальности при генерации.
Если вы действительно спрашиваете, как вы можете сгенерировать такой короткий ключ и гарантировать его уникальность без какой-либо проверки дубликатов, ответ: вы не можете.
Буквы красивые, цифры некрасивые. Вам нужны случайные строки, но не нужны «уродливые» случайные строки?
Создайте случайное число и распечатайте его в альфа-стиль (база-26), как "числа" резервирования, которые дают авиакомпании.
Насколько мне известно, в PHP нет встроенных базовых функций преобразования общего назначения, поэтому вам придется кодировать этот бит самостоятельно.
Другой вариант: использовать uniqid() и избавиться от цифр.
function strip_digits_from_string($string) {
return preg_replace('/[0-9]/', '', $string);
}
Или замените их буквами:
function replace_digits_with_letters($string) {
return strtr($string, '0123456789', 'abcdefghij');
}
Это довольно близко к тому, что я хочу. Идентификаторы авиабилетов также являются хорошим примером. В основном то, что я хочу, - это хороший способ создать этот случайный код из 3-5 символов / цифр, который я затем могу преобразовать в строку. В остальном Uniqid подходит, только слишком долго.
Сделайте небольшую функцию, которая возвращает случайные буквы заданной длины:
<?php
function generate_random_letters($length) {
$random = '';
for ($i = 0; $i < $length; $i++) {
$random .= chr(rand(ord('a'), ord('z')));
}
return $random;
}
Затем вы захотите вызвать это, пока оно не станет уникальным, в псевдокоде, в зависимости от того, где вы храните эту информацию:
do {
$unique = generate_random_letters(6);
} while (is_in_table($unique));
add_to_table($unique);
Вы также можете убедиться, что буквы не образуют слово в словаре. Может быть, это будет полный английский словарь или просто словарь плохих слов, чтобы избежать того, что покупатель сочтет безвкусным.
Обновлено: Я бы также добавил, что это имеет смысл только в том случае, если, поскольку вы собираетесь его использовать, это не для большого количества элементов, потому что это может стать довольно медленным, чем больше вы получите столкновений (получение идентификатора уже в таблице). Конечно, вам понадобится индексированная таблица, и вы захотите настроить количество букв в идентификаторе, чтобы избежать конфликтов. В этом случае с 6 буквами у вас будет 26 ^ 6 = 308915776 возможных уникальных идентификаторов (без плохих слов), которых должно хватить для вашей потребности в 10000.
Обновлено: Если вам нужны комбинации букв и цифр, вы можете использовать следующий код:
$random .= rand(0, 1) ? rand(0, 9) : chr(rand(ord('a'), ord('z')));
Вы должны поместить ord('a') и ord('z') вне цикла, чтобы избежать вызова функции на каждом проходе.
Вы можете использовать идентификатор и просто преобразовать его в число с основанием 36, если хотите преобразовать его туда и обратно. Может использоваться для любой таблицы с целочисленным идентификатором.
function toUId($baseId, $multiplier = 1) {
return base_convert($baseId * $multiplier, 10, 36);
}
function fromUId($uid, $multiplier = 1) {
return (int) base_convert($uid, 36, 10) / $multiplier;
}
echo toUId(10000, 11111);
1u5h0w
echo fromUId('1u5h0w', 11111);
10000
Умные люди, вероятно, смогут понять это с достаточным количеством примеров идентификаторов. Не позволяйте этой безвестности заменить безопасность.
Есть ли способ использовать base_convert () для включения букв верхнего и нижнего регистра и 0-9? Base_convert ($ uid, 62, 10) работает?
JoshFinnie: вам нужно будет создать свою собственную чувствительную к регистру функцию для более высоких базовых значений, чем 36.
function rand_str($len = 12, $type = '111', $add = null) {
$rand = ($type[0] == '1' ? 'abcdefghijklmnpqrstuvwxyz' : '') .
($type[1] == '1' ? 'ABCDEFGHIJKLMNPQRSTUVWXYZ' : '') .
($type[2] == '1' ? '123456789' : '') .
(strlen($add) > 0 ? $add : '');
if (empty($rand)) $rand = sha1( uniqid(mt_rand(), true) . uniqid( uniqid(mt_rand(), true), true) );
return substr(str_shuffle( str_repeat($rand, 2) ), 0, $len);
}
Вот процедура, которую я использую для случайных base62 любой длины ...
Вызов gen_uuid() возвращает такие строки, как WJX0u0jV, E9EMaZ3P и т. д.
По умолчанию это возвращает 8 цифр, следовательно, интервал составляет 64 ^ 8 или примерно 10 ^ 14, этого часто бывает достаточно, чтобы столкновения были довольно редкими.
Для большей или меньшей строки передайте по желанию $ len. Нет ограничений по длине, поскольку я добавляю до тех пор, пока не буду удовлетворен [до предела безопасности 128 символов, который можно удалить].
Обратите внимание: используйте случайную соль внутри в md5 [или sha1, если хотите], так что это не может быть легко реконструировано.
Я не нашел никаких надежных преобразований base62 в Интернете, поэтому этот подход удаления символов из результата base64.
Бесплатное использование под лицензией BSD, наслаждаться,
function gen_uuid($len=8)
{
$hex = md5("your_random_salt_here_31415" . uniqid("", true));
$pack = pack('H*', $hex);
$uid = base64_encode($pack); // max 22 chars
$uid = ereg_replace("[^A-Za-z0-9]", "", $uid); // mixed case
//$uid = ereg_replace("[^A-Z0-9]", "", strtoupper($uid)); // uppercase only
if ($len<4)
$len=4;
if ($len>128)
$len=128; // prevent silliness, can remove
while (strlen($uid)<$len)
$uid = $uid . gen_uuid(22); // append until length achieved
return substr($uid, 0, $len);
}
для версии в верхнем регистре используйте вместо нее эту строку - $ uid = ereg_replace ("[" A-Z0-9] "," ", strtoupper ($ uid));
Случайно, но если соль была переменной, есть ли способ отменить это, чтобы найти переменную соли?
Действительно простое решение:
Сделайте уникальный идентификатор с помощью:
$id = 100;
base_convert($id, 10, 36);
Получите снова исходное значение:
intval($str,36);
Не могу поверить в это, так как это из другой страницы переполнения стека, но я подумал, что решение было настолько элегантным и потрясающим, что людям, ссылающимся на это, стоило скопировать его в этот поток.
Это полностью не соответствует требованиям "Единственное требование - не иметь очевидного порядка"
@gen_uuid () пользователя gord.
У preg_replace были неприятные проблемы с utf-8, из-за которых uid иногда содержал "+" или "/". Чтобы обойти это, вы должны явно указать шаблон utf-8
function gen_uuid($len=8) {
$hex = md5("yourSaltHere" . uniqid("", true));
$pack = pack('H*', $hex);
$tmp = base64_encode($pack);
$uid = preg_replace("#(*UTF8)[^A-Za-z0-9]#", "", $tmp);
$len = max(4, min(128, $len));
while (strlen($uid) < $len)
$uid .= gen_uuid(22);
return substr($uid, 0, $len);
}
Мне потребовалось довольно много времени, чтобы найти это, возможно, это избавит кого-то от головной боли
Я придумал, как мне кажется, довольно крутое решение, делающее это без проверки уникальности. Я думал, что поделюсь с будущими посетителями.
Счетчик - это действительно простой способ гарантировать уникальность, или, если вы используете базу данных, первичный ключ также гарантирует уникальность. Проблема в том, что он плохо выглядит и может быть уязвим. Итак, я взял последовательность и перемешал ее с помощью шифра. Поскольку шифр можно поменять местами, я знаю, что каждый идентификатор уникален, но при этом остается случайным.
Это python, а не php, но я загрузил здесь код: https://github.com/adecker89/Tiny-Unique-Identifiers
Если вам нравится более длинная версия уникального идентификатора, используйте это:
$ uniqueid = sha1 (md5 (время ()));
Вы также можете сделать это так:
public static function generateCode($length = 6)
{
$az = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$azr = rand(0, 51);
$azs = substr($az, $azr, 10);
$stamp = hash('sha256', time());
$mt = hash('sha256', mt_rand(5, 20));
$alpha = hash('sha256', $azs);
$hash = str_shuffle($stamp . $mt . $alpha);
$code = ucfirst(substr($hash, $azr, $length));
return $code;
}
Вы можете делать эти нечистые / дорогостоящие вещи без, такие как циклы, конкатенации строк или множественные вызовы rand (), в чистом и удобном для чтения виде. Также лучше использовать mt_rand():
function createRandomString($length)
{
$random = mt_rand(0, (1 << ($length << 2)) - 1);
return dechex($random);
}
Если вам нужно, чтобы строка имела точную длину в любом случае, просто добавьте к шестнадцатеричному числу нули:
function createRandomString($length)
{
$random = mt_rand(0, (1 << ($length << 2)) - 1);
$number = dechex($random);
return str_pad($number, $length, '0', STR_PAD_LEFT);
}
«Теоретический недостаток» заключается в том, что вы ограничены возможностями PHP - но в этом случае это скорее философский вопрос;) Давайте все равно рассмотрим его:
$length <= 8по меньшей мере в 32-битной системе, где ограничение PHP для этого должно быть 4.294.967.295.mt_rand()по меньшей мере в 32-битной системе это должно быть 2.147.483.647.Возвращаясь к теме - интуитивно понятный do { (generate ID) } while { (id is not uniqe) } (insert id) имеет один недостаток и один возможный недостаток, который может увести вас в темноту ...
Недостаток: Проверка пессимистична. Для этого всегда требует проверки в базе данных. Наличие достаточного пространства ключей (например, длина 5 для ваших 10k записей) вряд ли вызовет коллизии так же часто, поскольку сравнительно может потреблять меньше ресурсов, чтобы просто попытаться сохранить данные и повторить попытку только в случае ошибки UNIQUE KEY.
Недостаток:Пользователь А извлекает идентификатор, который проверяется как еще не использованный. Затем код попытается вставить данные. Но тем временем Пользователь B вошел в тот же цикл и, к сожалению, получает то же случайное число, потому что Пользователь А еще не сохранен, и этот идентификатор все еще был свободен. Теперь система сохраняет либо Пользователь B, либо Пользователь А, и при попытке сохранить второго пользователя уже есть другой пользователь с тем же идентификатором.
Вам нужно будет обработать это исключение в любом случае и повторить попытку вставки с вновь созданным идентификатором. Добавление этого при сохранении пессимистичного цикла проверки (который вам потребуется повторно ввести) приведет к довольно уродливому и сложному для понимания коду. К счастью, решение этой проблемы такое же, как и решение проблемы: Просто сделайте это в первую очередь и попробуйте сохранить данные. В случае ошибки UNIQUE KEY просто повторите попытку с новым идентификатором.
Не нужно проверять базу данных, просто вставляйте / ловите / регенерируйте.
Взгляните на эту статью
В нем объясняется, как генерировать короткие уникальные идентификаторы из ваших идентификаторов bdd, как это делает YouTube.
На самом деле функция в статье очень похожа на функция php base_convert, которая преобразует число из одного основания в другое (но только до 36).
Вы можете добиться этого с меньшим количеством кода:
function gen_uid($l=10){
return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, $l);
}
Результат (примеры):
Отличное решение, но оно может возвращать только одно вхождение каждой буквы, что ограничивает возможности. Немного переписал: function gen_uid($l=10){ $str = ""; for ($x=0;$x<$l;$x++) $str .= substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, 1); return $str; }
Вот код:
<?php
/*
THE FOLLOWING CODE WILL PRINT:
A database_id value of 200 maps to 5K
A database_id value of 1 maps to 1
A database_id value of 1987645 maps to 16LOD
*/
$database_id = 200;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 200 maps to $base36value\n";
$database_id = 1;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 1 maps to $base36value\n";
$database_id = 1987645;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 1987645 maps to $base36value\n";
// HERE'S THE FUNCTION THAT DOES THE HEAVY LIFTING...
function dec2string ($decimal, $base)
// convert a decimal number into a string using $base
{
//DebugBreak();
global $error;
$string = null;
$base = (int)$base;
if ($base < 2 | $base > 36 | $base == 10) {
echo 'BASE must be in the range 2-9 or 11-36';
exit;
} // if
// maximum character string is 36 characters
$charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
// strip off excess characters (anything beyond $base)
$charset = substr($charset, 0, $base);
if (!ereg('(^[0-9]{1,50}$)', trim($decimal))) {
$error['dec_input'] = 'Value must be a positive integer with < 50 digits';
return false;
} // if
do {
// get remainder after dividing by BASE
$remainder = bcmod($decimal, $base);
$char = substr($charset, $remainder, 1); // get CHAR from array
$string = "$char$string"; // prepend to output
//$decimal = ($decimal - $remainder) / $base;
$decimal = bcdiv(bcsub($decimal, $remainder), $base);
} while ($decimal > 0);
return $string;
}
?>
10 символов:
substr(uniqid(),-10);
5 двоичных символов:
hex2bin( substr(uniqid(),-10) );
8 символов base64:
base64_encode( hex2bin( substr(uniqid(),-10) ) );
Вы нашли решение? Если да, поделитесь им или наградите ответом.