https://foro.gambas-es.org/viewtopic.php?f=5&t=7719
-----------------------------------
Grandamakulo
Martes, 17 Septiembre 2019, 17:45

Clave De Identificación única —de Nuevo—
-----------------------------------
[align=justify]Aunque este tema se ha tratado en dos ocasiones —la primera de manera tangencial en 2016 y la siguiente este mismo año— al menos en este foro, creo que se puede aportar algo nuevo a este asunto:

https://foro.gambas-es.org/viewtopic.php?f=5&t=5837&p=34850&highlight=#p34850
https://foro.gambas-es.org/viewtopic.php?f=5&t=7543&start=0

La cuestión es generar muy rápida y sencillamente una clave única, o casi única, y que sea lo más corta posible. En uno de los hilos se propone el uso de uuidgen, pero las claves obtenidas son muy largas. La otra manera es emplear fecha y hora. La manera más sencilla es colocar como cadena el año, mes, día, hora, minuto y segundo desde Now(). Aunque funciona, es mejorable. Sus defectos son la longitud y que sólo se puede generar una por segundo. En cuanto a la longitud se puede solucionar empleando una codificación más eficiente, colocando directamente el número de segundos de Now() e, incluso mejor, pasándola a Hex. Se proponía en su día:

[code linenumbers=false syntax=gambas]strId = Hex(CFloat(Now) * 86400000)[/code]

Limpio, eficaz y en una sola línea. Pero, desde luego, sólo se puede generar una por segundo. Y, claro, no es única o casi. 
El problema del «segundo» se puede solucionar añadiendo Timer, que da una fracción de segundo —aunque no sincronizada con Now(), pero para esta aplicación, no es relevante—. 

[code linenumbers=false syntax=gambas]sIzq = Hex(CFloat(Now) * 86400, 10) & Hex(Frac(Timer) * 1e6, 5)[/code]

Y ya puestos a reducir, en lugar de emplear base 16, podemos emplear base 64, y así usamos bastantes caracteres imprimibles, en lugar de los 16 conocidos.
El siguiente código emplea los segundos totales de Now() —10 dígitos hexadecimales— más microsegundos como fracción de segundo de Timer —5 dígitos hexadecimales—. Se emplea base 64 por la facilidad de la transformación, ya que cada tres dígitos hex, se transforman en dos en b64, por lo que no hay que manejar ese «peazo» número:

[code linenumbers=false syntax=gambas]Public Function GenerarID() As String
  ' **** Generador de clave de IDentificación única
  ' <<<< Devuelve una ID como cadena de texto.
  ' >>>> No necesita argumentos. Emplea funciones de tiempo.
  
  ' Genera una clave haxadecimal de diez dígitos con el día hasta los segundos
  ' (cienmilésima de día aprox.) y otra de cinco dígitos con la parte fraccional
  ' de Timer.
  ' Luego la pasa de base 16 a base 64 tomando los dígitos de tres en tres y
  ' obtiene dos. De quince dígitos base 16 pasa a 10 base 64.

  Dim sB64 As String    ' Conjunto de dígitos en base 64
  Dim iSup As Integer   ' Dígito superior en B64
  Dim iInf As Integer   ' Dígito inferior en B64
  Dim sIzq As String    ' Fracción que queda por leer de base 16
  Dim sDer As String    ' Fracción 3 dígitos base 16
  Dim sRes As String    ' Resultado. Acumulador
  Dim iPas As Integer   ' Variable de paso

  sB64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  sIzq = Hex(CFloat(Now) * 86400, 10) & Hex(Frac(Timer) * 1e6, 5)
  sDer = ""
  sRes = ""
  Do Until Len(sIzq) = 0
    sDer = Right(sIzq, 3)
    sIzq = Left(sIzq, -3)
    iPas = Val("&" & sDer)
    iSup = iPas \ 64
    iInf = iPas Mod 64
    sRes = sB64[iSup] & sB64[iInf] & sRes
  Loop
  Return sRes
End[/code]

