การสร้างสตริงแบบสุ่มด้วยตัวอักษรและตัวเลขตัวพิมพ์ใหญ่


1335

ฉันต้องการสร้างสตริงที่มีขนาด N

ควรประกอบด้วยตัวเลขและตัวอักษรภาษาอังกฤษตัวพิมพ์ใหญ่เช่น:

  • 6U1S75
  • 4Z4UKK
  • U911K4

ฉันจะทำสิ่งนี้สำเร็จในแบบpythonicอย่างไร


11
นี่เป็นคำถามที่ได้รับความนิยมมาก ฉันหวังว่าผู้เชี่ยวชาญจะเพิ่มความพิเศษของตัวเลขสุ่มเหล่านี้สำหรับ 3 คำตอบยอดนิยมเช่นความน่าจะเป็นที่ปะทะกันในช่วงขนาดสตริงพูดตั้งแต่ 6 ถึง 16
ผู้ใช้

8
@buffer มันง่ายในการคำนวณจำนวนชุดค่าผสมที่เป็นไปได้ 10 ตัวเลข + 26 ตัวอักษร = 36 ตัวละครที่เป็นไปได้โดยใช้กำลัง 6 (ความยาวของสตริง) เท่ากับประมาณสองพันล้าน กฎง่ายๆสำหรับค่าสุ่มคือ "ถ้าฉันสร้างคุณค่าสำหรับมนุษย์ทุกคนบนโลกพวกเขาจะมีค่าได้กี่ค่า" ในกรณีนี้จะมีค่าน้อยกว่าหนึ่งค่าต่อคนดังนั้นหากนี่คือการระบุผู้ใช้หรือวัตถุมันมีอักขระน้อยเกินไป อีกทางเลือกหนึ่งคือการเพิ่มตัวอักษรตัวพิมพ์เล็กซึ่งจะทำให้คุณมีค่าไม่ซ้ำกันที่ 62 ^ 6 = เกือบ 57 พันล้านค่าที่ไม่ซ้ำกัน
Blixt

1
และในขณะที่มันอาจดูงี่เง่าที่จะคิดถึงประชากรโลกนั่นเป็นเพียงเพราะคุณต้องการบัฟเฟอร์ขนาดใหญ่สำหรับการชนที่อาจเกิดขึ้น ดูปัญหาวันเกิด: en.wikipedia.org/wiki/Birthday_problem
Blixt

1
@buffer คุณจะสนใจคำตอบนี้แล้ว
Anish Ramaswamy

ไม่ควรเปลี่ยนชื่อนี้"การสร้างสตริงแบบสุ่มที่ปลอดภัยการเข้ารหัส ... " ?
smci

คำตอบ:


2540

ตอบในหนึ่งบรรทัด:

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

จากนั้นเราจะใช้ list comprehension เพื่อสร้างรายการขององค์ประกอบ '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'

7
@jorelli: มันไม่เข้าใจรายการ มันเป็นนิพจน์ตัวสร้าง
Ignacio Vazquez-Abrams

2
@ joreilli: ฉันเพิ่มบันทึกย่อเกี่ยวกับเรื่องนี้ในคำตอบและลิงก์ไปยังคำตอบโดยละเอียดเพิ่มเติมเกี่ยวกับ iterable รายการเข้าใจกำเนิดและในที่สุดคำหลักผลตอบแทน
E-satis

1
ฉันต้องการแทนที่ด้วยrange xrange
ดร. Jan-Philip Gehrcke

1
มีประโยชน์มาก. น่าสนใจที่ Django ใช้โค้ดนี้เพื่อสร้างรหัสผ่านและโทเค็น CSRF แม้ว่าคุณควรแทนที่randomด้วยrandom.SystemRandom(): github.com/django/django/blob/…
ผู้ใช้

1
@ Chiel92 random.sampleสร้างตัวอย่างโดยไม่มีการแทนที่กล่าวอีกนัยหนึ่งโดยไม่มีความเป็นไปได้ที่จะทำซ้ำอักขระซึ่งไม่ได้อยู่ในข้อกำหนดของ OP ฉันไม่คิดว่ามันจะเป็นที่พึงปรารถนาสำหรับการใช้งานส่วนใหญ่
ontologist

557

