Как создать случайную строку с помощью PHP?


Я знаю, что функция rand в PHP генерирует случайные целые числа, но каков наилучший способ генерации случайной строки, такой как:

исходная строка, 9 символов

$string = 'abcdefghi';

пример случайной строки, ограничивающейся 6 символами

$string = 'ibfeca';

обновление: я нашел тонны этих типов функции, в основном я пытаюсь понять логику каждого шага.

обновление: функция должна генерировать любое количество символы по мере необходимости.

пожалуйста, прокомментируйте детали, если вы ответите.

17 56

17 ответов:

Ну, вы не уточнили все вопросы, которые я задал в своем комментарии, но я предполагаю, что вы хотите, чтобы функция, которая может принимать строку "возможных" символов и длину строки для возврата. Прокомментировал тщательно, как просили, используя больше переменных, чем я обычно, для ясности:

function get_random_string($valid_chars, $length)
{
    // start with an empty random string
    $random_string = "";

    // count the number of chars in the valid chars string so we know how many choices we have
    $num_valid_chars = strlen($valid_chars);

    // repeat the steps until we've created a string of the right length
    for ($i = 0; $i < $length; $i++)
    {
        // pick a random number from 1 up to the number of valid chars
        $random_pick = mt_rand(1, $num_valid_chars);

        // take the random character out of the string of valid chars
        // subtract 1 from $random_pick because strings are indexed starting at 0, and we started picking at 1
        $random_char = $valid_chars[$random_pick-1];

        // add the randomly-chosen char onto the end of our string so far
        $random_string .= $random_char;
    }

    // return our finished random string
    return $random_string;
}

чтобы вызвать эту функцию с вашими данными примера, вы бы назвали это что-то вроде:

$original_string = 'abcdefghi';
$random_string = get_random_string($original_string, 6);

обратите внимание, что эта функция не проверяет уникальность в допустимых символах передаваемый ему. Например, если вы вызвали его с допустимой строкой символов 'AAAB', это было бы в три раза более вероятно, чтобы выбрать A для каждой буквы в качестве B. Что может рассматриваться как ошибка или функция, в зависимости от ваших потребностей.

если вы хотите разрешить повторные вхождения символов, вы можете использовать эту функцию:

function randString($length, $charset='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
{
    $str = '';
    $count = strlen($charset);
    while ($length--) {
        $str .= $charset[mt_rand(0, $count-1)];
    }
    return $str;
}

основной алгоритм заключается в создании длина> раз случайное число между 0 и количество символов> - 1 мы используем в качестве индекса, чтобы выбрать символ из нашего набора и объединить эти символы. 0 и количество символов> - 1 границы представляют границы $charset строка в качестве первого символа адресуется с $charset[0] и последнее с $charset[count($charset) - 1].

мое любимое:

 echo substr(md5(rand()), 0, 7);

Итак, позвольте мне начать с того, что ИСПОЛЬЗУЙТЕ БИБЛИОТЕКУ. Многие существуют:

суть проблемы заключается в том, что почти каждый ответ на этой странице подвержен атаке. mt_rand(),rand(),lcg_value() и uniqid() все уязвим для атак.

хорошая система будет использовать /dev/urandom из файловой системы, или mcrypt_create_iv()MCRYPT_DEV_URANDOM) или openssl_pseudo_random_bytes(). Что все вышеперечисленное и делает. PHP 7 будет поставляться с двумя новыми функциямиrandom_bytes($len) и random_int($min, $max) это тоже безопасно.

имейте в виду, что большинство из этих функций (кроме random_int()) возврат "необработанных строк" означает, что они могут содержать любой символ ASCII из 0 - 255. Если вам нужна строка для печати, я бы предложил запустить результат через base64_encode().

function generate_random_string($name_length = 8) {
    $alpha_numeric = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    return substr(str_shuffle(str_repeat($alpha_numeric, $name_length)), 0, $name_length);
}

обновлен код согласно mzhang ' s большое предложение в комментариях ниже.

лучшая и обновленная версия отличного ответа @taskamiski:

лучшая версия, используя mt_rand() вместо rand():

echo md5(mt_rand()); // 32 char string = 128bit

еще лучше, для более длинных строк, используя hash() функция, позволяющая выбрать алгоритм хэширования:

echo hash('sha256', mt_rand()); // 64 char string
echo hash('sha512', mt_rand()); // 128 char string

если вы хотите сократить результат, скажем, до 50 символов, сделайте это так:

echo substr(hash('sha256', mt_rand()), 0, 50); // 50 char string

объединение символов в конце должно быть более эффективным, чем повторная конкатенация строк.

