Iterating a Python Cipher from Scratch
Getting started in scripting doesn’t have to be scary
Earlier this week, I was working on a project that required a little bit of python scripting. While not anything too complicated, it reminded me that I needed to stay fresh with my programming, and so, as a personal challenge, I decided afterwards to create a python script from scratch with as little internet-referencing as possible. In order to show how we can iterate a python script from something very simple into something gradually more complex, I decided to share my process below. Of course, I’m not going to include any of the times I got stuck on a syntax error or anything like that — those things happen and you shouldn’t feel bad about it, but there’s no benefit to posting about it here (at the moment, anyway).
My python project is a simple Caesar Cipher. A Caesar cipher simply shifts the letters on the alphabet down by a predetermined number. (Refer to this Wikipedia page for an explanation). While extremely easy to crack, it’s a basic demonstration of encryption that I thought would work well for a simple exercise.
I also wanted to demonstrate how python shouldn’t be that scary for scripting newcomers — it’s fairly user-friendly language. In fact, everything I know about python scripting is self-taught: about a year ago, I just decided to sit down and learn it (I used CodeAcademy for some of my learning— they’re a great resource, but be aware that most of their courses use python2, which is outdated, although it still provides a good foundation regardless).
To start with, I needed to create a script (titled “SimpleCipher.py”), tell the terminal (or whatever application will be executing it) to interpret the script with python3, and accept a user input (this will be our phrase that needs to be encrypted).
Note that, for every picture of the python script, I’m also going to try to include a shot of that same script getting executed in order to demonstrate how (or if) it works. This method can be time consuming, but it is one way to ensure that you’re not introducing errors as you go along.
If you think about how a Caesar cipher works, every character of the phrase will need to be shifted. In order to do that, we need a way to interact with each individual character in a string. For that, we’ll use a for loop that works through each character in the phrase.
In the script below, we’re moving through each character in the string, starting with the first character (remember that all python indexes start with 0, not 1) and going through to the last one, based on the length of the phrase. I’ll print each result, to show that we’ve singled out each character before moving through the loop again. (I used i to represent each character in the string, not sure why, let’s roll with it)
Now that we’ve verified that each character is getting interacted with individually, let’s start assembling the rest of the script. After each letter is shifted, we’re going to want to add the characters together to create a new phrase that will get output as a single, encrypted string. Let’s set that part up without actually doing the shifting yet, just to verify that we’re getting one string and not a series of characters again. (You can ignore the AllofIt variable at the moment — I got ahead of myself and made a string index of every letter in the alphabet. We’ll use this later, but not yet)
As you can see above, we’re still interacting with each character individually because our initial code is still there, but it eventually gets reassembled into a new phrase. Since we’re not shifting (or encrypting) the individual characters yet, the returned phrase is identical to the input phrase.
Let’s start encrypting! To begin, we’re going to keep things simple and merely shift every letter down the alphabet by a factor of one. (So, a=b, b=c, etc.). I created a string that contains every letter in the alphabet, which won’t be visible to the user putting in the phrase, but we’ll be using it “under the hood” to perform the letter shifting.
The function .index() looks for the specified character in the string AllofIt and the +1 in our newchar variable moves the value referencing the indexed letter down by one. At the end of the loop, this new character is added to the final string.
The result is a phrase where every letter is moved down by one.
But wait! This works for most letters, but what happens if we put a “z” in our initial phrase?
As you can see, this breaks our code. There is no letter after z in our AllofIt string.
There are a number of ways we can circumvent this problem. For instance, we can use the try and except commands within our for loop. The try command includes our initial code that shifts each character down by one. If this doesn’t work, as in the case of the z character, it follows the new except code, which tells the character to move back to the start of the AllofIt phrase (so, z = a).
Our code is no longer broken, and any phrase containing a z character now shifts correctly.
Let’s complicate this a bit, though — what happens if we try to use capital letters in our phrase?
There are no capital letters in our reference string, so the code doesn’t quite work as we’d like — instead, it takes the same action as it does with a z character: it goes back to the start of the string and selects the a character from AllofIt.
There are simply too many possible exceptions that may execute with this code. Maybe we should try a different strategy for dealing with the end of the alphabet.
We need to account for capital letters in our reference string. Of course, if we include capital letters after the lowercase letters in our reference string, a lowercase z will translate to a capital A. If we want lowercase letters to remain lowercase, we need to try something else.
It’s time to introduce the if…else statement.
Put simply, the if statement looks for a condition, and, if it’s true, it executes the following code. If it’s not true, it moves on to the next condition (else if or elif in python shorthand), and checks the condition of that.
As you can see, our code now accounts for lowercase and capital letters!
Let’s get even more complicated. We’re only shifting characters down by one letter, but what if we want to do even more? What if we want the user to decide how many letters to shift the characters in the phrase? How do we change our code to account for that?
One thing at a time!
First, let’s make the characters shift by two letters, rather than one. This simple change will actually affect our script quite a bit. We’re not letting the user choose the cipher amount just yet, but we are going to change our script to account for a variable. We’re just going to make it so our variable has to be a factor of two to start with — We’ll let the user decide the cipher amount later, once we’ve verified that our code works for an amount larger than one.
Our formula now accounts for shifting the characters by ciphr rather than by +1 (ciphr just happens to equal two in this ).
The reason why the two variations of z include the formula ciphr -1 is because we need to account for the fact that we already shifted the character by a factor of one when we told it start back on the appropriate a character.
Uh oh! It looks like our formula mostly works, but now any capital Y character breaks the code, much like z did at the start of this exercise. We know an easy way to fix it (with the if…else statement), but we want this script to eventually use a variety of cipher amounts, and we don’t want to have to manually account for every possible variation. That would take way too long and complicate our script quite a bit!
Instead, we’re going to introduce a little bit of python math to our existing script.
In python, the percent sign (%) is known as the “modulus.” When used in mathematical functions, the percent sign returns the remainder of a division function. For example, 14%8 returns a value of 6, which is the remainder. The result of a modulus will always be a whole number.
Our string AllofIt includes 52 characters. If we use the function %52 after the ciphr number, it will always return a whole number that conveniently starts over again down the line whenever a number larger than 52 is used.
We can see that this works with a ciphr value of two, but will this work if we use other values?
As it turns out, it does!
In fact, this one simple mathematical function servers our purpose so well, we don’t even need the if…else statement anymore. Any number larger than 52 just gets factored down to a new number at the start of the string. So, z won’t break the code, just as y won’t break the code when use a ciphr value higher than two. Let’s comment out the if…else sections and make sure that this is true.
We do have a new problem, however: We’re back to capital letters being converted into lowercase letters and vice versa. The code doesn’t “break,” but we no longer have a variable telling the character string to split down the middle.
The easiest way to fix this is to have two different reference strings — one for capital letters and one for lowercase letters. For this to work, however, we have to introduce a new if…else statement — this time to check if each character in the inputted phrase is a lowercase or capital. We do this with the functions .isupper() and .islower() which return true or false values, depending on the context of the character input.
Additionally, since each reference string is now only 26 characters, we need our mathematical formula to represent that now as well.
It’s time to finish up this python script. Let’s change the ciphr variable to be dependent on user input. This way, we can shift (or, encrypt) our phrase differently depending on whatever number the user decides.
Once again, it works, but now we can change our shifting amount on the fly, without any errors. Let’s make one last change:
Now, the user has total control over the cipher — they put in a phrase, the amount they want to shift the letters by, and voila! We have encrypted text. The final addition is mostly a cosmetic change — if the user puts in a symbol or number, rather than breaking the code, the script returns a “sorry” message and exits. We could, of course, script a way to factor in unusual characters, but I think we’ve basically accomplished what we set out to do already.
And, with that, we’ve now successfully iterated a python script until we got a fully-functioning Caesar Cipher.
I hope that demonstrates how we can develop a python script from start to finish using just a little bit of trial and error. A lot of scripting can come down to simply trying to think of better, more effective ways to accomplish a task, and you don’t need to be intimidated by the end result. After all, our script merely started with a user input and a print command.