题 在Python中使用大写字母和数字生成随机字符串


我想生成一个大小为N的字符串。

它应由数字和大写英文字母组成,例如:

  • 6U1S75
  • 4Z4UKK
  • U911K4

我怎样才能实现这一目标 Python的 办法?


1001
2018-02-13 12:23


起源


这是一个非常受欢迎的问题。我希望专家能够对前3个答案的这些随机数的唯一性加以考虑,即字符串大小范围的碰撞概率,比如从6到16。 - user
@buffer很容易计算出可能的组合数量。 10个数字+ 26个字母= 36个可能的字符,6的幂(字符串的长度)等于大约20亿。我对随机值的经验法则是“如果我为地球上的每个人生成了值,那么他们每个人有多少值?”。在这种情况下,每人少于一个值,因此如果这是为了识别用户或对象,则字符太少。一种替代方案是添加小写字母,使您达到62 ^ 6 =近570亿个唯一值。 - Blixt
虽然想到世界人口可能看起来很愚蠢,但这只是因为你想要为潜在的碰撞提供巨大的缓冲。看到生日问题: en.wikipedia.org/wiki/Birthday_problem - Blixt
@buffer,你会感兴趣的 这个答案 然后。 - Anish Ramaswamy


答案:


答案一行:

''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))

甚至更短,从Python 3.6开始使用 random.choices()

''.join(random.choices(string.ascii_uppercase + string.digits, k=N))

加密更安全的版本;看到 https://stackoverflow.com/a/23728630/2213647

''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))

具体而言,具有进一步重用的清洁功能:

>>> import string
>>> import random
>>> def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
...    return ''.join(random.choice(chars) for _ in range(size))
...
>>> id_generator()
'G5G74W'
>>> id_generator(3, "6793YUIO")
'Y3U'

它是如何工作的 ?

我们进口 string,一个包含常见ASCII字符序列的模块,和 random,一个处理随机生成的模块。

string.ascii_uppercase + string.digits 只是连接表示大写ASCII字符和数字的字符列表:

>>> string.ascii_uppercase
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> string.digits
'0123456789'
>>> string.ascii_uppercase + string.digits
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

然后我们使用列表推导来创建'n'元素列表:

>>> range(4) # range create a list of 'n' numbers
[0, 1, 2, 3]
>>> ['elem' for _ in range(4)] # we use range to create 4 times 'elem'
['elem', 'elem', 'elem', 'elem']

在上面的例子中,我们使用 [ 创建列表,但我们不在 id_generator 函数,因此Python不会在内存中创建列表,而是一个接一个地生成元素(更多关于此 这里)。

而不是要求创建字符串的'n'倍 elem,我们将要求Python创建一个随机字符的'n'次,从一系列字符中挑选:

>>> random.choice("abcde")
'a'
>>> random.choice("abcde")
'd'
>>> random.choice("abcde")
'b'

因此 random.choice(chars) for _ in range(size) 真正在创造一系列 size 字符。从中随机挑选的字符 chars

>>> [random.choice('abcde') for _ in range(3)]
['a', 'b', 'b']
>>> [random.choice('abcde') for _ in range(3)]
['e', 'b', 'e']
>>> [random.choice('abcde') for _ in range(3)]
['d', 'a', 'c']

然后我们只用一个空字符串连接它们,这样序列变成一个字符串:

>>> ''.join(['a', 'b', 'b'])
'abb'
>>> [random.choice('abcde') for _ in range(3)]
['d', 'c', 'b']
>>> ''.join(random.choice('abcde') for _ in range(3))
'dac'

2053
2018-02-13 12:26



这个怎么用???我是蟒蛇新手,喜欢它的极高水平,但这让我大吃一惊。有什么地方我可以阅读这方面的文件吗? - Youarefunny
@jorelli:这不是列表理解;它是一个生成器表达式。 - Ignacio Vazquez-Abrams
@Youarefunny:我编辑了答案,因此您将详细解释这些内容的工作原理。 - e-satis
@joreilli:我在答案中添加了一个关于此的快速注释,以及关于可迭代,列表推导,生成器以及最终yield关键字的更详细答案的链接。 - e-satis
@nurettin: en.wikipedia.org/wiki/Gettext#Operation - Ignacio Vazquez-Abrams


这个Stack Overflow问题是当前Google随机字符串Python的最高结果。目前最好的答案是:

''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))

这是一个很好的方法,但是 PRNG 随机不是加密安全的。我假设很多研究这个问题的人都希望生成加密或密码的随机字符串。您可以通过对上述代码进行少量更改来安全地执行此操作:

''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))

运用 random.SystemRandom() 而不只是在* nix机器上随机使用/ dev / urandom CryptGenRandom() 在Windows中。这些是加密安全的PRNG。运用 random.choice 代替 random.SystemRandom().choice 在一个需要安全PRNG的应用程序中可能具有潜在的破坏性,并且考虑到这个问题的普及,我敢打赌已经多次犯错。