правка #1: добавлена опция, чтобы избежать повторения символов.

Edit #2: бросает исключение, чтобы избежать попадания в бесконечный цикл, если выбран $norepeat и $len больше, чем набор символов для выбора.

Edit #3: использует ключи массива для хранения выбранных случайных символов при выборе $norepeat, так как поиск ключа ассоциативного массива выполняется быстрее, чем линейный поиск матрица.

function rand_str($len, $norepeat = true)
{
    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    $max = strlen($chars) - 1;

    if ($norepeat && len > $max + 1) {
        throw new Exception("Non repetitive random string can't be longer than charset");
    }

    $rand_chars = array();

    while ($len) {
        $picked = $chars[mt_rand(0, $max)];

        if ($norepeat) {
            if (!array_key_exists($picked, $rand_chars)) {
                $rand_chars[$picked] = true;
                $len--;
            }
        }
        else {
            $rand_chars[] = $picked;
            $len--;
        }
    }

    return implode('', $norepeat ? array_keys($rand_chars) : $rand_chars);   
}

это создаст случайную строку

function generateRandomString($length=10) {
    $original_string = array_merge(range(0,9), range('a','z'), range('A', 'Z'));
    $original_string = implode("", $original_string);
    return substr(str_shuffle($original_string), 0, $length);
}
echo generateRandomString(6);

Я думаю, что я добавлю свой вклад здесь.

function random_string($length) {
    $bytes_1 = openssl_random_pseudo_bytes($length);
    $hex_1 = bin2hex($bytes_1);
    $random_numbers = substr(sha1(rand()), 0, $length);
    $bytes_2 = openssl_random_pseudo_bytes($length);
    $hex_2 = bin2hex($bytes_2);
    $combined_chars = $hex_1 . $random_numbers . $hex_2;
    $chars_crypted = hash('sha512', $combined_chars);

    return $chars_crypted;
}

спасибо

большинство аспектов этого уже обсуждались, но я бы порекомендовал небольшое обновление: Если вы используете это для розничного использования, я бы избегал домена ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

и вместо использования: ABCDEFGHJKMNPQRSTUVWXY3456789

конечно, вы получаете гораздо меньше символов, но это экономит много хлопот, так как клиенты не могут ошибиться 0 для O, или 1 для l или 2 для Z. Кроме того, вы можете сделать верхний на входе и затем клиенты могут вводить буквы верхнего или нижнего регистра - это также иногда сбивает с толку, поскольку они могут выглядеть одинаково.

для чего вам нужна случайная строка?

это будет использоваться для чего-либо отдаленно похожее на пароль?

если ваша случайная строка требует любой свойства безопасности вообще, вы должны использовать PHP 7's random_int() функция вместо всех небезопасных mt_rand() ответов в этой теме.

/**
 * Generate a random string
 * 
 * @link https://paragonie.com/b/JvICXzh_jhLyt4y3
 *
 * @param int $length - How long should our random string be?
 * @param string $charset - A string of all possible characters to choose from
 * @return string
 */
function random_str($length = 32, $charset = 'abcdefghijklmnopqrstuvwxyz')
{
    // Type checks:
    if (!is_numeric($length)) {
        throw new InvalidArgumentException(
            'random_str - Argument 1 - expected an integer'
        );
    }
    if (!is_string($charset)) {
        throw new InvalidArgumentException(
            'random_str - Argument 2 - expected a string'
        );
    }

    if ($length < 1) {
        // Just return an empty string. Any value < 1 is meaningless.
        return '';
    }
    // This is the maximum index for all of the characters in the string $charset
    $charset_max = strlen($charset) - 1;
    if ($charset_max < 1) {
        // Avoid letting users do: random_str($int, 'a'); -> 'aaaaa...'
        throw new LogicException(
            'random_str - Argument 2 - expected a string at least 2 characters long'
        );
    }
    // Now that we have good data, this is the meat of our function:
    $random_str = '';
    for ($i = 0; $i < $length; ++$i) {
        $r = random_int(0, $charset_max);
        $random_str .= $charset[$r];
    }
    return $random_str;
}

если вы еще не на PHP 7 (что, вероятно, так, поскольку он не был выпущен на момент написания этой статьи), то вы захотите paragonie / random_compat, который является пользовательской реализацией random_bytes() и random_int() для PHP 5 проектов.

для контекстов безопасности, всегда использовать random_int(), а не rand(),mt_rand() и т. д. посмотреть ircmaxell это как хорошо.