Quesion Stack Overflow นี้เป็นผลการค้นหาอันดับต้น ๆ ของ Google ในปัจจุบันสำหรับ "Random string 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()แทนที่จะใช้แบบสุ่มใช้ / dev / urandom บนเครื่อง * nix และCryptGenRandom()ใน Windows เหล่านี้มีความปลอดภัย PRNGs cryptographically การใช้random.choiceแทนrandom.SystemRandom().choiceแอปพลิเคชันที่ต้องใช้ PRNG ที่ปลอดภัยอาจทำลายล้างและได้รับความนิยมจากคำถามนี้ฉันพนันได้เลยว่าความผิดพลาดนั้นเกิดขึ้นหลายครั้งแล้ว

หากคุณใช้ python3.6 ขึ้นไปคุณสามารถใช้โมดูลลับใหม่ตามที่ระบุไว้ในคำตอบของ MSeifert :

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

โมดูลเอกสารยังหารือถึงวิธีที่สะดวกในการสร้างโทเค็นที่ปลอดภัยและแนวทางปฏิบัติที่ดีที่สุดปฏิบัติที่ดีที่สุด


3
ใช่ไลบรารีมาตรฐานอย่างเป็นทางการสำหรับrandomได้เตือนสิ่งนี้: " คำเตือน : เครื่องกำเนิดไฟฟ้าแบบหลอกเทียมของโมดูลนี้ไม่ควรใช้เพื่อจุดประสงค์ด้านความปลอดภัยใช้ os.urandom () หรือ SystemRandom หากคุณต้องการเครื่องกำเนิดเลขสุ่มแบบเข้ารหัสลับที่ปลอดภัย " นี่คือการอ้างอิง: random.SystemRandomและos.urandom
lord63 j

4
คำตอบที่ดี บันทึกย่อขนาดเล็ก: คุณเปลี่ยนเป็นstring.uppercaseสิ่งที่สามารถนำไปสู่ผลลัพธ์ที่ไม่คาดคิดขึ้นอยู่กับชุดภาษา การใช้string.ascii_uppercase(หรือstring.ascii_letters + string.digitsสำหรับ base62 แทน base36) ปลอดภัยกว่าในกรณีที่เกี่ยวข้องกับการเข้ารหัส
Blixt

บันทึกย่อขนาดเล็ก - ดีกว่าที่จะใช้xrangeแทนที่จะrangeเป็นรายการหลังสร้างรายการในหน่วยความจำในขณะที่อดีตสร้างตัววนซ้ำ
guyarad

2
การสุ่มต่อยจะเป็นแบบเดิมหรือไม่ ฉันต้องการใช้คีย์หลัก
shakthydoss

3
@shakthydoss: ไม่ มันอาจจะกลับมา "AAA000" ซึ่งเป็นสตริงสุ่มและถัดไป "AAA000" ซึ่งเป็นยังสตริงแบบสุ่ม คุณต้องเพิ่มการตรวจสอบความเป็นเอกลักษณ์อย่างชัดเจน
usr2564301

189

เพียงใช้ Python builtin 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

14
+1 สำหรับการคิดถึงคำถาม บางทีคุณอาจอธิบายสั้น ๆ ถึงความแตกต่างระหว่าง uuid1 และ uuid4 ได้
โทมัส Ahle

1
ถ้าฉันทำ uuid1 สามครั้งติดต่อกันฉันจะได้รับ: d161fd16-ab0f-11e3-9314-00259073e4a8, d3535b56-ab0f-11e3-9314-00259073e4a8, d161fd16-ab0f-11e3-9314-9014 8 ตัวอักษรแรกแตกต่างกันและส่วนที่เหลือเหมือนกัน) นี่ไม่ใช่กรณีของ uuid4
Chase Roberts

9
uui1: สร้าง UUID จาก ID โฮสต์หมายเลขลำดับและเวลาปัจจุบัน uuid4: สร้าง UUID แบบสุ่ม
Bijan

7
หากคุณต้องการข้ามการคัดเลือกสตริง & ยัติภังค์แทนที่คุณสามารถโทร my_uuid.get_hex () หรือ uuid.uuid4 (). get_hex () และมันจะส่งคืนสตริงที่สร้างจาก uuid ที่ไม่มียัติภังค์
dshap

8
เป็นความคิดที่ดีที่จะตัดทอน UUID หรือไม่? string_lengthความน่าจะเป็นที่จะเกิดการชนนั้นขึ้นอยู่กับว่ามีขนาดเล็กเพียงใด
ผู้ใช้

44

วิธีที่ง่ายกว่าเร็วกว่า แต่สุ่มน้อยกว่าคือใช้random.sampleแทนการเลือกตัวอักษรแต่ละตัวแยกกันถ้าอนุญาตให้ใช้การซ้ำซ้อนได้ให้ขยายพื้นฐานการสุ่มของคุณโดย 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' เป็น อักขระตัวแรก ในการนำมาใช้แบบสุ่มตัวอย่างอัตราต่อรองของการได้รับ 'X' เป็นอักขระตัวต่อมาใด ๆ ที่มีโอกาสเพียง 6/7 เท่านั้นในการได้รับมันเป็นตัวอักษรตัวแรก