如果您使用的是python3.6或更高版本,则可以使用新的 秘密 模块。

''.join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(N))

模块文档还讨论了方便的方法 生成安全令牌 和 最佳做法


450
2018-05-19 01:41



这值得更高或纳入公认的答案。微妙但重要的区别。 - user
我把它添加到接受的答案中。 - twasbrillig
是的,官方标准库 random 警告说:“警告:此模块的伪随机生成器不应用于安全目的。如果需要加密安全的伪随机数生成器,请使用os.urandom()或SystemRandom。“这是ref: random.SystemRandom 和 os.urandom - lord63. j
很好的答案。小记:你改成了 string.uppercase 这可能会导致意外的结果,具体取决于区域设置。运用 string.ascii_uppercase (要么 string.ascii_letters + string.digits 对于base62而不是base36)在涉及编码的情况下更安全。 - Blixt
小记 - 更好用 xrange 代替 range 因为后者生成内存列表,而前者创建迭代器。 - guyarad


只需使用Python的内置uuid:

如果UUID适合您的目的,请使用内置功能 UUID 包。

一线解决方案:

import uuid; uuid.uuid4().hex.upper()[0:6]

在深度版本中:

例:

import uuid
uuid.uuid4() #uuid4 => full random uuid
# Outputs something like: UUID('0172fc9a-1dac-4414-b88d-6b9a6feb91ea')

如果您需要完全符合您的格式(例如,“6U1S75”),您可以这样做:

import uuid

def my_random_string(string_length=10):
    """Returns a random string of length string_length."""
    random = str(uuid.uuid4()) # Convert UUID format to a Python string.
    random = random.upper() # Make all characters uppercase.
    random = random.replace("-","") # Remove the UUID '-'.
    return random[0:string_length] # Return the random string.

print(my_random_string(6)) # For example, D9E50C

133
2018-06-26 15:11



+1用于思考问题。也许你可以简单地解释一下uuid1和uuid4之间的区别。 - Thomas Ahle
如果我连续三次做uuid1,我会得到:d161fd16-ab0f-11e3-9314-00259073e4a8,d3535b56-ab0f-11e3-9314-00259073e4a8,d413be32-ab0f-11e3-9314-00259073e4a8,这些似乎都很可疑(前8个字符不同,其余的相同)。 uuid4不是这种情况 - Chase Roberts
uui1:从主机ID,序列号和当前时间生成UUID。 uuid4:生成随机UUID。 - Bijan
如果你想跳过字符串转换和连字符替换,你可以只调用my_uuid.get_hex()或uuid.uuid4()。get_hex(),它将返回从没有连字符的uuid生成的字符串。 - dshap
截断UUID是个好主意吗?取决于多小 string_length 是的,碰撞的概率可能是一个问题。 - user


使用更简单,更快但稍微随机的方式 random.sample 而不是分别选择每个字母,如果允许n次重复,则将随机基数扩大n倍,例如

import random
import string

char_set = string.ascii_uppercase + string.digits
print ''.join(random.sample(char_set*6, 6))

注意: random.sample可以防止字符重用,乘以字符集的大小可以实现多次重复,但它们仍然不太可能是纯随机选择。如果我们选择长度为6的字符串,并且我们选择'X'作为第一个字符,在选择示例中,获得第二个字符的'X'的几率与获得'X'的几率相同第一个角色。在random.sample实现中,将'X'作为任何后续字符的几率仅为获得第一个字符的几率的6/7


41
2018-02-13 12:44



这种方式并不差,但它并不像分别选择每个字符那样随意 sample 你永远不会得到两次相同的角色。当然,它也会失败 N 比......高 36。 - bobince
其中一个例子有重复,所以我怀疑他是否希望禁止重复。 - Mark Byers
''.join(random.sample(char_set*6,6)) 解决了这个问题。 - Shih-Wen Su
如果random.sample阻止字符重用,则将字符集的大小相乘会产生多次重复 可能,但他们仍然少 容易 然后他们是一个纯粹的随机选择。如果我们选择长度为6的字符串,并且我们选择'X'作为第一个字符,在选择示例中,获得第二个字符的'X'的几率与获得'X'的几率相同第一个角色。在random.sample实现中,将'X'作为任何后续字符的几率仅为获得第一个字符的几率的5/6。 - pcurry
当您在生成的字符串中移动时,重复获取特定字符的几率会下降。从26个大写字母加10个数字生成6个字符的字符串,随机选择每个字符,任何特定的字符串出现频率为1 /(36 ^ 6)。生成'FU3WYE'和'XXXXXX'的机会是一样的。在示例实现中,生成'XXXXXX'的机会是(1 /(36 ^ 6))*((6/6)*(5/6)*(4/6)*(3/6)*(2 / 6)*(1/6))由于random.sample的非替换特征。 'XXXXXX'在样本实现中的可能性降低了324倍。 - pcurry


import uuid
lowercase_str = uuid.uuid4().hex  

lowercase_str 是一个随机值 'cea8b32e00934aaea8c005a35d85a5c0'