// @author http://codeascraft.etsy.com/2012/07/19/better-random-numbers-in-php-using-devurandom/                                                
function devurandom_rand($min = 0, $max = 0x7FFFFFFF)
{
    $diff = $max - $min;
    if ($diff < 0 || $diff > 0x7FFFFFFF) {
        throw new RuntimeException('Bad range');
    }
    $bytes = mcrypt_create_iv(4, MCRYPT_DEV_URANDOM);
    if ($bytes === false || strlen($bytes) != 4) {
        throw new RuntimeException('Unable to get 4 bytes');
    }
    $ary = unpack('Nint', $bytes);
    $val = $ary['int'] & 0x7FFFFFFF; // 32-bit safe                           
    $fp = (float) $val / 2147483647.0; // convert to [0,1]                          
    return round($fp * $diff) + $min;
}

function build_token($length = 60, $characters_map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
    $map_length = mb_strlen($characters_map)-1;
    $token = '';
    while ($length--) {
        $token .= mb_substr($characters_map, devurandom_rand(0,$map_length),1);
    }
    return $token;
}

это будет работать только в среде UNIX, где PHP компилируется с mcrypt.

вы хотите создать свой пароль путем случайной перестановки исходных букв? Должен ли он просто содержать уникальные символы?

использовать rand для выбора случайных букв по индексу.

вы можете создать массив символов, а затем использовать rand (), чтобы выбрать букву из массива и добавить ее в строку.

$letters = array( [0] => 'a' [1] => 'b' [2] => 'c' [3] => 'd' ... [25] = 'z');

$lengthOfString = 10;
$str = '';

while( $lengthOfString-- )
{
   $str .= $letters[rand(0,25)];
}
echo $str;

* обратите внимание, что это позволяет повторять символы

это строится на решение Гамбо добавляя функциональность для перечисления набора символов, которые будут пропущены в базовом наборе символов. Случайная строка выбирает символы из $base_charset которые также не отображаются в $skip_charset.

/* Make a random string of length using characters from $charset, excluding $skip_chars.
 * @param length (integer) length of return value
 * @param skip_chars (string) characters to be excluded from $charset
 * @param charset (string) characters of posibilities for characters in return val
 * @return (string) random string of length $length    */
function rand_string(
        $length, 
        $skip_charset = '', 
        $base_charset='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'){
  $skip_len = strlen($skip_charset);
  for ($i = 0; $i<$skip_len; $i++){
    $base_charset = str_replace($skip_charset[$i], '', $base_charset);
  }
  cvar_dump($base_charset, '$base_charset after replace');
  $str = '';
  $count = strlen($base_charset);
  while ($length--) {
    $str .= $base_charset[mt_rand(0, $count - 1)];
  }
  return $str;
}

вот некоторые примеры использования. В первых двух примерах используется значение по умолчанию для $base_charset. Последний пример явно определяет $base_charset.

echo rand_string(15, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
//  470620078953298
echo rand_string(8, 'abcdefghijklmnopqrstuvwxyz0123456789');
//  UKLIHOTFSUZMFPU
echo rand_string(15, 'def', 'abcdef');
//  cbcbbccbabccaba

ну, я искал решение, и я kindda использовал решение @Chad Birch, объединенное с решением @Gumbo. Вот что я придумал:

function get_random_string($length, $valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456790!·$%&/()=?¿¡',.-;:+*`+´ç")
{
    $random_string = "";
    $num_valid_chars = strlen($valid_chars);
    for ($i = 0; $i < $length; $i++, $random_string .= $valid_chars[mt_rand(1, $num_valid_chars)-1]);
    return $random_string;
}

Я думаю, что комментарии в значительной степени излишни, так как ответы, которые я использовал для создания этого, уже полностью прокомментированы. Ура!

Если вас не волнует время, память или эффективность процессора, и если ваша система может справиться с этим, почему бы не попробовать этот алгоритм?!

function randStr($len, $charset = 'abcdABCD0123') {
    $out = '';
    $str = array();

    for ($i = 0; $i < PHP_INT_MAX; $i++) {
        $str[$i] = $charset;

        shuffle($str);
        $charset .= implode($charset, $str);
        $charset = str_shuffle($charset);
    }

    $str = array_flip($str);
    $str = array_keys($str);

    for ($i = 0; $i < PHP_INT_MAX; $i++) {
        shuffle($str);
    }

    $str = implode('', $str);

    for ($i = 0; $i < strlen($str); $i++) {
        $index = mt_rand(1, strlen($str));
        $out .= $str[$index - 1];
    }

    for ($i = 0; $i < PHP_INT_MAX; $i++) {
        $out = str_shuffle($out);
    }

    return substr($out, 0, $len);
}

возможно, это будет лучше читать, если он использует рекурсию, но я не уверен, что PHP использует хвостовую рекурсию или нет...