8
วิธีนี้ไม่เลว แต่ก็ไม่ค่อยสุ่มเท่าการเลือกตัวละครแต่ละตัวแยกกันเช่นเดียวกับที่sampleคุณจะไม่ได้รับตัวละครเดียวกันสองครั้ง นอกจากนี้ยังมีของหลักสูตรก็จะล้มเหลวสูงกว่าN 36
bobince

สำหรับกรณีการใช้งานที่กำหนด (ถ้าไม่มีการทำซ้ำก็โอเค) ฉันจะบอกว่ามันยังคงเป็นทางออกที่ดีที่สุด
Anurag Uniyal

3
ตัวอย่างหนึ่งมีการทำซ้ำดังนั้นฉันสงสัยว่าเขากำลังมองหาที่จะไม่อนุญาตให้ทำซ้ำ
Mark Byers

5
หาก Random.sample ป้องกันการใช้อักขระซ้ำการคูณขนาดของชุดอักขระทำให้สามารถทำซ้ำได้หลายครั้งแต่ยังมีโอกาสน้อยกว่านั้นพวกเขาจะอยู่ในตัวเลือกแบบสุ่มล้วนๆ ถ้าเราไปหาสตริงที่มีความยาว 6 และเราเลือก 'X' เป็นอักขระตัวแรกในตัวอย่างที่เลือกโอกาสที่จะได้ 'X' สำหรับตัวละครตัวที่สองนั้นเหมือนกับอัตราต่อรองของการได้รับ 'X' เป็น อักขระตัวแรก ในการนำมาใช้แบบสุ่มตัวอย่างอัตราต่อรองของการได้รับ 'X' เป็นตัวละครใด ๆ ที่ตามมามีเพียง 5/6 โอกาสที่จะได้รับมันเป็นตัวอักษรตัวแรก
pcurry

1
โอกาสที่จะได้รับอักขระซ้ำ ๆ นั้นลดลงเมื่อคุณเลื่อนไปตามสายอักขระที่สร้างขึ้น การสร้างสตริง 6 ตัวอักษรจาก 26 ตัวอักษรตัวพิมพ์ใหญ่บวก 10 หลักสุ่มเลือกตัวละครแต่ละตัวอย่างอิสระสตริงใด ๆ ที่เกิดขึ้นกับความถี่ 1 / (36 ^ 6) โอกาสในการสร้าง 'FU3WYE' และ 'XXXXXX' เหมือนกัน ในการใช้งานตัวอย่างโอกาสในการสร้าง 'XXXXXX' คือ (1 / (36 ^ 6)) * ((6/6) * (5/6) * (4/6) * (3/6) * (3/6) * (2 / 6) * (1/6)) เนื่องจากคุณสมบัติที่ไม่ใช่การแทนที่ของ random.sample 'XXXXXX' มีโอกาสน้อยกว่าในการใช้งานตัวอย่าง 324 เท่า
pcurry

32
import uuid
lowercase_str = uuid.uuid4().hex  

lowercase_str เป็นค่าสุ่มเช่น 'cea8b32e00934aaea8c005a35d85a5c0'

uppercase_str = lowercase_str.upper()

uppercase_str คือ 'CEA8B32E00934AAEA8C005A35D85A5C0'


2
uppercase_str[:N+1]
Yajo

@Yajo ใช่เราสามารถ จำกัด การใช้การหั่น
Savad KP

2
@Yajo: ไม่คุณไม่ต้องการแบ่งค่าฐานสิบหก คุณลบเอนโทรปีเมื่อเทียบกับลำดับตัวอักษรและตัวเลขแบบเต็ม บางที base32-encode ค่าแทน (ลดค่าเอนโทรปีจาก 36 ** n เป็น 32 ** n, ยังดีกว่า 16 ** n)
Martijn Pieters

19

