'Caesar Cipher in Python: remove spaces in a list

I'm working on a Caesar Cypher project. I take the user's input, turn it into a list, take out spaces, and then encrypt the letters.

My question is: How do I re-add these spaces into the final encrypted message?

Here's what I have achieved so far (pretend word = message)

alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

en_de = input("Do you want to encrypt or decrypt your message? ")
word = input("Enter a word: ")
shift = input("Enter a number: ")

word = list(word)

indexes = []

def encrypt():
  for letters in word:
    if letters in alphabet:
      index = (alphabet.index(letters))
      int(index)
      indexes.append(index)
  print(indexes)
  n = 0
  n = n + int(shift)
  for i in range(len(indexes)):
   indexes[i] = indexes[i] + n
  print(indexes)
  ceaser_cipher = ''
  for i in indexes:
    if i > len(alphabet)-1:
      i %= len(alphabet)
    ceaser_cipher = ceaser_cipher + (alphabet[i])
  for 
  print(ceaser_cipher)

def decrypt():
  for letters in word:
    index = (alphabet.index(letters))
    int(index)
    indexes.append(index)
  print(indexes)
  n = 0
  n = n + int(shift)
  for i in range(len(indexes)):
   indexes[i] = indexes[i] - n
  print(indexes)
  ceaser_cipher = ''
  for i in indexes:
    if i > len(alphabet)-1:
      i %= len(alphabet)
    ceaser_cipher = ceaser_cipher + (alphabet[i])
  print(ceaser_cipher)  

if en_de == "encrypt":
  encrypt()
elif en_de == "decrypt":
  decrypt()


Solution 1:[1]

The structure I would go for, is like this:

input_string = input("Enter message or cipher: ")
input_list = input_string.split(" ")
# input_list will be a list of each word in input_string
# encrypt each word in input_list and append each encrypted word 
# to a new list, let's say that list is called output_list
output = " ".join(output_list)
# output will be a string of each item in the list separated by spaces

The final program could look something like this:

alphabet = list("abcdefghijklmnopqrstuvwxyz")
message = input("message or cipher: ").lower().split(" ")
shift = int(input("shift: "))
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
cipher_list = []
for word in message:
    cipher_word = ""
    for letter in word:
        cipher_word += shifted_alphabet[alphabet.index(letter)]
    cipher_list.append(cipher_word)
print(" ".join(cipher_list))

Solution 2:[2]

You are in the right path in your learning, and @Toby's answer is a good contribution for you.

I'd like to just tease you here and let you know that the whole "Caesar Cipher" can be fully implemented in a one-liner, once you get familiar with many other Python features and syntax.

Check this:

''.join(chr(a + (i - a + shift) % 26) if (a := 65) <= (i := ord(c)) <= 90 or (a := 97) <= i <= 122 else c for c in word)

Examples:

cipher = lambda w, s: ''.join(chr(a + (i - a + s) % 26) if (a := 65) <= (i := ord(c)) <= 90 or (a := 97) <= i <= 122 else c for c in w)

word = 'Hello, beautiful World!'
print(cipher(word, 4)) # positive shift
# Lipps, fieyxmjyp Asvph!

word = 'Lipps, fieyxmjyp Asvph!'
print(cipher(word, -4)) # -4 shift means decipher a +4 shift
# Hello, beautiful World!

Explanation:

  • ''.join(seq) takes a sequence of strings seq and concatenate them together into one string;
  • ord(c) gives index of the character c in the Unicode table. For example, ord('a') returns the integer 97 and ord('A') returns the integer 65;
  • chr(i) is the inverse of ord: It gives the character at index i in the Unicode table. For example, chr(122) returns the string 'z' and chr(90) returns the string 'Z';
  • ... for c in word is a generator expression. Is a nice compact and efficient idiom for creating a sequence from another sequence. In this case, I am taking every character c in the string word, performing an operation, and creating a new sequence with each resulting element.
  • ... if ... else ... is the ternary operator. It is a shortcut to embed a condition in an expression. Here, if the character index is not between 65 and 90 (uppercase letters) or between 97 and 122 (lowercase letters), I am returning the same character c untouched. That makes any non-letter not get transformed by the cipher (like space, comma, or exclamation mark);
  • x <= y <= z is a chained comparisson, it is a nice idiom available in python that resembles the Math conditional expression.
  • (a := 65) and (i := ord(c)) make use of the walrus operator :=, aka "assignment expression". With this, you can assign a variable at the same time you use it in an expression, and this variable will be available for use in the enclosed context after the declaration. For example, i is assigned in the left side of the or operator, and is used in the right side. Also, a is assigned in the "test" part of the conditional and used in the "expression" part.
  • chr(a + (i - a + shift) % 26) is performing a modulo operation with the python's operator %, where 26 is the count of letters from A to Z. That means, if your shift makes your letter overflow the alphabet, it circles back to the beginning/end. For example, 'B' with a shift of -4 gives 'X', and 'x' with a shift of +30 gives 'b' (because 30 = 26 + 4);

It is a rather contrived implementation, but hopefully you got to know some new stuff today on python!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dharman
Solution 2