I used to enjoy cryptograms a lot and wanted to generate some on my own. I originally wrote a script to do this in Perl. While learning Python, I rewrote it and learned a few things about the string library in the process. Here goes...
import sys, string, random
We're going to be using sys to catch arguments passed to our script, or to catch input from a pipe. We've got lots of string manipulation so we're importing the string module. The random module will give us a way to shuffle a list so we can match each letter with another random letter. More on all of these later in the script.
We'll jump to the bottom of the script and show something you'll see a lot in Python:
if __name__ == '__main__':
if len(sys.argv) == 2:
print encrypt(open(sys.argv[1], 'r'))
else:
print encrypt(sys.stdin)
The first line here tells Python to call the encrypt function if the name of the script is __main__; more technically, if the name of the module is __main__. Every Python script can be a module. If the module is imported from another Python script, the module's __name__ is set to the filename without the directory path or file extension. If the script is run directly from the command line as a standalone program, __name__ is set to __main__. In this way, Python makes it easy to make programs reusable, or debug parts of a module before integrating it into a larger program.
The rest of this segment checks the number of arguments passed in. The first argument is always the script name itself. If there is a 2nd argument, we open the file and pass the file object to the encrypt function. If there isn't a 2nd argument, we open sys.stdin which is intended to let this script run by piping the output of another program to this program. This allows for running it using fortune: fortune | ./cryptogram.py
1: def encrypt(file):
2: alphabet = string.uppercase
3: text = ''
4:
5: for line in file.readlines():
6: text += string.upper(line)
7:
8: mixed = list(string.uppercase)
9: random.shuffle(mixed)
10: mixed = string.join(mixed,'')
11:
12: return string.translate(text,string.maketrans(mixed,alphabet))
This segment defines a function encrypt and takes one parameter, a file object. Line 2 sets up our alphabet, which is preset in the string library, along with many others. Lines 5 and 6 read the file in one line at a time, converts it to all uppercase, and appends each line onto the text variable.
Now we get to the meat. Lines 8 through 10 set up a string of letters consisting of the alphabet in random order. Line 8 uses the uppercase variable of the string module like we used before, but puts each letter into a list. The shuffle method of the random module expects a list so we pass 'mixed' to that. It performs an in-place shuffle. Line 10 we convert the list of letters back into a string.
Line 12 calls the translate function. This would be similar to Perl's tr/// call. The first argument is the text file we want to translate. The second argument is the translation table, which is a 256 byte string of the mapping that needs to be done. The maketrans function helps us build that string and both arguments to maketrans need to be the same length.
With that, I'll leave you with the full script, and the output on a quote. Here's a hint, it's a Monty Python quote.
#!/usr/bin/env python
# cryptogram.py
import sys, string, random
def encrypt(file):
alphabet = string.uppercase
text = ''
for line in file.readlines():
text += string.upper(line)
mixed = list(string.uppercase) # list of letters in alphabet
random.shuffle(mixed) # randomize it
mixed = string.join(mixed,'') # convert back to string
return string.translate(text,string.maketrans(mixed,alphabet))
if __name__ == '__main__':
if len(sys.argv) == 2:
print encrypt(open(sys.argv[1], 'r'))
else:
print encrypt(sys.stdin)
Output...
R GTRAN GTDG DSS OYYI, BROTG GTRANRAO CMYCSM RA
GTRW ZYVAGBL DBM WRZN DAI GRBMI YF EMRAO GYSI
GTDG DSS OYYI, BROTG GTRANRAO CMYCSM RA GTRW
ZYVAGBL DBM FMI VC URGT EMRAO GYSI GTDG DSS OYYI,
BROTG GTRANRAO CMYCSM RA GTRW ZYVAGBL DBM FMI
VC URGT EMRAO WRZN DAI GRBMI. R'J ZMBGDRASL AYG,
DAI R'J WRZN DAI GRBMI YF EMRAO GYSI GTDG R DJ!
-- JYAGL CLGTYA