วิธีที่เร็วขึ้นง่ายขึ้นและยืดหยุ่นมากขึ้นในการทำเช่นนี้คือการใช้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 Standard Library แต่คุณแทบจะไม่เคยใช้มันในรูปแบบนั้น คุณจะต้องการใช้ SystemRandom (หรือสำรองหากไม่พร้อมใช้งาน) ตรวจสอบให้แน่ใจว่ามีการแสดงชุดอักขระที่จำเป็นใช้ unicode (หรือไม่) ตรวจสอบให้แน่ใจว่าการเรียกใช้แบบต่อเนื่องสร้างสตริงที่ไม่ซ้ำกันใช้ชุดย่อยของ ฯลฯ ทั้งหมดนี้ต้องใช้รหัสมากกว่าจำนวนมากในคำตอบที่ให้ไว้ ความพยายามที่หลากหลายในการวางแนวทางการแก้ปัญหาทั้งหมดมีข้อ จำกัด ที่ strgen แก้ไขด้วยความกะทัดรัดและพลังที่แสดงออกโดยใช้ภาษาเทมเพลตอย่างง่าย

มันอยู่ใน PyPI:

pip install StringGenerator

การเปิดเผยข้อมูล: ฉันเป็นผู้เขียนโมดูล strgen


12

จาก Python 3.6 คุณควรใช้secretsโมดูลหากคุณต้องการให้มันปลอดภัยด้วยการเข้ารหัสแทนrandomโมดูล (มิฉะนั้นคำตอบนี้จะเหมือนกับ @Ignacio Vazquez-Abrams):

from secrets import choice
import string

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

หมายเหตุเพิ่มเติมอีกประการหนึ่ง: ความเข้าใจในรายการนั้นเร็วกว่าในกรณีที่str.joinใช้นิพจน์ตัวสร้าง


10

ขึ้นอยู่กับคำตอบสแต็คโอเวอร์โฟลอื่นวิธีที่มีน้ำหนักเบาที่สุดในการสร้างสตริงแบบสุ่มและตัวเลขฐานสิบหกแบบสุ่มเวอร์ชันที่ดีกว่าคำตอบที่ยอมรับจะเป็น:

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

เร็วกว่ามาก


1
นี่เป็นสิ่งที่ดีแม้ว่ามันจะใช้ 'AF' เท่านั้นและไม่ใช่ 'A-Z' นอกจากนี้รหัสที่ได้รับมีความสุขน้อยลงเล็กน้อยเมื่อ Nparametrize
โทมัส Ahle

9

หากคุณต้องการสตริงสุ่มแทนที่จะเป็นแบบสุ่มหลอกคุณควรใช้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))

3
เป็นวิธีการที่os.urandomไม่ได้หลอกสุ่ม? อาจใช้อัลกอริธึมที่ดีกว่าในการสร้างตัวเลขที่สุ่มมากกว่า แต่ก็ยังสุ่มหลอก
Tyilo

@Tyilo ผมตระหนักถึงความแตกต่างระหว่างและ/dev/random /dev/urandomปัญหาคือ/dev/randomบล็อกเมื่อมีเอนโทรปีไม่เพียงพอซึ่ง จำกัด ประโยชน์ สำหรับแผ่นเวลาหนึ่ง /dev/urandomยังไม่ดีพอ แต่ฉันคิดว่ามันดีกว่าการสุ่มหลอกที่นี่
John La Rooy

1
ฉันจะบอกว่าทั้งสอง/dev/randomและ/dev/urandomหลอกแบบสุ่ม แต่มันอาจขึ้นอยู่กับคำจำกัดความของคุณ
Tyilo

9

ฉันคิดว่าไม่มีใครตอบเรื่องนี้เลยฮ่า ๆ ! แต่เดี๋ยวก่อนนี่เป็นของฉันไปที่:

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))], "")

4
ฉันจะไม่ลงคะแนนนี้ แต่ฉันคิดว่ามันซับซ้อนเกินไปสำหรับงานง่าย ๆ การกลับมาเป็นสัตว์ประหลาด เรียบง่ายดีกว่าซับซ้อน
คาร์ลสมิ ธ

12
@CarlSmith จริงวิธีการแก้ปัญหาของฉันดูเหมือนว่า overkill สำหรับงาน แต่ฉันก็ตระหนักถึงวิธีการแก้ปัญหาที่ง่ายกว่าอื่น ๆ และต้องการเพียงแค่หาเส้นทางทางเลือกเพื่อคำตอบที่ดี หากปราศจากอิสรภาพความคิดสร้างสรรค์ตกอยู่ในอันตราย
nemesisfixx

7

วิธีนี้เร็วกว่าเล็กน้อยและน่ารำคาญกว่าวิธี random.choice () วิธี Ignacio ที่โพสต์

มันใช้ประโยชน์จากธรรมชาติของอัลกอริธึมแบบหลอกเทียมและแบ๊งส์ในระดับบิตและเปลี่ยนเร็วกว่าการสร้างหมายเลขสุ่มใหม่สำหรับแต่ละตัวละคร

