This lab, you're going to be implementing a very rudimentary cryptography program in python. Your program will have to generate a random substitution code for all the letters in the range a-z, mapping them to other letters in a-z such that the letters can be translated uniquely back to the original ones. All other characters will be passed through unchanged. Your program will use this code to translate a text file (which you supply, though it should work correctly regardless of the contents of the file) and then translate it back into the original form. The point is not so much to do world-class cryptography, but rather to give you some more experience working with objects and modules in python.
We are supplying you with a test script called
test_crypto
. Note: this will only work on a Unix system, so
please use a Unix system to develop this (or at least to test it).
You have to write two files:
char_translator.py This file will contain code to
generate the random substitution code and to translate and untranslate
individual characters based on that code. NOTE: python now contains
the string methods string.maketrans()
and
string.translate()
which pretty much do this for you. So don't
use either of these methods, or the problem becomes totally trivial.
crypto.py This file will do the actual translation and untranslation of files.
Here are the classes and methods that should be in each file. Feel free to add more classes or methods if it's convenient for you. In the file "char_translator.py", you should have:
A function called random_permute_chars
, which generates
and returns a string which is a random permutation of the characters in the
range a-z (all lowercase). The python functions
random.shuffle
and
string.join
will be useful to you; see the python library
documentation to learn how they work. To convert a string to a list, do
this:
l = map(lambda x:x, s)
where l
is the list and s
is the string. Or, more
simply, you can just do this:
l = list(s)
Note that the permutation must have no duplicates and must include all the letters from a-z (by definition of permutation). Note also that python strings can be indexed as if they were lists, and you can use
for c in s: # code using c
(where c
is a character and s
is a string) to iterate
through the characters.
A class called CharTranslator
, with methods:
__init__
, which takes a string which is a random
permutation of characters in the range a-z as its only argument and
stores it in the CharTranslator object. Make sure you check that the
string is a valid permutation.
translate_char
, which translates a single character
based on the permutation. In other words, to get the translation of a
character, find the corresponding character in the permuted string and
return that. You can leave uppercase characters alone if you like, or
translate them (making sure that the result is also uppercase). All
other characters should pass through unchanged.
untranslate_char
, which undoes the translation.
An exception class called InvalidTranslationString
which is raised if the translation string is invalid. This should be used in
the __init__
method for CharTranslator
.
In the file "crypto.py", you should have a class called Coder
which has these methods:
__init__
, which takes a CharTranslator object as its
argument and stores it in the Coder object.
encode_file
, which takes an input and output file name
and encodes the input file, storing the output in the output file.
decode_file
, which takes an input and output file name
and decodes the input file, storing the output in the output file.
Once you've developed your code, run the file
test_crypto
with some file name as its argument. If your
modules are correct the program will encode the file, decode it, and test to
see if the decoded version is the same as the encoded version. Try to make
sure that your test file has all the letters from a-z and A-Z as well as some
non-alphabetic characters. Program source code is a good input to use.
This is an easy lab, so I expect perfect programs ;-) Here are a few things to keep in mind while you write this program.
You may find the ord
and chr
functions to be
useful. They are built-in functions that convert characters (strings of
length 1) to ASCII codes and back. Use pydoc to find out more.
Use functions and methods from the string
and
list
modules (type "pydoc string
" and "pydoc
list
" at the shell prompt to find out more about these functions and
methods) to make your program more concise. In particular, the string
"abcde...z" is just string.ascii_lowercase
. Also check out the
join
and find
functions, and the index
and sort
methods on lists. Also note that you can do
comparisons on strings and lists, not just on numbers.
Some string functions can be used as methods e.g. the
string.join()
function, which can be written like this:
>>> "".join(["foo", "bar", "baz"]) 'foobarbaz'
This says to join the given list of strings with an empty string as the separator.
Make sure you close all files that you open using the
close()
method of file objects.
Assume that the input file can be arbitrarily large (e.g. one gigabyte). Because of this, you don't want to load the entire file into memory before you output anything, even if this would make your code a bit more concise or more clever-looking.
To iterate through the lines in a file you can use "for line in
file: ...
" instead of an infinite loop with a break
statement in the middle.
Since the files you need to write are modules, not executable
programs, you don't need the "#! /usr/bin/env python
" line at
the beginning and you don't need to do "chmod +x
" on the files.
However, the "test_crypto
" file I supply you with (note the lack
of the ".py
" suffix) is an executable file, so you should
do a "chmod +x
" on it after you download it.
When an error occurs in a class method, you should not print an
error message and call "sys.exit(1)
". You have no idea what
context the class instances will be used in, but normally they will be part
of a larger program (as they are in this case), and exiting when one thing
goes wrong may not be the right thing to do -- you just don't know. So
instead, you should raise an exception, preferably with an error message that
tells the code that called the method what went wrong. In some cases
(e.g. opening a non-existent file) an exception will be thrown from a
method you call that will contain the error message already; in that case,
you don't have to catch the exception and re-throw a different one (or, even
worse, the same one); just let it go through (this may come as a shock to
Java programmers, but rest assured, it's fine).