Puede generar un código —en mi ordenador— en unas 4,5 millonésimas de segundo. Quizá ésa es su debilidad: un equipo cinco veces más rápido que el mío podría generar dos códigos iguales seguidos. Tiene una solución obvia, pero creo que así es suficiente para la mayoría de las aplicaciones. En cuanto a la universalidad, existen dos consideraciones:
Por «arriba», el ciclo se repite cada 30.000 años, más o menos. Sin problema, de hecho, se podría reducir la longitud de la clave quitando los valores más significativos, a razón de un factor 64 por cada uno —un dígito nos lleva a un ciclo de unos 500 años, dos, ojo, a poco más de siete—.
Por «abajo», se genera un código distinto por cada millonésima de segundo, aunque existe el desfase comentado, pero al final la secuencia es la misma. Por tanto, existe una posibilidad elevada de que haya dos equipos que generen el mismo código. Depende de la necesidad de unicidad que realmente necesites, y la rapidez. Si quieres un código virtualmente único, pero largo y lento, uuidgen, sin duda. Si quieres algo más de andar por casa, con una unicidad aceptable, pero rápido como la cagalera y breve como la felicidad, éste que presento.
Y, como para muestra un botón, dejo ejemplos de códigos generados lo más rápidamente posible con mi equipo —como veis, la diferencia es de 22 ó 23 letras dentro del sistema b64, que se corresponden con esas 22 millonésimas de segundo: Debug tarda más que la asignación directa a una matriz—:
[font=Courier New]
Mht5utsGuf
Mht5utsGuw
Mht5utsGvC
Mht5utsGvT
Mht5utsGvl
Mht5utsGv2
Mht5utsGwH
Mht5utsGwZ
Mht5utsGwq
Mht5utsGw8
Mht5utsGxN
Mht5utsGxe
Mht5utsGxw
Mht5utsGyB
Mht5utsGyT[/font][/align]


-----------------------------------
shordi
Martes, 17 Septiembre 2019, 18:45

Re: Clave De Identificación única —de Nuevo—
-----------------------------------
¿Has probado esto?

[code linenumbers=false syntax=gambas]Public function generaId() as string

 return Base64(Hex(CFloat(Now) * 86400, 10) & Hex(Frac(Timer) * 1e6, 5))

end[/code]


-----------------------------------
Grandamakulo
Martes, 17 Septiembre 2019, 19:31

Re: Clave De Identificación única —de Nuevo—
-----------------------------------
Hola, Shordi:
La función Base64 pasa a dicha base los bytes de una cadena, no cambia de base. De hecho, ocupa un 33% más que la propia cadena que transforma.

Con este ejemplo se ve más fácil:

[code linenumbers=false syntax=gambas]Public Sub Main()

  Dim sB64 As String    ' Conjunto de dígitos en base 64
  Dim iSup As Integer   ' Dígito superior en B64
  Dim iInf As Integer   ' Dígito inferior en B64
  Dim sIzq As String    ' Fracción que queda por leer de base 16
  Dim sDer As String    ' Fracción 3 dígitos base 16
  Dim sRes As String    ' Resultado. Acumulador
  Dim iPas As Integer   ' Variable de paso

  Dim Alfa As Float
  Dim Beta As Float
  
  Alfa = CFloat(Now)
  Beta = Timer

  sB64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  sIzq = Hex(Alfa * 86400, 10) & Hex(Frac(Beta) * 1e6, 5)
  sDer = ""
  sRes = ""
  Do Until Len(sIzq) = 0
    sDer = Right(sIzq, 3)
    sIzq = Left(sIzq, -3)
    iPas = Val("&" & sDer)
    iSup = iPas \ 64
    iInf = iPas Mod 64
    sRes = sB64[iSup] & sB64[iInf] & sRes
  Loop
  
  Debug "ID"
  Debug "Sólo segundos HEX       : " & Hex(Alfa * 86400000)
  Debug "Segundos y fracción HEX : " & Hex(Alfa * 86400, 10) & Hex(Frac(Beta) * 1e6, 5)
  Debug "Segundos y fracción b64 : " & sRes
  Debug "Segundos y fracción b64$: " & Base64(Hex(Alfa * 86400, 10) & Hex(Frac(Beta) * 1e6, 5))

End[/code]

Éste es el resultado:

[font=Courier New]ID
Sólo segundos HEX       : C3BB53E9C289
Segundos y fracción HEX : 321B79D571261DB
Segundos y fracción b64 : Mht51XEmHb
Segundos y fracción b64$: MzIxQjc5RDU3MTI2MURC[/font]

PS.—Editado para usar los mismos datos de fecha y segundos en la generación de ID


-----------------------------------
shordi
Martes, 17 Septiembre 2019, 19:58

Re: Clave De Identificación única —de Nuevo—
-----------------------------------
Ok, no los había contado. Si la diferencia de tamaño es tan importante, llevas razón.

Pero no negarás que lo otro es más para ceporros apresurados como yo... :P


-----------------------------------
Grandamakulo
Martes, 17 Septiembre 2019, 20:05

Re: Clave De Identificación única —de Nuevo—
-----------------------------------
[quote user="shordi" post="46397"]Ok, no los había contado. Si la diferencia de tamaño es tan importante, llevas razón.

Pero no negarás que lo otro es más para ceporros apresurados como yo... :P[/quote]
 :lol:  :lol:  :lol:


-----------------------------------
gambafeliz
Miercoles, 18 Septiembre 2019, 05:15

Re: Clave De Identificación única —de Nuevo—
-----------------------------------
[quote user="shordi" post="46397"]Ok, no los había contado. Si la diferencia de tamaño es tan importante, llevas razón.

Pero no negarás que lo otro es más para ceporros apresurados como yo... :P[/quote]

 :lol:  :lol:  :lol:  :lol:


