如何在Python中生成特定长度的随机字符串?

59
对于一个项目,我需要一种创建数千个随机字符串的方法,同时保持冲突率低。我要求这些随机字符串只有12个字符长,且全部为大写字母。有什么建议吗?

3
你的意思是你不想要任何小写数字吗? - martineau
嗯,是的,那应该澄清一下 :) - Maarten Bodewes
不要忘记阅读有关Python中默认随机数生成器的页面(http://docs.python.org/2/library/random.html)。碰撞的几率似乎完全取决于“随机字符串”的大小,但这并不意味着攻击者无法重新创建随机数;生成的随机数*不是加密安全的*。 - Maarten Bodewes
哈哈,对的。我是指字母数字。 - Brandon
7个回答

135

代码:

from random import choice
from string import ascii_uppercase

print(''.join(choice(ascii_uppercase) for i in range(12)))

输出:

5个示例:

QPUPZVVHUNSN
EFJACZEBYQEB
QBQJJEEOYTZY
EOJUSUEAJEEK
QWRWLIWDTDBD

编辑:

如果您仅需要数字,请使用digits常量,而不是来自string模块的ascii_uppercase

3个例子:

229945986931
867348810313
618228923380

4
是的,这很误导人:“12位数字且大写”--因为数字无法大写。 - Peter Varo
如果您需要字母数字字符,即ASCII大写字母加数字,则可以使用以下代码:import digits print(''.join(choice(ascii_uppercase + digits) for i in range(12))) - Sandeep Kanabar
@AnilJ 如需了解“random”模块的工作原理,请阅读官方文档:https://docs.python.org/3/library/random.html。 - Peter Varo
@goetzc string.digits 在 Python 3 中一直存在,从 3.0 版本开始。 - TheDiveO
@PeterVaro 几年晚了,但你能详细解释一下吗?我不明白一个数字怎么能变成大写。 - Itération 122442
显示剩余2条评论

23

使用 Django,您可以在 django.utils.crypto 模块中使用 get_random_string 函数。

get_random_string(length=12,
    allowed_chars=u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
    Returns a securely generated random string.

    The default length of 12 with the a-z, A-Z, 0-9 character set returns
    a 71-bit value. log_2((26+26+10)^12) =~ 71 bits

示例:

get_random_string()
u'ngccjtxvvmr9'

get_random_string(4, allowed_chars='bqDE56')
u'DDD6'

但如果您不想使用 Django这里 有其独立的代码:

代码:

import random
import hashlib
import time

SECRET_KEY = 'PUT A RANDOM KEY WITH 50 CHARACTERS LENGTH HERE !!'

try:
    random = random.SystemRandom()
    using_sysrandom = True
except NotImplementedError:
    import warnings
    warnings.warn('A secure pseudo-random number generator is not available '
                  'on your system. Falling back to Mersenne Twister.')
    using_sysrandom = False


def get_random_string(length=12,
                      allowed_chars='abcdefghijklmnopqrstuvwxyz'
                                    'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
    """
    Returns a securely generated random string.

    The default length of 12 with the a-z, A-Z, 0-9 character set returns
    a 71-bit value. log_2((26+26+10)^12) =~ 71 bits
    """
    if not using_sysrandom:
        # This is ugly, and a hack, but it makes things better than
        # the alternative of predictability. This re-seeds the PRNG
        # using a value that is hard for an attacker to predict, every
        # time a random string is required. This may change the
        # properties of the chosen random sequence slightly, but this
        # is better than absolute predictability.
        random.seed(
            hashlib.sha256(
                ("%s%s%s" % (
                    random.getstate(),
                    time.time(),
                    SECRET_KEY)).encode('utf-8')
            ).digest())
    return ''.join(random.choice(allowed_chars) for i in range(length))

4

可以制作一个生成器:

from string import ascii_uppercase
import random
from itertools import islice

def random_chars(size, chars=ascii_uppercase):
    selection = iter(lambda: random.choice(chars), object())
    while True:
        yield ''.join(islice(selection, size))

random_gen = random_chars(12)
print next(random_gen)
# LEQIITOSJZOQ
print next(random_gen)
# PXUYJTOTHWPJ

当需要时,只需从生成器中提取即可... 可以使用 next(random_gen) 在需要它们时进行提取,或者例如使用 random_200 = list(islice(random_gen, 200))...


2
使用生成器的好处是什么? - martineau
@martineau 可以一次取一个,使用不同变量设置每个,可以切片以一次取n个等等... 主要区别在于它本身就是一个可迭代对象,而不是重复调用函数... - Jon Clements
为什么你不会反复调用一个函数呢? - user2357112
恢复生成器状态与重复调用函数并没有优势,如果您想设置固定参数,functools.partial可以做到这一点。如果内部循环中没有Python级别的生成器和几个Python级别的函数调用,那么listislice在C中的事实将是一个优势。泄漏循环变量很烦人,但没有理由避免使用列表推导式。 - user2357112
如果您使用生成器,获取单个随机字符串的方式为 next(random_chars(n)),而对于普通函数则只需使用 random_chars(n)。循环遍历 k 个随机字符串的方式为 for s in islice(random_chars(n), k):,而对于普通函数则是 for i in xrange(k): s = random_chars(n)。我认为 islicenext 调用是警告信号,表明您实际上并不想在这里使用生成器。 - user2357112
显示剩余3条评论

1
#!/bin/python3
import random
import string
def f(n: int) -> str:
        bytes(random.choices(string.ascii_uppercase.encode('ascii'),k=n)).decode('ascii')

针对非常大的n,运行速度更快。避免字符串连接。


0

该函数生成指定长度的大写字母随机字符串。

例如:长度为6,将生成以下随机序列模式

YLNYVQ

    import random as r

    def generate_random_string(length):
        random_string = ''
        random_str_seq = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        for i in range(0,length):
            if i % length == 0 and i != 0:
                random_string += '-'
            random_string += str(random_str_seq[r.randint(0, len(random_str_seq) - 1)])
        return random_string

使用上述代码 random_str_seq = "ABC@#$%^!&_+|*()OPQRSTUVWXYZ" 可以得到更加复杂的结果。 - Iqra.

0
使用一个 set 存储已生成的值,实现一个不重复的随机生成器函数。请注意,在处理非常大的字符串或数量时,这将会占用一些内存,并且可能会稍微减慢速度。当达到给定数量或最大可能组合时,generator 将停止运行。
代码:
#!/usr/bin/env python

from typing import Generator
from random import SystemRandom as RND
from string import ascii_uppercase, digits


def string_generator(size: int = 1, amount: int = 1) -> Generator[str, None, None]:
    """
    Return x random strings of a fixed length.

    :param size: string length, defaults to 1
    :type size: int, optional
    :param amount: amount of random strings to generate, defaults to 1
    :type amount: int, optional
    :yield: Yield composed random string if unique
    :rtype: Generator[str, None, None]
    """
    CHARS = list(ascii_uppercase + digits)
    LIMIT = len(CHARS) ** size
    count, check, string = 0, set(), ''
    while LIMIT > count < amount:
        string = ''.join(RND().choices(CHARS, k=size))
        if string not in check:
            check.add(string)
            yield string
            count += 1


for my_count, my_string in enumerate(string_generator(12, 20)):
    print(my_count, my_string)

输出:

0 IESUASWBRHPD
1 JGGO1THKLC9K
2 BW04A5GWBA7K
3 KDQTY72BV1S9
4 FAOL5L28VVMN
5 NLDNNBGHTRTI
6 2RV6TE6BCQ8K
7 B79B8FBPUD07
8 89VXXRHPUN41
9 DFC8QJUY6HRB
10 FXYYDKVQHC5Z
11 57KTZE67RSCU
12 389H1UT7N6CI
13 AKZMN9XITAVB
14 6T9ACH3GDAYG
15 CH8RJUQMTMBE
16 SPQ7E02ZLFD3
17 YD6JFXGIF3YF
18 ZUSA2X6OVNCN
19 JQRH6LR229Y4

0

如果需要加密强度的伪随机字节,您可以使用 OpenSSL 的 pyOpenSSL 包装器。

它提供了 bytes 函数来收集一系列伪随机的 字节

from OpenSSL import rand

b = rand.bytes(7)

顺便说一下,12个大写字母的熵比56位多一点。你只需要读取7个字节。


1
12个随机选择的大写字母不会对应于大约56.4位的熵吗? - DSM
1
rand.bytes在最新版本的OpenSSL中已不再受支持。 - david

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接