Wygeneruj losowy ciąg 5 znaków


102

Chcę utworzyć dokładny ciąg 5 losowych znaków z jak najmniejszą możliwością powielenia. Jaki byłby najlepszy sposób na zrobienie tego? Dzięki.


2
masz zestaw konkretnych znaków czy tylko 0-9a-zA-Z?
Yanick Rochon,

To wyzwanie w golfa kodowego może być znaleziskiem: codegolf.stackexchange.com/questions/119226/ ...
Titus

Używając Random::alphanumericString($length)możesz uzyskać ciągi z 0-9a-zA-Z, które pochodzą z kryptograficznie zabezpieczonych losowych danych.
krakaj

Odpowiedzi:


162
$rand = substr(md5(microtime()),rand(0,26),5);

Byłoby moim najlepszym przypuszczeniem - chyba że szukasz też znaków specjalnych:

$seed = str_split('abcdefghijklmnopqrstuvwxyz'
                 .'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                 .'0123456789!@#$%^&*()'); // and any other characters
shuffle($seed); // probably optional since array_is randomized; this may be redundant
$rand = '';
foreach (array_rand($seed, 5) as $k) $rand .= $seed[$k];

Przykład

I dla jednego opartego na zegarze (mniej kolizji, ponieważ jest przyrostowy):

function incrementalHash($len = 5){
  $charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  $base = strlen($charset);
  $result = '';

  $now = explode(' ', microtime())[1];
  while ($now >= $base){
    $i = $now % $base;
    $result = $charset[$i] . $result;
    $now /= $base;
  }
  return substr($result, -5);
}

Uwaga: przyrostowe oznacza łatwiejsze do odgadnięcia; Jeśli używasz tego jako soli lub tokena weryfikacyjnego, nie rób tego. Sól (teraz) „WCWyb” oznacza za 5 sekund „WCWyg”)


6
Problem z używaniem md5()polega jednak na tym, że otrzymujesz ciąg złożony z 16-znakowego zestawu (10 cyfr i ado f, czyli 4 bity na znak łańcucha). To może być wystarczające do niektórych celów, ale może być zbyt małe do celów kryptograficznych (pięć znaków z 16 symboli = 16 ^ 5 = 20 bitów = 1048576 możliwości).
Łuk

3
array_rand($seed, 5)zwróci klucz tablicy, a nie wartość, więc twój kod nie zadziała
alumi

1
Myślę, że używanie kryptograficznej funkcji skrótu do generowania losowych znaków jest co najmniej niewłaściwe.
gronostaj

Jest to możliwe także zawierać ", 'i ukośnik w $charset? Czy muszę używać sekwencji ucieczki, aby uwzględnić te znaki?
maja

1
Drugi fragment nawet nie używa $lenparametru wejściowego ... zapomniałeś o tym?
TechNyquist

76

Jeśli forbrakuje pętli, oto, czego lubię używać:

$s = substr(str_shuffle(str_repeat("0123456789abcdefghijklmnopqrstuvwxyz", 5)), 0, 5);

Ciekawe rozwiązanie ... bardzo zwięzłe, choć zastanawiam się, czy jest to szybsze czy wolniejsze niż używanie for. Jednak nie zawracam sobie głowy testem porównawczym :-)
Arc

@matthew str_shuffle będzie produkować duplikaty w sposób ciągły
SCC

@ user2826057: str_shuffle('ab')dałby abalbo baale nigdy aa. Dodanie str_repeatpozwala na to. Ale to powiedziawszy, rozwiązanie, które podałem, nie jest tak naprawdę poważne ... chociaż działa.
Matthew,

2
Istnieje prawdopodobieństwo 1/60466176, że tak się stanie, zakładając, że RNG jest równomiernie rozłożone.
Matthew,

1
Jest to idealne rozwiązanie w naszym przypadku generowania kodów kuponów. Usunęliśmy mylące znaki, jak l, i, 1, 0, O, etc i z 500 kuponów dostał żadnych duplikatów.
Luke Cousins



15

