Dig Deeper
ASCII values
Ascii
def rotate(text, key):
result = ""
for letter in text:
if letter.isalpha():
if letter.isupper():
result += chr((ord(letter) - 65 + key) % 26 + 65)
else:
result += chr((ord(letter) - 97 + key) % 26 + 97)
else:
result += letter
return result
This approach uses ascii values.
ASCII stands for American Standard Code for Information Interchange.
It is a 7-bit character encoding standard for electronic communication first described in 1969, becoming a formal standard in 2015.
It uses numbers to represent 128 different entities including carriage returns, whitespace characters, box characters, alphabetic characters, punctuation, and the numbers 0-9.
In ascii, all the lowercase English letters appear between 97 and 123.
While the uppercase letters are in the range between 65 and 91.
This approach only supports the English alphabet.
Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
For example, the Scandinavian letter **å** has the extended ascii value of **134**, but is used in combination with Latin-1 characters that appear in the 65-91 and 97-123 ranges.
This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
The approach starts with defining the function rotate(), with a variable result is assigned to an empty string.
The elements of the text argument are then iterated over using a for loop.
Each element is checked to see if it is a letter, and if it is a lower or uppercase letter.
Python has a builtin function called ord that converts a unicode symbol to an integer representation.
Unicode’s first 128 code points have the same numbers as their ascii counterparts.
If the element is an uppercase letter, ord is used to convert the letter to an integer.
The integer is added to the numeric key and then 65 is subtracted from the total.
Finally, the result is modulo (%) 26 to put the value within the 0-26 range, and 65 is added back.
This is because we want to know which letter of the alphabet the number will become.
If the new number is over 26 we want to make sure that it “wraps around” to remain in the range of 0-26.
To properly use modulo for a range we have to make sure that it starts at zero, so we subtract 65.
To get back to a letter in the ascii range we add 65 and use the chr method to convert the value to a letter.
The process is the same for a lowercase letter, but with 97 subtracted/added to put the letter in the lowercase ascii range of 97 - 123.
Any element that is not a letter (whitespace or punctuation) is added directly to the result string.
When all the elements have been looped over, the full result is returned.
Alphabet
Alphabet
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
def rotate(text, key):
result = ""
for letter in text:
if letter.isalpha():
if letter.isupper():
result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper()
else:
result += AlPHABET[(AlPHABET.index(letter) + key) % 26]
else:
result += letter
return result
The approach starts with defining the constant ALPHABET which is a string of all lowercase letters.
The function rotate() is then declared, and a variable result is defined as an empty string.
The text argument is then iterated over via a for loop.
Each element is checked to make sure it is a letter, and subsequently checked if it is uppercase or lowercase.
Uppercase letters are converted to lowercase.
Then the index of each letter is found in the AlPHABET constant.
The numeric key value is added to the letter index and modulo (%) 26 is used on the result.
Finally, the new number is used as an index into the AlPHABET constant, and the resulting letter is converted back to uppercase.
Lowercase letters follow the same process without the conversion steps.
If the element is not a letter (for example, space or punctuation) then it is added directly to the result string.
The result string is returned once the loop finishes.
If only English letters are needed, the constant string.ascii_lowercase can be imported from the string module.
from string import ascii_lowercase
AlPHABET = ascii_lowercase
Str Translate
Str Translate
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
def rotate(text, key):
translator = AlPHABET[key:] + AlPHABET[:key]
return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
This approach uses the <str>.translate method.
translate takes a translation table as an argument.
To create a translation table we use str.makestrans.
This approach starts with defining a constant of all the lowercase letters in the alphabet.
Then the function rotate() is declared.
A translator variable defined with the value of the AlPHABET constant sliced from the key to the end and then sliced from the start to the key.
This is done so we have 2 strings which are the same but shifted by the key value.
Say we have the AlPHABET constant with the value of abcdefghijklmnopqrstuvwxyz and the key is 3.
Then the translator variable will have the value of defghijklmnopqrstuvwxyzabc.
str.translate is then called on the text argument.
str.translate takes a translation table mapping start values to transformed values as an argument.
To create a translation table, str.makestrans is used.
makestrans takes 2 arguments: the first is the string to be translated, and the second is the string the first argument should be translated to.
For our solution, the first argument is the AlPHABET constant + the AlPHABET constant in uppercase.
The second argument is the translator variable + uppercase translator variable.
str.makestrans takes the Unicode values of the first argument and maps them to the corresponding Unicode values in the second argument, creating a dict.
>>> str.maketrans("abc", "def")
{97: 100, 98: 101, 99: 102}
str.translate takes the dict created by str.makestrans and uses it to translate the characters in the text argument.
>>> "abc".translate({97: 100, 98: 101, 99: 102})
'def'
Once the str.translate loop completes, we return the result.
Source: Exercism python/rotational-cipher