【问题标题】:What's the best way to generate random strings of a specific length in Python?在 Python 中生成特定长度的随机字符串的最佳方法是什么?
【发布时间】:2013-08-21 13:30:23
【问题描述】:

对于一个项目,我需要一种方法来创建数千个随机字符串,同时保持低冲突。我正在寻找它们只有 12 个字符长且仅大写。有什么建议吗?

【问题讨论】:

  • 你的意思是你不想要任何小写数字?
  • 嗯,是的,这应该澄清:)
  • 别忘了阅读这个关于the default random number generator in python的页面。冲突的可能性似乎完全取决于“随机字符串”的大小,但这并不意味着攻击者无法重新创建随机数;生成的随机数加密不安全
  • 哈,对。我的意思是字母数字。

标签: python random


【解决方案1】:

代码:

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

【讨论】:

  • 是的,这是误导性的:“12 位长且大写” -- 因为数字不能大写
  • 如果您需要字母数字,即 ASCII 大写加数字,那么 import digits print(''.join(choice(ascii_uppercase + digits) for i in range(12)))
  • 这是否每次都给出一个唯一的 ID?如果我从多个线程(例如其中 2 个)调用此函数 10000 次怎么办?在给定时间点发生冲突或获得相同 ID 的概率是多少?
  • @AnilJ 有关random 模块如何工作的更多信息,请阅读官方文档:docs.python.org/3/library/random.html
  • 好吧,digits 不在 Python3 上。您可以使用string.hexdigits 来获得“0123456789abcdefABCDEF”的混合,或者只使用string.digits + string.ascii_letters 来获得所有字母。
【解决方案2】:
#!/bin/python3
import random
import string
def f(n: int) -> str:
        bytes(random.choices(string.ascii_uppercase.encode('ascii'),k=n)).decode('ascii')

对于非常大的 n 跑得更快。避免 str 连接。

【讨论】:

    【解决方案3】:

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

    eg:length = 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"可以给你更复杂的结果。
    【解决方案4】:

    通过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'
    

    但如果你不想拥有Djangohere 是它的独立代码:

    代码:

    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))
    

    【讨论】:

      【解决方案5】:

      对于加密性强的伪随机字节,您可以使用围绕 OpenSSL 的 pyOpenSSL 包装器。

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

      from OpenSSL import rand
      
      b = rand.bytes(7)
      

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

      【讨论】:

      • 随机选择的 12 个大写字母不是对应于大约 56.4 位的熵吗?
      • 在 OpenSSL 的最新版本中不再支持 rand.bytes
      【解决方案6】:

      可以做一个生成器:

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

      【讨论】:

      • 那么使用生成器的好处是?
      • @martineau 可以一次取一个,设置具有不同变量的变量,可以一次取 n 个等...主要区别在于它实际上是一个可迭代的本身,而不是重复调用一个函数...
      • 为什么不会你只是重复调用一个函数?
      • functools.partial 可以修复参数,list(itertools.islice(gen, n)) 并不比[func() for _ in xrange(n)]
      • @user2357112 通过构建一个生成器,比恢复它的状态有一个优势,而不是重复设置和调用一个函数......而且listislice 将在实现级别工作而不是作为可能泄漏其_(在 Py 2.x 中)变量的列表组合,并且必须构建一个不必要的范围约束,否则会被处理......此外,在函数之上构建也更难,而不是流...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-25
      • 2011-06-22
      • 2018-07-03
      • 2020-12-01
      • 2010-10-19
      • 2012-03-17
      相关资源
      最近更新 更多