Efficiently generate a 16-character, alphanumeric string

PythonHashRandom

Python Problem Overview


I'm looking for a very quick way to generate an alphanumeric unique id for a primary key in a table.

Would something like this work?

def genKey():
    hash = hashlib.md5(RANDOM_NUMBER).digest().encode("base64")
    alnum_hash = re.sub(r'[^a-zA-Z0-9]', "", hash)
    return alnum_hash[:16]

What would be a good way to generate random numbers? If I base it on microtime, I have to account for the possibility of several calls of genKey() at the same time from different instances.

Or is there a better way to do all this?

Python Solutions


Solution 1 - Python

As none of the answers provide you with a random string consisting of characters 0-9, a-z, A-Z: Here is a working solution which will give you one of approx. 62^16 = 4.76724 e+28 keys:

import random, string
x = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(16))
print(x)

It is also very readable without knowing ASCII codes by heart.

There is an even shorter version since python 3.6.2:

import random, string
x = ''.join(random.choices(string.ascii_letters + string.digits, k=16))
print(x)

Solution 2 - Python

You can use this:

>>> import random
>>> ''.join(random.choice('0123456789ABCDEF') for i in range(16))
'E2C6B2E19E4A7777'

There is no guarantee that the keys generated will be unique so you should be ready to retry with a new key in the case the original insert fails. Also, you might want to consider using a deterministic algorithm to generate a string from an auto-incremented id instead of using random values, as this will guarantee you uniqueness (but it will also give predictable keys).

Solution 3 - Python

Have a look at the uuid module (Python 2.5+).

A quick example:

>>> import uuid
>>> uid = uuid.uuid4()
>>> uid.hex
'df008b2e24f947b1b873c94d8a3f2201'

Note that the OP asked for a 16-character alphanumeric string, but UUID4 strings are 32 characters long. You should not truncate this string, instead, use the complete 32 characters.

Solution 4 - Python

In Python 3.6, released in December 2016, the secrets module was introduced.

You can now generate a random token this way :

import secrets

secrets.token_hex(16)

From the Python docs :

> The secrets module is used for generating cryptographically strong > random numbers suitable for managing data such as passwords, account > authentication, security tokens, and related secrets. > > In particularly, secrets should be used in preference to the default > pseudo-random number generator in the random module, which is designed > for modelling and simulation, not security or cryptography.

https://docs.python.org/3/library/secrets.html

Solution 5 - Python

For random numbers a good source is os.urandom:

 >> import os
 >> import hashlib
 >> random_data = os.urandom(128)
 >> hashlib.md5(random_data).hexdigest()[:16]

Solution 6 - Python

>>> import random
>>> ''.join(random.sample(map(chr, range(48, 57) + range(65, 90) + range(97, 122)), 16))
'CDh0geq3NpKtcXfP'

Solution 7 - Python

This value is incremented by 1 on each call (it wraps around). Deciding where the best place to store the value will depend on how you are using it. You may find http://blogs.msdn.com/oldnewthing/archive/2008/06/27/8659071.aspx">this</a> explanation of interest, as it discusses not only how Guids work but also how to make a smaller one.

The short answer is this: Use some of those characters as a timestamp and the other characters as a "uniquifier," a value increments by 1 on each call to your uid generator.

Solution 8 - Python

There's an official recipe:

import string
import secrets
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(16))
print(password)

This will create output similar to 'STCT3jdDUkppph03'.

Solution 9 - Python

I would prefer urandom over secrets.token_hex, as it samples from a richer character set and hence needs a smaller length to achieve the same entropy.

os.urandom, which reads from urandom, is considered secure (see the relevant answer in a question if urandom is secure). You can then read as much as you like from urandom and produce a random alphanummeric as follows:

import math
import os
def random_alphanumeric(str_len: int) -> str:
  rand_len = 3 * (math.ceil(str_len / 3) + 1)
  return base64.b64encode(os.urandom(rand_len), altchars=b'aA').decode('ascii')[:str_len]

NOTE: The above function is not secure. Since you need a "very quick way to generate an alphanumeric", this function sacrifices performance over security, since the frequencies of a and A (or whatever characters you choose to replace + and / with) will be increased compared to what urandom would give you otherwise.

If you put randomness above performance, you could do something like:

def secure_random_alphanumeric(str_len: int) -> str:
  ret = ''
  while len(ret) < str_len:
    rand_len = 3 * (math.ceil((str_len - len(ret)) / 3) + 2)
    ret += base64.b64encode(os.urandom(rand_len)).decode('ascii').replace('+', '').replace('/', '').replace('=', '')
  return ret[:str_len]

Note that chaining replace turns out to be faster than sequntially calling it, as per this answer.

Also, in the above, +1 is replaced by +2 when determining rand_lento reduce the number of iterations needed to achieve the requested length. You could even replace by +3 or more to reduce even more the possibility for an iteration, but then you would loose in performance at the chained replace calls.

Solution 10 - Python

import math
import secrets


def random_alphanum(length: int) -> str:
    if length == 0:
        return ''
    elif length < 0:
        raise ValueError('negative argument not allowed')
    else:
        text = secrets.token_hex(nbytes=math.ceil(length / 2))
        is_length_even = length % 2 == 0
        return text if is_length_even else text[1:]
  • uuid method is inefficient and limited because uuid only returns 36 characters, and is then truncated.
  • default psuedo-random number generator is not suitable for security or cryptographic applications, a standard module secrets is available and is designed for these applications.

Solution 11 - Python

Simply use python builtin uuid:

If UUIDs are okay for your purposes use the built in uuid package.

One Line Solution:

>>> import uuid
>>> str(uuid.uuid4().get_hex().upper()[0:16])
'40003A9B8C3045CA'

Solution 12 - Python

simply use python inbuilt uuid :

import uuid
print uuid.uuid4().hex[:16].upper()

Solution 13 - Python

You could use the choice function in np.random which chooses the number of characters specified from a list of characters:

import numpy as np
chars = np.array(list('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'))
np_codes = np.random.choice(chars,16)
print(''.join([val for val in np_codes]))

this outputs something like the following: 591FXwW61F4Q57av

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionensnareView Question on Stackoverflow
Solution 1 - PythonDavid SchumannView Answer on Stackoverflow
Solution 2 - PythonMark ByersView Answer on Stackoverflow
Solution 3 - PythonChristopheDView Answer on Stackoverflow
Solution 4 - PythonBrachamulView Answer on Stackoverflow
Solution 5 - PythonrlotunView Answer on Stackoverflow
Solution 6 - PythonJan MatějkaView Answer on Stackoverflow
Solution 7 - PythonBrianView Answer on Stackoverflow
Solution 8 - PythonmathandyView Answer on Stackoverflow
Solution 9 - PythonpavlarasView Answer on Stackoverflow
Solution 10 - PythonEdward CorrigallView Answer on Stackoverflow
Solution 11 - PythonJaykumar PatelView Answer on Stackoverflow
Solution 12 - PythonChirag MaliwalView Answer on Stackoverflow
Solution 13 - PythonPraveen Kumar SridharView Answer on Stackoverflow