# must be length 32 -- 5 bits -- the question didn't specify using the full set
# of uppercase letters ;)
_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'

def generate_with_randbits(size=32):
    def chop(x):
        while x:
            yield x & 31
            x = x >> 5
    return  ''.join(_ALPHABET[x] for x in chop(random.getrandbits(size * 5))).ljust(size, 'A')

... สร้างตัวสร้างที่ใช้หมายเลข 5 บิตในเวลา 0..31 จนกระทั่งไม่มีใครเหลือ

... join () ผลลัพธ์ของตัวสร้างบนตัวเลขสุ่มด้วยบิตที่ถูกต้อง

ด้วย Timeit สำหรับสตริง 32 อักขระเวลาคือ:

[('generate_with_random_choice', 28.92901611328125),
 ('generate_with_randbits', 20.0293550491333)]

... แต่สำหรับ 64 สายอักขระ randbits สูญเสีย;)

ฉันอาจจะไม่ใช้วิธีการนี้ในรหัสการผลิตเว้นแต่ฉันจะไม่ชอบเพื่อนร่วมงานของฉัน

แก้ไข: อัปเดตเพื่อให้เหมาะกับคำถาม (ตัวพิมพ์ใหญ่และตัวเลขเท่านั้น) และใช้ตัวดำเนินการบิต & & >> แทน% และ //


5

ฉันจะทำแบบนี้:

import random
from string import digits, ascii_uppercase

legals = digits + ascii_uppercase

def rand_string(length, char_set=legals):

    output = ''
    for _ in range(length): output += random.choice(char_set)
    return output

หรือเพียงแค่:

def rand_string(length, char_set=legals):

    return ''.join( random.choice(char_set) for _ in range(length) )

5

ใช้ฟังก์ชั่น random.choice () ของ Numpy

import numpy as np
import string        

if __name__ == '__main__':
    length = 16
    a = np.random.choice(list(string.ascii_uppercase + string.digits), length)                
    print(''.join(a))

เอกสารอยู่ที่นี่http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.random.choice.html


1
ทำไมฉันถึงควรใช้แบบสุ่ม numpy แทน python stdlib random?
Pax0r

เพราะมันช่วยให้ตัวเลือกเพิ่มเติมในการขัดแย้งเช่นความยาวความน่าจะเป็นของตัวแปรและการเลือกด้วยการแทนที่
Mudit Jain

5

บางครั้ง 0 (ศูนย์) & O (ตัวอักษร O) อาจสร้างความสับสน ดังนั้นฉันใช้

import uuid
uuid.uuid4().hex[:6].upper().replace('0','X').replace('O','Y')

4
>>> import string 
>>> import random

ตรรกะต่อไปนี้ยังคงสร้างตัวอย่างสุ่ม 6 ตัวอักษร

>>> print ''.join(random.sample((string.ascii_uppercase+string.digits),6))
JT7K3Q

ไม่จำเป็นต้องคูณด้วย 6

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

TK82HK

3
แต่ตัวแปรนี้จะบังคับให้ตัวละครทุกตัวแตกต่างกัน และมันจะไม่ทำงานหาก N มีขนาดใหญ่กว่า len (string.ascii_uppercase + string.digits)
MarSoft

3

สำหรับผู้ที่ชอบใช้งาน python:

from itertools import imap, starmap, islice, repeat
from functools import partial
from string import letters, digits, join
from random import choice

join_chars = partial(join, sep='')
identity = lambda o: o

def irand_seqs(symbols=join_chars((letters, digits)), length=6, join=join_chars, select=choice, breakup=islice):
    """ Generates an indefinite sequence of joined random symbols each of a specific length
    :param symbols: symbols to select,
        [defaults to string.letters + string.digits, digits 0 - 9, lower and upper case English letters.]
    :param length: the length of each sequence,
        [defaults to 6]
    :param join: method used to join selected symbol, 
        [defaults to ''.join generating a string.]
    :param select: method used to select a random element from the giving population. 
        [defaults to random.choice, which selects a single element randomly]
    :return: indefinite iterator generating random sequences of giving [:param length]
    >>> from tools import irand_seqs
    >>> strings = irand_seqs()
    >>> a = next(strings)
    >>> assert isinstance(a, (str, unicode))
    >>> assert len(a) == 6
    >>> assert next(strings) != next(strings)
    """
    return imap(join, starmap(breakup, repeat((imap(select, repeat(symbols)), None, length))))

