有效地生成一个16个字符的字母数字字符串


76

我正在寻找一种为表中的主键生成字母数字唯一ID的快速方法。

这样的事情会起作用吗?

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

生成随机数的好方法是什么?如果我基于微时间,则必须考虑从不同实例同时调用genKey()的可能性。

还是有更好的方法来完成所有这些工作?


Answers:


105

由于没有一个答案会为您提供由0-9,az,AZ字符组成的随机字符串:这是一个可行的解决方案,可为您提供大约一个。62 ^ 16 = 4.76724 e + 28键:

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

即使不了解ASCII码,它也很容易阅读。

由于python 3.6.2以下原因,版本更短:

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

1
这个数字实际上是错误的。我更新了。通过将可能的字符数乘以字符串长度的幂来计算。
大卫·舒曼

1
感谢您提及random.choices。尚未听说过,并且比选择循环要快得多
Andrew

48

您可以使用此:

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

无法保证生成的密钥是唯一的,因此在原始插入失败的情况下,您应该准备使用新密钥重试。另外,您可能要考虑使用确定性算法从自动递增的id生成字符串,而不是使用随机值,因为这将确保您的唯一性(但它还会提供可预测的键)。


1
根据文档,random不是随机的而是伪随机的。请改用os.urandom。
尼古拉2010年

7
@prometheus。是os.urandom不是伪随机的?
aaronasterling

1
我在回应Mark Byers对“随机值”一词的宽松用法。os.urandom仍然是伪随机的,但是在密码学上是安全的伪随机的,与相比,它更适合于广泛的用例random
尼古拉

1
@nikola如果密钥只是伪随机的,它并不重要,它们用于索引。
2015年

3
也许很明显,但“确定性”并不意味着唯一,您必须实际检查算法是否具有很长的重复周期。get_key = lambda n: n % 10是确定性的,但并非长期存在。
2015年

37

看一下uuid模块(Python 2.5+)。

一个简单的例子:

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

请注意,OP要求输入16个字符的字母数字字符串,但UUID4字符串的长度为32个字符。您不应截断该字符串,而应使用完整的32个字符。


7
这是32个字符,截断Guids是不安全的。
Brian

正确(关于截断)。另一方面:我只存储32个字符(除非您有非常特殊的原因只存储16个字符)。
ChristopheD 2010年

1
@Brian嗨,我需要知道为什么Guids不安全?你有参考吗?
阿迪亚特·穆巴拉克

1
@AdiyatMubarak:从根本上讲,您不需要参考。指南被记录为唯一。Guid的一半没有记录为唯一。就是说,blogs.msdn.microsoft.com/oldnewthing/20080627-00/?p=21823贯穿了截断一种特定GUID算法时发生的情况。
布莱恩

19

在2016年12月发布的Python 3.6中,secrets引入了该模块。

您现在可以通过以下方式生成随机令牌:

import secrets

secrets.token_hex(16)

从Python文档:

secrets模块用于生成适用于管理数据(例如密码,帐户身份验证,安全性令牌和相关机密)的高密码强度随机数。

特别是,secrets应该优先使用模块中的默认伪随机数生成器,该生成器random是为建模和仿真而设计的,而不是安全性或加密技术。

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


7

对于随机数,一个好的来源是os.urandom

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

我忘记了这么多很棒的urandom函数:V,这很好,比将字符集添加到字符串然后循环然后更好。内置;)
m3nda

1
其他答案中也提到了这一点,您不应截断md5哈希。
bman

@bman:我知道截断vertan UUID存在严重问题,因为随机性不是线性分布的。vor MD5,这应该不是问题。
最大

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

4
您的解决方案将省略字符9,Z和z。同样,sample()仅选择每个字符一次。因此,它将减少很多排列。这将为您提供16个随机数字和大写/小写字母的字符串:''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(6666))
David Schumann 2015年

2

该值在每次调用时增加1(环绕)。确定最佳位置存储值取决于您如何使用它。您可能会发现这种有趣的解释,因为它不仅讨论了Guid的工作原理,而且还讨论了如何制作更小一些的Guid。

简短的答案是:将其中一些字符用作时间戳,将其他字符用作“ uniquifier”,在每次对uid生成器的调用中,该值将增加1。




-3

您可以在np.random中使用选择功能,该功能从字符列表中选择指定的字符数:

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

这将输出如下内容: 591FXwW61F4Q57av

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.