Poniższe powinny zapewnić najmniejszą szansę na powielenie (możesz chcieć zastąpić mt_rand()lepszym źródłem liczb losowych, np. Z /dev/*randomlub z identyfikatorów GUID):

<?php
    $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    $result = '';
    for ($i = 0; $i < 5; $i++)
        $result .= $characters[mt_rand(0, 61)];
?>

EDYCJA:
Jeśli obawiasz się o bezpieczeństwo, naprawdę nie używaj rand()lub mt_rand()i sprawdź, czy Twoje urządzenie do losowych danych jest w rzeczywistości urządzeniem generującym losowe dane, a nie zwykłym plikiem lub czymś przewidywalnym /dev/zero. mt_rand()uważane za szkodliwe:
https://spideroak.com/blog/20121205114003-exploit-information-leaks-in-random-numbers-from-python-ruby-and-php

EDYCJA: Jeśli masz obsługę OpenSSL w PHP, możesz użyć openssl_random_pseudo_bytes():

<?php
    $length = 5;
    $randomBytes = openssl_random_pseudo_bytes($length);
    $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    $charactersLength = strlen($characters);
    $result = '';
    for ($i = 0; $i < $length; $i++)
        $result .= $characters[ord($randomBytes[$i]) % $charactersLength];
?>

Pierwszy przykład, bardziej sprytny z wynikiem $. = $ Znaków [mt_rand (0, strlen ($ znaków) -1)]
hamboy75

W takim przypadku należy wcześniej określić długość i zapisać ją przynajmniej w zmiennej. Używanie strlen()w pętli jest niepotrzebnym narzutem, zwłaszcza jeśli już wiesz, że zestaw znaków nie zmieni się w międzyczasie.
Łuk

Wątpię, php jest zoptymalizowany, w każdym razie jest to opcja :)
hamboy75

7

Zawsze używam do tego tej samej funkcji, zwykle do generowania haseł. Jest łatwy w użyciu i przydatny.

function randPass($length, $strength=8) {
    $vowels = 'aeuy';
    $consonants = 'bdghjmnpqrstvz';
    if ($strength >= 1) {
        $consonants .= 'BDGHJLMNPQRSTVWXZ';
    }
    if ($strength >= 2) {
        $vowels .= "AEUY";
    }
    if ($strength >= 4) {
        $consonants .= '23456789';
    }
    if ($strength >= 8) {
        $consonants .= '@#$%';
    }

    $password = '';
    $alt = time() % 2;
    for ($i = 0; $i < $length; $i++) {
        if ($alt == 1) {
            $password .= $consonants[(rand() % strlen($consonants))];
            $alt = 0;
        } else {
            $password .= $vowels[(rand() % strlen($vowels))];
            $alt = 1;
        }
    }
    return $password;
}

Niezły kod. Jednak $ alt zawsze zmienia się z 1 na 0. Czy nie byłoby lepiej zamiast tego losować $ alt?
Nico Andrade


3
$str = '';
$str_len = 8;
for($i = 0, $i < $str_len; $i++){
    //97 is ascii code for 'a' and 122 is ascii code for z
    $str .= chr(rand(97, 122));
}
return $str

2

Jeśli jest w porządku, że otrzymasz tylko litery AF, oto moje rozwiązanie:

str_pad(dechex(mt_rand(0, 0xFFFFF)), 5, '0', STR_PAD_LEFT);

Uważam, że używanie funkcji skrótu jest przesadą w przypadku tak prostego zadania, jak generowanie sekwencji losowych cyfr szesnastkowych. dechex+ mt_randwykona tę samą pracę, ale bez zbędnej pracy kryptograficznej. str_padgwarantuje 5-znakową długość ciągu wyjściowego (jeśli liczba losowa jest mniejsza niż 0x10000).

Podwójne prawdopodobieństwo zależy od mt_randwiarygodności. Mersenne Twister jest znany z wysokiej jakości losowości, więc powinien dobrze pasować do zadania.


2

Nie wiedziałem też, jak to zrobić, dopóki nie pomyślałem o użyciu PHP array. I jestem prawie pewien, że jest to najprostszy sposób generowania losowego ciągu lub liczby z tablicami. Kod:

function randstr ($len=10, $abc="aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789") {
    $letters = str_split($abc);
    $str = "";
    for ($i=0; $i<=$len; $i++) {
        $str .= $letters[rand(0, count($letters)-1)];
    };
    return $str;
};

Możesz użyć tej funkcji w ten sposób

randstr(20)     // returns a random 20 letter string
                // Or like this
randstr(5, abc) // returns a random 5 letter string using the letters "abc"

1

Podobna do odpowiedzi Brada Christiego , ale używająca sha1algorytmu dla znaków 0-9a-zA-Zi poprzedzona losową wartością:

$str = substr(sha1(mt_rand() . microtime()), mt_rand(0,35), 5);

Ale jeśli ustawiłeś zdefiniowane (dozwolone) znaki:

$validChars = array('0','1','2' /*...*/,'?','-','_','a','b','c' /*...*/);
$validCharsCount = count($validChars);

$str = '';
for ($i=0; $i<5; $i++) {
    $str .= $validChars[rand(0,$validCharsCount - 1)];
}

** AKTUALIZACJA **

Jak zauważył Archimedix , nie gwarantuje to zwrócenia „najmniejszej możliwości powielenia”, ponieważ liczba kombinacji jest niska dla podanego zakresu znaków. Będziesz musiał albo zwiększyć liczbę znaków, albo pozwolić na dodatkowe (specjalne) znaki w ciągu. Myślę, że w twoim przypadku lepsze byłoby pierwsze rozwiązanie.