มันสร้างตัววนซ้ำแบบไม่ จำกัด [infinite] ของการสุ่มลำดับโดยการสร้างลำดับแบบไม่ จำกัด ของสัญลักษณ์ที่สุ่มเลือกจากกลุ่มการให้แล้วแบ่งลำดับนี้เป็นส่วนความยาวที่เข้าร่วมแล้วควรทำงานกับลำดับใด ๆ ที่สนับสนุน getitem โดยค่าเริ่มต้นมันจะสร้างลำดับของตัวอักษรและตัวเลขแบบสุ่มแม้ว่าคุณจะสามารถปรับเปลี่ยนเพื่อสร้างสิ่งอื่น ๆ ได้อย่างง่ายดาย:

ตัวอย่างเช่นการสร้างอันดับของตัวเลขสุ่ม:

>>> irand_tuples = irand_seqs(xrange(10), join=tuple)
>>> next(irand_tuples)
(0, 5, 5, 7, 2, 8)
>>> next(irand_tuples)
(3, 2, 2, 0, 3, 1)

หากคุณไม่ต้องการใช้รุ่นต่อไปคุณสามารถทำให้เรียกได้ว่า:

>>> irand_tuples = irand_seqs(xrange(10), join=tuple)
>>> make_rand_tuples = partial(next, irand_tuples) 
>>> make_rand_tuples()
(1, 6, 2, 8, 1, 9)

หากคุณต้องการสร้างลำดับในการบินเพียงตั้งค่าการเข้าร่วมเพื่อระบุตัวตน

>>> irand_tuples = irand_seqs(xrange(10), join=identity)
>>> selections = next(irand_tuples)
>>> next(selections)
8
>>> list(selections)
[6, 3, 8, 2, 2]

ตามที่คนอื่น ๆ พูดถึงถ้าคุณต้องการความปลอดภัยเพิ่มขึ้นจากนั้นตั้งค่าฟังก์ชั่นการเลือกที่เหมาะสม:

>>> from random import SystemRandom
>>> rand_strs = irand_seqs(select=SystemRandom().choice)
'QsaDxQ'

ตัวเลือกเริ่มต้นคือchoiceสิ่งที่อาจเลือกสัญลักษณ์เดียวกันหลายครั้งสำหรับแต่ละชิ้นถ้าคุณต้องการให้สมาชิกเดิมเลือกมากที่สุดหนึ่งครั้งสำหรับแต่ละกลุ่ม

>>> from random import sample
>>> irand_samples = irand_seqs(xrange(10), length=1, join=next, select=lambda pool: sample(pool, 6))
>>> next(irand_samples)
[0, 9, 2, 3, 1, 6]

เราใช้sampleเป็นตัวเลือกของเราเพื่อทำการเลือกอย่างสมบูรณ์ดังนั้นชิ้นจริง ๆ แล้วมีความยาว 1 และการเข้าร่วมเราเพียงแค่เรียกnextสิ่งที่เรียกว่าอันถัดไปสร้างขึ้นอย่างสมบูรณ์รับตัวอย่างนี้ดูเหมือนยุ่งยากเล็กน้อยและมันเป็น ...


3

(1) สิ่งนี้จะให้ตัวพิมพ์ใหญ่และตัวเลขทั้งหมด:

import string, random
passkey=''
for x in range(8):
    if random.choice([1,2]) == 1:
        passkey += passkey.join(random.choice(string.ascii_uppercase))
    else:
        passkey += passkey.join(random.choice(string.digits))
print passkey 

(2) หากคุณต้องการรวมตัวอักษรตัวเล็กในคีย์ของคุณในภายหลังนี่จะใช้งานได้:

import string, random
passkey=''
for x in range(8):
    if random.choice([1,2]) == 1:
        passkey += passkey.join(random.choice(string.ascii_letters))
    else:
        passkey += passkey.join(random.choice(string.digits))
print passkey  

3

นี่เป็นการตอบสนองของ Anurag Uniyal และสิ่งที่ฉันกำลังทำกับตัวเอง

import random
import string

oneFile = open('‪Numbers.txt', 'w')
userInput = 0
key_count = 0
value_count = 0
chars = string.ascii_uppercase + string.digits + string.punctuation

for userInput in range(int(input('How many 12 digit keys do you want?'))):
    while key_count <= userInput:
        key_count += 1
        number = random.randint(1, 999)
        key = number

        text = str(key) + ": " + str(''.join(random.sample(chars*6, 12)))
        oneFile.write(text + "\n")
oneFile.close()

2
>>> import random
>>> str = []
>>> chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
>>> num = int(raw_input('How long do you want the string to be?  '))
How long do you want the string to be?  10
>>> for k in range(1, num+1):
...    str.append(random.choice(chars))
...
>>> str = "".join(str)
>>> str
'tm2JUQ04CK'

