Lucky 13

A couple of days ago, David Sparks tweeted a link to a site that will convert any text message into the equivalent string of ones and zeros.

I just sent an email calling a friend a really bad name in binary and it was kind of fun. roubaixinteractive.com/PlayGround/Bin…
David Sparks (@MacSparky) Wed Feb 20 2013 7:23 PM CST

I immediately thought of doing the same thing as a TextExpander snippet. I managed to get the conversion both ways working, but I wasn’t entirely happy with the way it handled Unicode.1 And because the binary strings are so long, I wasn’t sure where I’d use them. I certainly wouldn’t be able use it in Twitter messages except for very short ones.

But as I was thinking about text conversions, I realized that a snippet for doing the classic ROT13 conversion would be fun, simple, and would lead to converted strings that were the same length as the originals. And I’d only need to write one snippet, because ROT13 is symmetric: the transformation that encodes also decodes. Even better, ROT13 is, by its very nature, ASCII only—no need to worry about Unicode.

I wanted to write my converter in Python, and there’s a Stack Overflow question on ROT13 in Python that has a very concise answer, but it had, I thought, an odd way of ordering the characters in the translation table. I changed that around but basically kept the same structure:

python:
 1:  #!/usr/bin/python
 2:  
 3:  from string import maketrans
 4:  from sys import stdin, stdout
 5:  
 6:  alpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
 7:  rot13 = 'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
 8:  r13table = maketrans(alpha, rot13)
 9:  
10:  orig = stdin.read()
11:  stdout.write(orig.translate(r13table))

The key lines are 8 and 11. The maketrans function from the string module creates a translation table which, when used as the argument to the translate method, converts every character in the first string to the character at the corresponding position in the second string. Characters that aren’t in the first string are passed through unchanged.

I saved the script as rot13 in my ~/bin folder and made it executable. Then I created a TextExpander snippet that applied the ROT13 conversion to the contents of the clipboard and printed it out. I gave the snippet the obvious abbreviation: ;rot13.

ROT13 TextExpander snippet

To use the snippet, I

  1. Select the text I want to convert.
  2. Cut it (⌘X).
  3. Type ;rot13.

Rnfl nf cvr.

One last note: Instead of writing a Python script for the conversion and a shell script in TextExpander that calls it, I could have put the whole thing in the TE script. I decided to split the two apart so I’d have a filter that I could use outside of TextExpander if I ever needed one. If I were a bigger fan of Services, I’d fire up Automator and make a service that applies this same filter.


  1. It worked on every string I tested it on, but I just wasn’t confident that it would always work. Unicode does that to me.