Domanda

Voglio creare un ID univoco ma uniqid() sta dando qualcosa come '492607b0ee414'. Quello che vorrei è qualcosa di simile a quello che offre tinyurl: '64k8ra'. Più corto, meglio è. Gli unici requisiti sono che non dovrebbe avere un ordine evidente e che dovrebbe apparire più bello di una sequenza di numeri apparentemente casuale. Le lettere sono preferite rispetto ai numeri e idealmente non sarebbe un caso misto. Poiché il numero di voci non sarà così elevato (fino a circa 10000), il rischio di collisione non è un fattore enorme.

Qualche suggerimento apprezzato.

È stato utile?

Soluzione

Crea una piccola funzione che restituisca lettere casuali per una determinata lunghezza:

<?php
function generate_random_letters($length) {
    $random = '';
    for ($i = 0; $i < $length; $i++) {
        $random .= chr(rand(ord('a'), ord('z')));
    }
    return $random;
}

Quindi vorrai chiamarlo fino a quando non sarà univoco, in pseudo-codice a seconda di dove memorizzerai tali informazioni:

do {
    $unique = generate_random_letters(6);
} while (is_in_table($unique));
add_to_table($unique);

Puoi anche assicurarti che le lettere non formino una parola in un dizionario. Possa essere l'intero dizione inglese o solo una dizione di parolacce per evitare cose che un cliente troverebbe di cattivo gusto.

MODIFICA: Vorrei anche aggiungere questo senso solo se, come hai intenzione di usarlo, non è per una grande quantità di elementi perché questo potrebbe essere piuttosto lento quanto più collisioni ottieni (ottenere un ID già nella tabella) . Naturalmente, vorrai una tabella indicizzata e dovrai modificare il numero di lettere nell'ID per evitare la collisione. In questo caso, con 6 lettere, avresti 26 ^ 6 = 308915776 possibili ID univoci (meno le parolacce) che dovrebbero essere sufficienti per il tuo bisogno di 10000.

EDIT: Se vuoi una combinazione di lettere e numeri puoi usare il seguente codice:

$random .= rand(0, 1) ? rand(0, 9) : chr(rand(ord('a'), ord('z')));

Altri suggerimenti

@gen_uuid () di gord.

preg_replace ha avuto alcuni brutti problemi utf-8, che a volte uid contengono " + " oppure " / " ;. Per ovviare a questo, devi esplicitamente creare il modello 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);
}

Mi ci è voluto un po 'per scoprirlo, forse fa risparmiare a qualcun altro un mal di testa

Puoi ottenerlo con meno codice:

function gen_uid($l=10){
    return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, $l);
}

Risultato (esempi):

  • cjnp56brdy
  • 9d5uv84zfa
  • ih162lryez
  • ri4ocf6tkj
  • xj04s83egi

Esistono due modi per ottenere un ID univoco affidabile: Renderlo così lungo e variabile che le possibilità di una collisione sono incredibilmente piccole (come con un GUID) o memorizzare tutti gli ID generati in una tabella per la ricerca (in memoria o in un DB o in un file) per verificare l'univocità al momento della generazione.

Se stai davvero chiedendo come puoi generare una chiave così breve e garantire la sua unicità senza un qualche tipo di controllo duplicato, la risposta è che non puoi.

Ecco la routine che uso per casuali base62 di qualsiasi lunghezza ...

La chiamata gen_uuid() restituisce stringhe come WJX0u0jV, E9EMaZ3P ecc.

Di default restituisce 8 cifre, quindi uno spazio di 64 ^ 8 o circa 10 ^ 14, questo è spesso sufficiente per rendere le collisioni abbastanza rare.

Per una stringa più grande o più piccola, passare $ len come desiderato. Nessun limite di lunghezza, come aggiungo fino a quando non sia soddisfatto [fino al limite di sicurezza di 128 caratteri, che può essere rimosso].

Nota, usa un salt casuale dentro md5 [o sha1 se preferisci], quindi non può essere facilmente retroingegnerizzato.

Non ho trovato conversioni base62 affidabili sul Web, quindi questo approccio per eliminare i caratteri dal risultato base64.

Utilizzare liberamente sotto licenza BSD, godere,

gord

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);
}

Soluzione davvero semplice:

Crea l'ID univoco con:

$id = 100;
base_convert($id, 10, 36);

Ottieni nuovamente il valore originale:

intval($str,36);

Non posso prendermi il merito per questo dato che proviene da un'altra pagina di overflow dello stack, ma ho pensato che la soluzione fosse così elegante e fantastica che valesse la pena copiarla su questo thread per le persone che fanno riferimento a questo.

È possibile utilizzare l'ID e convertirlo semplicemente in un numero di base 36 se si desidera convertirlo avanti e indietro. Può essere utilizzato per qualsiasi tabella con un ID intero.

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

Le persone intelligenti possono probabilmente capirlo con sufficienti esempi di identificazione. Non lasciare che questa oscurità sostituisca la sicurezza.

Mi è venuta in mente quella che penso sia una soluzione piuttosto interessante, senza un controllo di unicità. Ho pensato di condividere per eventuali visitatori futuri.

Un contatore è un modo davvero semplice per garantire l'univocità o se si utilizza un database una chiave primaria garantisce anche l'univocità. Il problema è che sembra male e potrebbe essere vulnerabile. Quindi ho preso la sequenza e l'ho confusa con un codice. Dal momento che il codice può essere invertito, so che ogni ID è unico mentre appare ancora casuale.

È python non php, ma ho caricato il codice qui: https://github.com/adecker89/Tiny-Unique-Identifiers

Le lettere sono belle, le cifre sono brutte. Vuoi stringhe casuali, ma non vuoi & Quot; brutto & Quot; stringhe casuali?

Crea un numero casuale e stampalo in stile alfa ( base-26 ), come la prenotazione " numeri " che le compagnie aeree danno.

Non ci sono funzioni di conversione di base per scopi generici integrate in PHP, per quanto ne so, quindi dovresti codificare quel bit tu stesso.

Un'altra alternativa: usa uniqid() e sbarazzati delle cifre.

function strip_digits_from_string($string) {
    return preg_replace('/[0-9]/', '', $string);
}

O sostituiscili con lettere:

function replace_digits_with_letters($string) {
    return strtr($string, '0123456789', 'abcdefghij');
}

Puoi anche farlo in questo modo:

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;
    }

Puoi farlo senza cose sporche / costose come loop, concatenazioni di stringhe o chiamate multiple a rand (), in modo pulito e di facile lettura. Inoltre, è meglio usare mt_rand():

function createRandomString($length)
{
    $random = mt_rand(0, (1 << ($length << 2)) - 1);
    return dechex($random);
}

Se hai bisogno che la stringa abbia la lunghezza esatta in ogni caso, riempi semplicemente il numero esadecimale con zeri:

function createRandomString($length)
{
    $random = mt_rand(0, (1 << ($length << 2)) - 1);
    $number = dechex($random);
    return str_pad($number, $length, '0', STR_PAD_LEFT);
}

Il " ritorno teorico " è che sei limitato alle capacità di PHP - ma in questo caso si tratta più di una questione filosofica;) Analizziamolo comunque:

  • PHP è limitato in ciò che può rappresentare come un numero esadecimale facendolo in questo modo. Questo sarebbe $length <= 8 almeno su un sistema a 32 bit, dove la limitazione di PHP dovrebbe essere 4.294.967.295.
  • Il generatore di numeri casuali di PHP ha anche un massimo. Per do { (generate ID) } while { (id is not uniqe) } (insert id) almeno su un sistema a 32 bit, dovrebbe essere 2.147.483.647
  • Quindi teoricamente sei limitato a 2.147.483.647 ID.

Tornando all'argomento - l'intuitivo <=> ha uno svantaggio e un possibile difetto che potrebbe portarti dritto al buio ...

Svantaggio: la convalida è pessimistica. Farlo in questo modo sempre richiede un controllo nel database. Avere abbastanza spazio per le chiavi (ad esempio una lunghezza di 5 per le voci da 10k) è molto improbabile che causi collisioni con la stessa frequenza, poiché potrebbe essere comparativamente meno consumo di risorse per provare a archiviare i dati e riprovare solo in caso di un errore KEY UNICO.

Difetto: L'utente A recupera un ID che viene verificato come non ancora utilizzato. Quindi il codice tenterà di inserire i dati. Ma nel frattempo, Utente B è entrato nello stesso ciclo e purtroppo recupera lo stesso numero casuale, poiché Utente A non è ancora stato memorizzato e questo ID era ancora libero. Ora il sistema memorizza Utente B o Utente A , e quando si tenta di memorizzare il secondo Utente, nel frattempo ce n'è già l'altro - con lo stesso ID.

Dovresti gestire quell'eccezione in ogni caso e riprovare l'inserimento con un ID appena creato. Aggiungendo questo mantenendo il ciclo di controllo pessimistico (che dovresti inserire nuovamente) risulterà in codice abbastanza brutto e difficile da seguire. Fortunatamente la soluzione a questo è la stessa di quella che ha lo svantaggio: provalo in primo luogo e prova a memorizzare i dati. In caso di errore UNIQUE KEY, riprovare con un nuovo ID.

Dai un'occhiata a questo articolo

Spiega come generare brevi ID unici dai tuoi ID bdd, come fa YouTube.

In realtà, la funzione nell'articolo è molto correlata a funzione php base_convert che converte un numero da una base a un'altra (ma è solo fino alla base 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);
}

Se ti piace una versione più lunga di ID univoco, utilizza questo:
$ uniqueid = sha1 (md5 (time ()));

Migliore risposta: Il più piccolo unico " Hash Like " ID database univoco fornito da una stringa: soluzione PHP, non sono necessarie librerie di terze parti.

Ecco il codice:

<?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;

}

?>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top