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.

Piotr
źródło
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”)

Brad Christie
źródło
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);
Mateusz
źródło
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
25

Szybkim sposobem jest użycie najbardziej niestabilnych znaków funkcji uniqid .

Na przykład:

$rand = substr(uniqid('', true), -5);
John Parker
źródło
22

Możesz spróbować po prostu w ten sposób:

$length = 5;

$randomletter = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz"), 0, $length);

więcej szczegółów: http://forum.arnlweb.com/viewtopic.php?f=7&t=25

Apurba Pathak
źródło
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];
?>
Łuk
źródło
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;
}
Kristoffer la Cour
źródło
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
edrian
źródło
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.

gronostaj
źródło
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"

źródło
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.

Yanick Rochon
źródło
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

Lyoniel Farase
źródło
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);
Carl
źródło
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);
Straszny
źródło
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;
}
John F.
źródło
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.

LipESprY
źródło
Widzę, że stracony czas bezczynnie rozwija tę funkcję. Trochę googlowałem! @Andrew
LipESprY