random.choiceฟังก์ชั่นหยิบรายการที่สุ่มในรายการ นอกจากนี้คุณยังสร้างรายการเพื่อให้คุณสามารถเพิ่มตัวละครในforงบ ในตอนท้ายของ str คือ ['t', 'm', '2', 'J', 'U', 'Q', '0', '4', 'C', 'K'] แต่str = "".join(str)ใช้เวลา 'tm2JUQ04CK'ดูแลการที่ออกจากคุณกับ

หวังว่านี่จะช่วยได้!


ดี แต่คุณจะได้ใช้range(num)แทนและ STR str += random.choice(chars)จะได้รับสตริง
sashk

2
import string
from random import *
characters = string.ascii_letters + string.punctuation  + string.digits
password =  "".join(choice(characters) for x in range(randint(8, 16)))
print password

2
แม้ว่ารหัสนี้อาจตอบคำถาม แต่การให้บริบทเพิ่มเติมเกี่ยวกับสาเหตุและ / หรือวิธีการตอบคำถามนั้นจะช่วยปรับปรุงมูลค่าระยะยาวได้อย่างมีนัยสำคัญ โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบาย
Toby Speight

2
import random
q=2
o=1
list  =[r'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','s','0','1','2','3','4','5','6','7','8','9','0']
while(q>o):
    print("")

    for i in range(1,128):
        x=random.choice(list)
        print(x,end="")

ความยาวของสตริงสามารถเปลี่ยนได้สำหรับลูปเช่นสำหรับ i ในช่วง (1, ความยาว) มันเป็นอัลกอริธึมที่เข้าใจง่าย มันใช้รายการเพื่อให้คุณสามารถทิ้งอักขระที่คุณไม่ต้องการ


1

ง่าย ๆ :

import string
import random
character = string.lowercase + string.uppercase + string.digits + string.punctuation
char_len = len(character)
# you can specify your password length here
pass_len = random.randint(10,20)
password = ''
for x in range(pass_len):
    password = password + character[random.randint(0,char_len-1)]
print password

0

ฉันอยากจะแนะนำตัวเลือกต่อไปของคุณ:

import crypt
n = 10
crypt.crypt("any sring").replace('/', '').replace('.', '').upper()[-n:-1]

โหมดหวาดระแวง:

import uuid
import crypt
n = 10
crypt.crypt(str(uuid.uuid4())).replace('/', '').replace('.', '').upper()[-n:-1]

0

สองวิธี:

import random, math

def randStr_1(chars:str, length:int) -> str:
    chars *= math.ceil(length / len(chars))
    chars = letters[0:length]
    chars = list(chars)
    random.shuffle(characters)

    return ''.join(chars)

def randStr_2(chars:str, length:int) -> str:
    return ''.join(random.choice(chars) for i in range(chars))


เกณฑ์มาตรฐาน:

from timeit import timeit

setup = """
import os, subprocess, time, string, random, math

def randStr_1(letters:str, length:int) -> str:
    letters *= math.ceil(length / len(letters))
    letters = letters[0:length]
    letters = list(letters)
    random.shuffle(letters)
    return ''.join(letters)

def randStr_2(letters:str, length:int) -> str:
    return ''.join(random.choice(letters) for i in range(length))
"""

print('Method 1 vs Method 2', ', run 10 times each.')

for length in [100,1000,10000,50000,100000,500000,1000000]:
    print(length, 'characters:')

    eff1 = timeit("randStr_1(string.ascii_letters, {})".format(length), setup=setup, number=10)
    eff2 = timeit("randStr_2(string.ascii_letters, {})".format(length), setup=setup, number=10)
    print('\t{}s : {}s'.format(round(eff1, 6), round(eff2, 6)))
    print('\tratio = {} : {}\n'.format(eff1/eff1, round(eff2/eff1, 2)))

ผลผลิต:

Method 1 vs Method 2 , run 10 times each.

100 characters:
    0.001411s : 0.00179s
    ratio = 1.0 : 1.27

1000 characters:
    0.013857s : 0.017603s
    ratio = 1.0 : 1.27

10000 characters:
    0.13426s : 0.151169s
    ratio = 1.0 : 1.13

50000 characters:
    0.709403s : 0.855136s
    ratio = 1.0 : 1.21

100000 characters:
    1.360735s : 1.674584s
    ratio = 1.0 : 1.23

500000 characters:
    6.754923s : 7.160508s
    ratio = 1.0 : 1.06

