Clave De Identificación única —de Nuevo—


Objetivo: Clave De Identificación única —de Nuevo—
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/viewtopi...ghlight=#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:

strId = Hex(CFloat(Now) * 86400000)


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—.

sIzq = Hex(CFloat(Now) * 86400, 10) & Hex(Frac(Timer) * 1e6, 5)


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:

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


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—:

Mht5utsGuf
Mht5utsGuw
Mht5utsGvC
Mht5utsGvT
Mht5utsGvl
Mht5utsGv2
Mht5utsGwH
Mht5utsGwZ
Mht5utsGwq
Mht5utsGw8
Mht5utsGxN
Mht5utsGxe
Mht5utsGxw
Mht5utsGyB
Mht5utsGyT

última edición por Grandamakulo el Martes, 17 Septiembre 2019, 17:53; editado 2 veces
Objetivo: Re: Clave De Identificación única —de Nuevo—
¿Has probado esto?

Public function generaId() as string

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

end

Perfil MP  
Objetivo: 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:

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


Éste es el resultado:

ID
Sólo segundos HEX : C3BB53E9C289
Segundos y fracción HEX : 321B79D571261DB
Segundos y fracción b64 : Mht51XEmHb
Segundos y fracción b64$: MzIxQjc5RDU3MTI2MURC


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

última edición por Grandamakulo el Martes, 17 Septiembre 2019, 19:38; editado 1 vez
Objetivo: 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...

Perfil MP  
Objetivo: Re: Clave De Identificación única —de Nuevo—
shordi escribió: [Ver mensaje]
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...


Objetivo: Re: Clave De Identificación única —de Nuevo—
shordi escribió: [Ver mensaje]
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...



Perfil MP  

Página 1 de 1


  
No puede crear mensajes
No puede responder temas
No puede editar sus mensajes
No puede borrar sus mensajes
No puede votar en encuestas
No puede adjuntar archivos
Puede descargar archivos
No puede publicar eventos en el calendario

   

Está utilizando la versión (Lo-Fi). Para ver la versión completa del foro, haga clic aquí.

Powered by Icy Phoenix based on phpBB
Design by DiDiDaDo

Página generada en:: 0.3108s (PHP: -60% SQL: 160%)
Consultas SQL: 25 - Debug off - GZIP Activado