0

I've written a short python script that generates a six character random output. It generates outputs, but most of them have unrecognisable characters, which come out as little boxes with four characters in them. Is there some way to strip these? If not, can I make them readable/recognisable? Thanks.

Script

import os
randomString = os.urandom(6)
print(randomString)

Output of locale

LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=en_US.UTF-8
LC_TIME=en_US.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=en_US.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=en_US.UTF-8
LC_NAME=en_US.UTF-8
LC_ADDRESS=en_US.UTF-8
LC_TELEPHONE=en_US.UTF-8
LC_MEASUREMENT=en_US.UTF-8
LC_IDENTIFICATION=en_US.UTF-8
LC_ALL=
5
  • Can't you simply use an alphabet of readable characters, so that the generated output of 6 random characters would always be naturally readable?
    – janos
    Jan 8, 2017 at 17:02
  • It would be helpful if you share the python script with us an the output of locale. Add both to your question.
    – Thomas
    Jan 8, 2017 at 17:02
  • I could, but, I'm just wondering if it's possible to get it to work with something like os.urandom()
    – Null_Error
    Jan 8, 2017 at 17:03
  • Have you seen the suggestions here How can I convert a python urandom to a string? Jan 8, 2017 at 17:57
  • 2
    Voting to close as off-topic as this is a programming question.
    – fkraiem
    Jan 8, 2017 at 18:04

2 Answers 2

3

To generate a random alphanumeric string using the system's RNG (/dev/urandom) in Python, you should probably better use random.SystemRandom:

#!/usr/bin/env python3
import random, string

RNG = random.SystemRandom()                         # Random Number Generator
characters = string.ascii_letters + string.digits   # allowed characters

# build a string by choosing a random character from `characters` 6 times:
s = "".join(RNG.choice(characters) for n in range(6))

print(s)

The method above allows you to exactly specify which characters are allowed and guarantees that your output string has exactly the desired length.

I decided for string.ascii_letters + string.digits in this example, which results in abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.


However, you could also use your own small script (or directly read 6 bytes from /dev/urandom using the command head -c 6 /dev/urandom) and filter the output to only show alphanumeric characters using tr:

python3 your_script.py | tr -cd '[:alnum:]' ; echo
head -c 6 /dev/urandom | tr -cd '[:alnum:]' ; echo

The tr command deletes (-d) all characters that are not (-c = complement) in the specified character set '[:alnum:]' which is a special shorthand for all alphanumeric characters.

The echo in the end simply produces a line break.

The disadvantage of this is that your output string has an undefined length, because you can't know how many valid characters there will be in the output.

However, if you directly read from /dev/urandom without Python, you can invert the pipe order like below to keep filtering random bytes until the output has the desired length:

tr -cd '[:alnum:]' < /dev/urandom | head -c 6 ; echo

Alternatively, you can also turn any binary data into readable characters by simply encoding it in base64, which is a character set of a-z, A-Z, 0-9, / and +:

python3 your_script.py | base64
head -c 6 /dev/urandom | base64

Note that the length of a base64 encoded string is always bigger than the length of the original data in bytes. The base64 output will also always end with ==.

0

You need to read about 8 times more bytes than the desired number of letters and digits, then remove those bytes which do not encode letters or digits.

The bizzare square characters with codes inside appear because your locale is set to UTF-8, but you are reading arbitrary bytes from /dev/urandom which (of course) do not respect the rules of UTF-8 encoding.

I don't do Python (but see the edit at the end of this answer for possibly ugly Python code), but the goal is very easy to achieve with a simple shell script. To generate random passwords containing letters and digits, you can use something like:

dd if=/dev/urandom bs=512 count=1 2>/dev/null |
tr -cd a-zA-Z0-9 |
cut -c 1-16 ; echo

From 512 bytes you can extract on the average 124 letters and digits. (There are 62 = 2 * 26 + 10 letters and digits in the 256 different bytes which are generated with the same probability.)

You may want to change a-zA-Z0-9 to remove characters which look too much alike. You may also want to change 16 if you want longer or shorter passwords.

For example, generate a 20 character password eliminating characters which may be confused (0O, 1Il):

$ dd if=/dev/urandom bs=512 count=1 2>/dev/null |
> tr -cd a-km-zA-HJ-NP-Z2-9 |
> cut -c 1-20 ; echo
JaQ2chDci4xVvzZuyGJm

(This is a transcript, the $ and > are shell prompts.)

Edit: This is the result of my humble Python skills:

#! /usr/bin/env python
import os
import re
print(re.sub("[^a-zA-Z0-9]", "", os.urandom(256))[:6])

Not the answer you're looking for? Browse other questions tagged .