Several ways to use ROT13 letter substitution cipher in Python 3

Image for post
Image for post
Photo by Alex Motoc on Unsplash

ROT13 (“rotate by 13 places”) is a simple letter substitution cipher, a special case of “Caesar cipher” and was once popular on Internet forums.

The rule is very simple: you replace each letters with the 13th letter behind them. If the range is over the last letter z, just loop back and continue to count from letter a. Since there are 26 letters in English, apply ROT13 twice (or 4, 6, 8 times…) will return the original text:

Although The Zen of Python said “There should be one — and preferably only one — obvious way to do it”, there are in fact many ways to implement this in Python 3. This simple article will explore some of these methods.

The straightforward way — — using ASCII index

Image for post
Image for post
Photo by Nick Hillier on Unsplash

The first method is to iterate the word using ord() to get the Unicode of each letter in a word (for a~z they are the same as ASCII code), calculate the new value and use chr() to convert it back to letters.

To simply things, we assume our function rot13() would only receive a single word which only consisted of a~z or A~z letters. ord(‘a’) gets 97, and chr(97) gets ‘a’. The result will be a lower case word no matter what.

print(rot13('python'))

The list comprehension would generates a list of converted letters, then use str.join() to combine them together into a single string without any separation characters.

So we submit a word “python” and will get the following output:

Let’s expand this a bit so it can process a longer sentence with multiple words:

print(rot13('python programming language!'))

If a letter does not have Unicode between 97 (‘a’) and 122 (‘z’), it would be added without changing.

This will get you

The Zen of Python way — — using dictionary

Image for post
Image for post
Photo by Kari Shea on Unsplash

Most Python learners probably know that you can read “The Zen of Python” by executing “import this” in REPL:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
...

Fewer people would also know that the source code of “this” is not so straightforward — — the text is entirely coded in ROT13 and then converted back to normal English:

s = """Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
..."""

d = {}
for c in (65, 97):
for i in range(26):
d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))

(So even The Zen of Python does something not beautiful, explicit and simple in Python. Funny, isn’t it?)

Let’s define a rot13() function based on the coding method above:

print(rot13('Python programming language!'))

Here the code first builds a dictionary d as a mapping table: d[‘A’]=‘N’, d[‘B’]=‘O’, d[‘C’]=’P’, and d[‘a’]=‘n’, d[‘b’]=‘o’, etc. Then it uses list comprehension to look up letters from it. (dict.get() returns the letter itself if it is not found in the dictionary; in other words, non-alphabet characters will be unchanged.)

Using string for index and mapping instead

Image for post
Image for post
Photo by Maxim Fiyavchuk on Unsplash

Here’s another way to convert letters by using string as mapping table instead of a dictionary (this is based on code found here). Just the same, in order to simplify things, this rot13() would only returns lower case results.

def rot13(sentence):
alphabet = string.ascii_lowercase
return ''.join([alphabet[(alphabet.find(letter) + 13) % 26]
if alphabet.find(letter) >= 0 else letter
for letter in sentence.lower()])
print(rot13('python programming language!'))

“string.ascii_lowercase” returns a string = ‘abcde…xyz’. We use str.find() to locate the index of each letter, calculate the new positions and get the new letters. If the letter is not found in the string (get index -1, hence not an alphabet), use the original letter instead.

Using str.maketrans() and str.translate()

Image for post
Image for post
Photo by Edurne Chopeitia on Unsplash

There’s yet another way to make a mapping table in Python. (This is based on code found here.)

print(rot13('python programming language!'))

The code above is only applicable for lower case sentences. If we want to use this for both upper and lower case letters and not having to input all the alphabets twice, we can modify the code like this:

def rot13(sentence):
alphabet_l = string.ascii_lowercase
alphabet_u = string.ascii_uppercase
rot13 = str.maketrans(
alphabet_l + alphabet_u,
alphabet_l[13:] + alphabet_l[:13] + \
alphabet_u[13:] + alphabet_u[:13]
)
return str.translate(sentence, rot13)
print(rot13('PYTHON Programming Language!'))

Now we have a better mapping table, and the result is

The fastest and easiest way — — using codecs

Image for post
Image for post
Photo by Harley-Davidson on Unsplash

For all the efforts above, the codecs module already supports ROT13 encoding, which is surprisingly simple:

def rot13(sentence):
return codecs.encode(sentence, 'rot_13')
print(rot13('PYTHON Programming Language'))

Now, let’s try to use codecs.encode() on Zen of Python’s attribute s, which contains the ROT13 version of the text. Here we temporarily block/overwrite Python’s print() so module “this” won’t print the result on import, and “decode” the ROT13 string:

sys.stdout = open(os.devnull, 'w')  # block print()
import this
sys.stdout = sys.__stdout__ # enable print()
import codecs
print(codecs.encode(this.s, 'rot_13'))

We will get the output

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
...

I’ll stop here and leave you to think about meanings of life, the universe and everything as well as why Python itself sometimes make things complicated.

Image for post
Image for post
Photo by Jared Rice on Unsplash

Former translator and currently a tech-book editor based in Taiwan. https://krantasblog.blogspot.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store