Używanie time()jest do 1 miliona razy bardziej przewidywalne niż microtime().
Łuk

Zwróć także uwagę na moje komentarze do odpowiedzi Brada, większym problemem jest to, że wygenerowany hash jest reprezentacją szesnastkową składającą się z 16 prawidłowych znaków, dlatego pozostawia tylko 1024 warianty dla $str.
Łuk

Być może @Archimedix, ale OP nie pytał o bezpieczeństwo, pytanie zostało zdefiniowane tak, aby mieć algorytm generowania ciągów składający się z kombinacji 5x26 różnych znaków i unikający duplikatów. To prawdopodobnie dla numeru referencyjnego potwierdzenia w procesie rejestracji (lub procesu rozliczeniowego), lub coś w tym stylu
Yanick Rochon

To było moje zrozumienie, że nie poprosić o najmniejszej możliwości uzyskania powielane , a ten ma 0,1% prawdopodobieństwo (1 w 1024), co mogło być prawie 1 do 1 miliarda.
Łuk

jeśli jednak potrzebujesz wygenerować klucz referencyjny składający się z 5 znaków i dać temu klientowi / klientowi, nie chcesz ich dawać " ˫§»⁋⅓" tylko dlatego, że jest bezpieczniejszy ... zwiększ swój klucz referencyjny do 7 lub więcej znaków . (BTW: poprawka w moim poprzednim komentarzu, to 5x36 znaków)
Yanick Rochon

1

działa dobrze w PHP (php 5.4.4)

$seed = str_split('abcdefghijklmnopqrstuvwxyz');
$rand = array_rand($seed, 5);
$convert = array_map(function($n){
    global $seed;
    return $seed[$n];
},$rand);

$var = implode('',$convert);
echo $var;

Live Demo


1

Źródło: funkcja PHP generująca losowe znaki

Ta prosta funkcja PHP zadziałała dla mnie:

function cvf_ps_generate_random_code($length=10) {

   $string = '';
   // You can define your own characters here.
   $characters = "23456789ABCDEFHJKLMNPRTVWXYZabcdefghijklmnopqrstuvwxyz";

   for ($p = 0; $p < $length; $p++) {
       $string .= $characters[mt_rand(0, strlen($characters)-1)];
   }

   return $string;

}

Stosowanie:

echo cvf_ps_generate_random_code(5);

1

Oto moje random 5 cents...

$random=function($a, $b) {
    return(
        substr(str_shuffle(('\\`)/|@'.
        password_hash(mt_rand(0,999999),
        PASSWORD_DEFAULT).'!*^&~(')),
        $a, $b)
    );
};

echo($random(0,5));

Nowa password_hash()funkcja PHP (*> = PHP 5.5) wykonuje zadanie generowania przyzwoicie długiego zestawu wielkich i małych liter oraz cyfr.

Dwa konkat. łańcuchy przed i po password_hashfunkcji $ random nadają się do zmiany.

Parametry dla $random()* ($ a, $ b) są w rzeczywistości substr()parametrami. :)

UWAGA: to nie musi być funkcją, może to być również normalna zmienna .. jak jeden paskudny singliner, na przykład:

$random=(substr(str_shuffle(('\\`)/|@'.password_hash(mt_rand(0,999999), PASSWORD_DEFAULT).'!*^&~(')), 0, 5));

echo($random);

1
function CaracteresAleatorios( $Tamanno, $Opciones) {
    $Opciones = empty($Opciones) ? array(0, 1, 2) : $Opciones;
    $Tamanno = empty($Tamanno) ? 16 : $Tamanno;
    $Caracteres=array("0123456789","abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    $Caracteres= implode("",array_intersect_key($Caracteres, array_flip($Opciones)));
    $CantidadCaracteres=strlen($Caracteres)-1;
    $CaracteresAleatorios='';
    for ($k = 0; $k < $Tamanno; $k++) {
        $CaracteresAleatorios.=$Caracteres[rand(0, $CantidadCaracteres)];
    }
    return $CaracteresAleatorios;
}

Porada dla profesjonalistów - Dołącz wyjaśnienie, w jaki sposób Twoja odpowiedź rozwiązuje problem PO i dlaczego może się różnić od innych już udzielonych odpowiedzi.
Tom

0

Używam tego:

<?php function fRand($len) {
    $str = '';
    $a = "abcdefghijklmnopqrstuvwxyz0123456789";
    $b = str_split($a);
    for ($i=1; $i <= $len ; $i++) { 
        $str .= $b[rand(0,strlen($a)-1)];
    }
    return $str;
} ?>

Kiedy to wywołasz, ustawia długość sznurka.

<?php echo fRand([LENGHT]); ?>

Możesz także zmienić możliwe znaki w ciągu $a.


Widzę, że stracony czas bezczynnie rozwija tę funkcję. Trochę googlowałem! @Andrew
LipESprY
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.