uppercase_str = lowercase_str.upper()

uppercase_str 是 'CEA8B32E00934AAEA8C005A35D85A5C0'


23
2017-12-01 10:04



当随机值没有长度限制时它很好 - Savad KP
uppercase_str[:N+1] - Yajo
@Yajo是的,我们可以限制使用切片 - Savad KP
@Yajo:不,你不想切片十六进制值。与完整的大写字母和数字序列相比,您删除熵。也许base32编码该值(略微减少熵,从36 ** n到32 ** n,仍然优于16 ** n)。 - Martijn Pieters♦


从Ignacio那里得到答案,这适用于Python 2.6:

import random
import string

N=6
print ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))

输出示例:

JQUBT2


18
2018-02-13 12:35



因为我们正在生成随机字符串 string.ascii_uppercase + string.digits 〜 ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,有可能 random.choice 可能只会选择 letters,我测试了它,得到了 NZAUDKM, WBZMKFH,ZUJHHTU - akash karothiya
@akashkarothiya:这是真的,是OP隐含要求的。我刚刚测试了它并得到了: L1KBO7, BLSDEB, 3RQB59, PFOO20所以一切皆有可能。 - quamrana


更快,更简单,更灵活的方法是使用 strgen 模块(pip install StringGenerator)。

生成带有大写字母和数字的6个字符的随机字符串:

>>> from strgen import StringGenerator as SG
>>> SG("[\u\d]{6}").render()
u'YZI2CI'

获取一个唯一的列表:

>>> SG("[\l\d]{10}").render_list(5,unique=True)
[u'xqqtmi1pOk', u'zmkWdUr63O', u'PGaGcPHrX2', u'6RZiUbkk2i', u'j9eIeeWgEF']

保证 字符串中的一个“特殊”字符:

>>> SG("[\l\d]{10}&[\p]").render()
u'jaYI0bcPG*0'

随机HTML颜色:

>>> SG("#[\h]{6}").render()
u'#CEdFCa'

等等

我们需要意识到这一点:

''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))

可能没有数字(或大写字符)。

strgen 开发人员的时间比任何上述解决方案都快。 Ignacio的解决方案是最快的运行时间,使用Python标准库是正确的答案。但你几乎不会以那种形式使用它。您将需要使用SystemRandom(或者如果不可用则回退),确保表示所需的字符集,使用unicode(或不使用),确保连续调用生成唯一字符串,使用其中一个字符串模块字符类的子集,所有这些都需要比提供的答案更多的代码。概括解决方案的各种尝试都具有限制,strgen使用简单的模板语言以更加简洁和富有表现力的方式解决。

它在PyPI上:

pip install StringGenerator

披露:我是strgen模块的作者。


16
2017-08-26 11:47





如果你需要一个随机字符串而不是一个 伪随机 一,你应该用 os.urandom 作为来源

from os import urandom
from itertools import islice, imap, repeat
import string

def rand_string(length=5):
    chars = set(string.ascii_uppercase + string.digits)
    char_gen = (c for c in imap(urandom, repeat(1)) if c in chars)
    return ''.join(islice(char_gen, None, length))

8
2017-09-13 04:32



怎么 os.urandom 不伪随机?它可能使用更好的算法来生成更随机的数字,但它仍然是伪随机的。 - Tyilo
@Tyilo,看到这里 docs.python.org/2/library/os.html#os.urandom - John La Rooy
stackoverflow.com/questions/5635277/... - Tyilo
@Tyilo,我知道它们之间的区别 /dev/random 和 /dev/urandom。问题是 /dev/random 当没有足够的熵限制它的有用性时阻塞。为一个 一次性垫  /dev/urandom 不够好,但我认为这比伪随机更好。 - John La Rooy
我会说两者都是 /dev/random 和 /dev/urandom 是伪随机的,但它可能取决于您的定义。 - Tyilo


我以为没人回答这个哈哈!但是,嘿,这是我自己的:

import random

def random_alphanumeric(limit):
    #ascii alphabet of all alphanumerals
    r = (range(48, 58) + range(65, 91) + range(97, 123))
    random.shuffle(r)
    return reduce(lambda i, s: i + chr(s), r[:random.randint(0, len(r))], "")

8
2018-02-10 23:27



我不会对此投票,但我认为这对于这么简单的任务来说太复杂了。返回表达式是一个怪物。简单比复杂更好。 - Carl Smith
@CarlSmith,我的解决方案似乎有点矫枉过正,但我​​知道其他更简单的解决方案,并希望找到另一条通向良好答案的途径。没有自由,创造力就处于危险之中,因此我继续发布它。 - nemesisfixx


基于另一个Stack Overflow答案, 最轻量级的方法来创建随机字符串和随机十六进制数,比接受的答案更好的版本是:

('%06x' % random.randrange(16**6)).upper()

快多了。


8
2017-12-19 17:51



这很好,但它只会使用'A-F'而不是'A-Z'。此外,在参数化时,代码变得不那么好了 N。 - Thomas Ahle