1000000 characters:
    11.232965s : 14.223914s
    ratio = 1.0 : 1.27

ประสิทธิภาพของวิธีแรกดีกว่า


0

ฉันไปแล้วแม้ว่าคำตอบเกือบทั้งหมด แต่ไม่มีคำตอบใดที่ดูง่ายขึ้น ฉันอยากจะแนะนำให้คุณลองใช้รหัสผ่านห้องสมุดซึ่งโดยทั่วไปจะใช้ในการสร้างรหัสผ่านแบบสุ่ม

คุณสามารถสร้างสตริงสุ่มที่คุณเลือกความยาวเครื่องหมายวรรคตอนตัวเลขตัวอักษรและเล็ก

นี่คือรหัสสำหรับกรณีของคุณ:

from passgen import passgen
string_length = int(input())
random_string = passgen(length=string_length, punctuation=False, digits=True, letters=True, case='upper')

0

สร้างตัวอักษร 16 บิตประกอบด้วยรหัสตัวอักษรตัวเลข '_' และ '-'

os.urandom(16).translate((f'{string.ascii_letters}{string.digits}-_'*4).encode('ascii'))


0

ฉันกำลังดูคำตอบต่าง ๆ และใช้เวลาอ่านเอกสารความลับ

โมดูลลับใช้สำหรับสร้างหมายเลขสุ่มที่รัดกุมเหมาะสำหรับการจัดการข้อมูลเช่นรหัสผ่านการตรวจสอบบัญชีโทเค็นความปลอดภัยและความลับที่เกี่ยวข้อง

โดยเฉพาะอย่างยิ่งความลับควรใช้ในการตั้งค่าตัวสร้างตัวเลขสุ่มหลอกเริ่มต้นในโมดูลสุ่มซึ่งถูกออกแบบมาสำหรับการสร้างแบบจำลองและการจำลองไม่ใช่ความปลอดภัยหรือการเข้ารหัส

ดูเพิ่มเติมเกี่ยวกับสิ่งที่มีให้ฉันพบฟังก์ชั่นที่มีประโยชน์มากถ้าคุณต้องการเลียนแบบ ID เช่น Google Drive ID:

secret.token_urlsafe ([nbytes = None])
ส่งคืนสตริงข้อความแบบ URL ที่ปลอดภัยซึ่งประกอบด้วย nbytes Random ไบต์ ข้อความที่เข้ารหัส Base64 ดังนั้นโดยเฉลี่ยผลแต่ละไบต์ประมาณ1.3 ตัวอักษร หาก nbytes เป็นไม่มีหรือไม่ได้ให้มาจะใช้ค่าเริ่มต้นที่เหมาะสม

ใช้วิธีต่อไปนี้:

import secrets
import math

def id_generator():
    id = secrets.token_urlsafe(math.floor(32 / 1.3))
    return id

print(id_generator())

ส่งออก id ความยาว 32 ตัวอักษร:

joXR8dYbBDAHpVs5ci6iD-oIgPhkeQFk

ฉันรู้ว่าสิ่งนี้แตกต่างจากคำถามของ OP เล็กน้อย แต่ฉันคาดหวังว่ามันจะยังคงมีประโยชน์สำหรับคนจำนวนมากที่กำลังมองหากรณีการใช้งานแบบเดียวกับที่ฉันกำลังมองหา


-1
import string, random
lower = string.ascii_lowercase
upper = string.ascii_uppercase
digits = string.digits
special = '!"£$%^&*.,@#/?'

def rand_pass(l=4, u=4, d=4, s=4):
    p = []
    [p.append(random.choice(lower)) for x in range(l)]
    [p.append(random.choice(upper)) for x in range(u)]
    [p.append(random.choice(digits)) for x in range(d)]
    [p.append(random.choice(special)) for x in range(s)]
    random.shuffle(p)
    return "".join(p)

print(rand_pass())
# @5U,@A4yIZvnp%51

-2

ฉันพบว่าสิ่งนี้ง่ายกว่าและสะอาดกว่า

str_Key           = ""
str_FullKey       = "" 
str_CharacterPool = "01234ABCDEFfghij~>()"
for int_I in range(64): 
    str_Key = random.choice(str_CharacterPool) 
    str_FullKey = str_FullKey + str_Key 

เพียงแค่เปลี่ยน 64 เพื่อเปลี่ยนความยาวเปลี่ยน CharacterPool เพื่อทำอัลฟ่าเพียงตัวอักษรตัวเลขหรือตัวเลขเท่านั้นหรือตัวละครแปลก ๆ หรืออะไรก็ตามที่คุณต้องการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.