Issue with Caesar Cipher and while loops - python

I'm making this Caesar Cipher decoder and I want the program to print every single option (the 26 ways it could be shifted). However, when I run my code nothing shows, what was my error. If you know please tell me, I'm new to coding and in need of help.
import sys
import time
L2I = dict(zip("ABCDEFGHIJKLMNOPQRSTUVWXYZ",range(26)))
I2L = dict(zip(range(26),"ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
msg = ("What is the intercepted message \n")
for character in msg:
sys.stdout.write(character)
sys.stdout.flush()
time.sleep(0.1)
msg_ans = input("> ")
msg_ans = msg_ans.strip()
shift = 0
def decipher(msg_ans,shift):
while shift < 26:
for i in msg_ans.upper():
if i.isalpha() == True :
msg_ans += I2L[ (L2I[i]+ shift)%26 ]
shift += 1
else:
msg_ans += i
shift += 1
print (msg_ans)
decipher(msg_ans,shift)
I expect it to output the 26 ways it can be shifted. However when I put the word 'Hello' I get 'HelloHFNOSMKSTXRQZBGWUCDHBAJLQLKTVAVVFIO' instead of 'IFMMP JGNNQ ...'

There are a couple of issues. First, you're incrementing shift every time you check a single character. In reality, you only want to increment it after each time you cycle completely through the message. You should also move the initialization into the function. There's no reason to pass shift in, since you're just trying all 26 possibilities in order.
def decipher(msg_ans):
shift = 0
while shift < 26:
for i in msg_ans.upper():
if i.isalpha() == True :
msg_ans += I2L[ (L2I[i]+ shift)%26 ]
else:
msg_ans += i
shift += 1
print (msg_ans)
At this point, though, there's no reason to use a while loop instead of a for:
def decipher(msg_ans):
for shift in range(26):
for i in msg_ans.upper():
if i.isalpha() == True :
msg_ans += I2L[ (L2I[i]+ shift)%26 ]
else:
msg_ans += i
print (msg_ans)
The other issue is that you're just appending the new characters to the end of your input string. You don't specify what form you actually want it in, so let's say you want it in a list of strings. You'll need to initialize the list, build a temporary string on each iteration, and then append the temporary string to the list:
def decipher(msg_ans):
possible_messages = []
for shift in range(26):
decoded_msg = ''
for i in msg_ans.upper():
if i.isalpha() == True :
decoded_msg += I2L[ (L2I[i]+ shift)%26 ]
else:
decoded_msg += i
possible_messages.append(decoded_msg)
return possible_messages
Then just print the result of your invocation of the function:
print(decipher(msg_ans))

msg should be like this
msg = "What is the intercepted message \n"
Also you probably want to print instead of return here
return msg_ans

Related

How to group consecutive letters in a string in Python?

For example: string = aaaacccc, then I need the output to be 4a4c. Is there a way to do this without using any advanced methods, such as libraries or functions?
Also, if someone knows how to do the reverse: turning "4a4c: into aaaacccc, that would be great to know.
This will do the work in one iteration
Keep two temp variable one for current character, another for count of that character and one variable for the result.
Just iterate through the string and keep increasing the count if it matches with the previous one.
If it doesn't then update the result with count and value of character and update the character and count.
At last add the last character and the count to the result. Done!
input_str = "aaaacccc"
if input_str.isalpha():
current_str = input_str[0]
count = 0
final_string = ""
for i in input_str:
if i==current_str:
count+=1
else:
final_string+=str(count)+current_str
current_str = i
count = 1
final_string+=str(count)+current_str
print (final_string)
Another solution and I included even a patchwork reverse operation like you mentioned in your post. Both run in O(n) and are fairly simple to understand. The encode is basically identical one posted by Akanasha, he was just a bit faster in posting his answer while i was writing the decode().
def encode(x):
if not x.isalpha():
raise ValueError()
output = ""
current_l = x[0]
counter = 0
for pos in x:
if current_l != pos:
output += str(counter) + current_l
counter = 1
current_l = pos
else:
counter += 1
return output + str(counter) + current_l
def decode(x):
output = ""
i = 0
while i < len(x):
if x[i].isnumeric():
n = i + 1
while x[n].isnumeric():
n += 1
output += int(x[i:n])*x[n]
i = n
i += 1
return output
test = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaasasggggggbbbbdd"
test1 = encode(test)
print(test1)
test2 = decode(test1)
print(test2)
print(test == test2)
yes, you do not need any libraries:
list1 = list("aaaacccc")
letters = []
for i in list1:
if i not in letters:
letters.append(i)
string = ""
for i in letters:
string += str(list1.count(i))
string+=str(i)
print(string)
Basically, it loops through the list, finds the unique letters and then prints the count with the letter itself. Reversing would be the same function, just print the amount.

Upper case characters every other character but ignoring symbols and spaces

I'm a novice working on a practice script of which I have 90% figured out but am stumped on one portion. I'm doing the mocking spongebob "challenge" on dmoj which asks you to make every other character of a given string upper case, but demands symbols and spaces to be ignored and only letters to be counted.
I and am able to make every other character upper case or lower case, but I am not sure how to ignore symbols and spaces? I'll drop what I have so far for critique. Thank you for your time.
meme1 = "I don't even know her like that"
meme2 = "You can't just make a new meme from a different Spongebob clip every
couple of months"
meme3 = "I must have listened to that latest Ed Sheeran song 10000 times!"
memeFIN1 = [""] * len(meme1)
memeFIN2 = [""] * len(meme2)
memeFIN3 = [""] * len(meme3)
memeFIN1[1::2] = meme1[1::2].upper()
memeFIN2[1::2] = meme2[1::2].upper()
memeFIN3[1::2] = meme3[1::2].upper()
memeFIN1[::2] = meme1[::2].lower()
memeFIN2[::2] = meme2[::2].lower()
memeFIN3[::2] = meme3[::2].lower()
memeFIN1 = "".join(memeFIN1)
memeFIN2 = "".join(memeFIN2)
memeFIN3 = "".join(memeFIN3)
print(memeFIN1)
print(memeFIN2)
print(memeFIN3)
EDIT:
All of the following solutions in the answers helped me come to my own, but none of them seemed to work entirely on their own. In cheesits solution, changing counter to start at 1 works, but not realizing that (as an utter noob) I did counters differently, essentially doing the same thing. This is the solution that worked for me:
meme1 = "I don't even know her like that"
meme2 = "You can't just make a new meme from a different Spongebob clip every couple of months"
meme3 = "I must have listened to that latest Ed Sheeran song 10000 times!"
def spongebobify(meme):
count = 0
char = []
for ch in meme:
if ch.isalpha() and count % 2 == 1:
char.append(ch.upper())
count += 1
elif ch.isalpha():
char.append(ch.lower())
count += 1
else:
char.append(ch)
return ''.join(char)
m1 = spongebobify(meme1)
m2 = spongebobify(meme2)
m3 = spongebobify(meme3)
print (m1)
print (m2)
print (m3)
If you want something readable, try this:
def spongebobify(phrase):
## Turn every character lowercase
phrase = phrase.lower()
## Keep track of how many letters you've seen
counter = 0
## Create a list to hold characters
chars = []
## Go through the entire string
for ch in phrase:
## If this is a letter, increment
if ch.isalpha():
counter += 1
## If this is a letter and our counter is odd:
if ch.isalpha() and counter % 2:
chars.append(ch.upper())
## Otherwise, just add it as-is
else:
chars.append(ch)
return ''.join(chars)
If you want a one liner (disregarding efficiency):
def spongebobify(phrase):
return ''.join([ch.lower() if ch.isalpha() and len([c for c in phrase[:i] if c.isalpha()]) % 2 else ch for i, ch in enumerate(phrase.upper())])
## Readable version:
#return ''.join([
# ch.lower()
# if ch.isalpha()
# and len([c for c in phrase[:i] if c.isalpha()]) % 2
# else ch
# for i, ch in enumerate(phrase.upper())
#])
you can use a bit to indicate which lower/upper you're currently looking at, toggling the bit whenever an alpha character is found.
def speak_like_spongebob(phrase):
case_bit = 0
res = []
for lower_upper in zip(phrase.lower(), phrase.upper()):
res.append(lower_upper[case_bit])
case_bit ^= lower_upper[0].isalpha()
return ''.join(res)
You need to use a loop, so you can increment a counter only when the character is a letter, and then check whether that counter is odd or even.
def mock_spongebob(input):
letters = 0
result = ""
for c in input:
if c.isalpha():
result += c.lower() if letters % 2 == 0 else c.upper()
letters += 1
else:
result += c
return result
As others have mentioned, you will need to iterate through each character in the strings, determine whether it's a character or a symbol, and act accordingly.
The following program would print every other character in uppercase, not counting non-alphabetic characters:
meme1 = "I don't even know her like that"
meme2 = "You can't just make a new meme from a different Spongebob clip every couple of months"
meme3 = "I must have listened to that latest Ed Sheeran song 10000 times!"
for meme in [meme1, meme2, meme3]:
count = 0
meme_mod = ""
for c in meme:
if not c.isalpha():
meme_mod += c
continue
elif count % 2 == 0:
meme_mod += c.lower()
else:
meme_mod += c.upper()
count += 1
print meme_mod
This program would output:
i DoN't EvEn KnOw HeR lIkE tHaT
yOu CaN't JuSt MaKe A nEw MeMe FrOm A dIfFeReNt SpOnGeBoB cLiP eVeRy CoUpLe Of MoNtHs
i MuSt HaVe LiStEnEd To ThAt LaTeSt Ed ShEeRaN sOnG 10000 tImEs!
Here's an article that shows how to write a SpongeBob Mocking Converter script in Python

How do you combine a number list with multiple outputs give by a while loop

just a quick note: the title may not be 100% accurate as i dont really know how to explain my problem.
Im creating a Caesar cipher which will repeat all 26 different variations, the problem here is that beside the outputted text i would like to number each variation but it doesnt work.
message = """SAHH""" # Add Code
message = message.upper()
keyShift = 1
encryptedMsg = ""
Waste = ' '
#running the program
while keyShift <= 26:
for character in message:
if character.isalpha() == True:
x = ord(character) - 65
x += keyShift
x = x % 26
encryptedMsg += chr(x + 65)
else:
encryptedMsg += character
for a in enumerate(encryptedMsg, 1):
print('{}'.format(a),encryptedMsg,"\n")
encryptedMsg = Waste
keyShift +=1
If you run this the out put will be Each letter of the first variation will have it's own number.
for a in enumerate(encryptedMsg, 1):
print('{}'.format(a),encryptedMsg,"\n")
The aim is as i said above each variation of the Caesar cipher should have a number and this code gives a number to each letter of the first variation.
If I understand your problem correctly, you need to create a dictionary of results instead of enumerating your message. I do not quite understand what you are trying to achieve with your later for loop, though.
Do you just want to create all variations of caesar with a different key shift and print them? If so, this will (probably) do it, assuming your code is correct:
message = """SAHH""" # Add Code
message = message.upper()
keyShift = 1
encryptedMsg = ""
Waste = ' '
results = {}
#running the program
while keyShift <= 26:
encryptedMsg = ""
for character in message:
if character.isalpha() is True:
x = ord(character) - 65
x += keyShift
x %= 26
encryptedMsg += chr(x + 65)
else:
encryptedMsg += character
results[keyShift] = encryptedMsg
keyShift =+ 1
for r in results.keys():
print r, results[r]
Or did you mean something else?
Hannu

Python : Exiting for loop?

I did some research on SO and am aware that many similar questions have been asked but I couldn't quite get my anwser. Anyway, I'm trying to build a library to "encrypt" a string with "Cesar's number" technique wich means I have to take the string and replace each letters with another letter X positions away in the alphabet (I hope that makes sense). Here's my code :
from string import ascii_lowercase, ascii_uppercase
def creer_encodeur_cesar(distance):
retour = lambda x: encodeur_cesar(x, distance)
return retour
def encodeur_cesar(string, distance):
tabLowerCase = list(ascii_lowercase)
tabUpperCase = list(ascii_uppercase)
tabString = list(string)
l_encodedStr = []
for char in tabString:
position = 0
if char == " ":
pass
elif char.isupper():
#do something
elif char.islower():
for ctl in range(0, len(tabLowerCase)):
position = ctl
if char == tabLowerCase[ctl]:
if (ctl + distance) > 26:
position = ctl + distance - 25
char = tabLowerCase[position + distance]
l_encodedStr.append(char)
#How to break out of here??
encodedStr = str(l_encodedStr)
return encodedStr
encodeur5 = creer_encodeur_cesar(5)
print(encodeur5("lionel"))
So, in my second elif statement, I want to break once I have succesfully found and encrypted a character instead of looping trough the whole alphabet. I have tried to use break but it broke out of the main for loop. Not what I want. I saw that I could use try exceptand raise but I don't quite know how I could do that and is it a good idea?
What's the best way to do this? What are the good practices in this case?
Any help would be appreciated and thanks in advance!
You can use the continue keyword.
From the docs:
>>> for num in range(2, 10):
... if num % 2 == 0:
... print "Found an even number", num
... continue
... print "Found a number", num
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9

caesar cipher brute force decryption

I am trying to write my own python code to brute force a caesar cipher and I need some help. I specifically need help near the end of my code of the function. I want to know how to print specifically so that after each key tried there is a gap. I am using python 3.3 and have just started coding 3 weeks ago.
print ("The plaintext will be stripped of any formatting such as spaces.")
freqlist = []
maxkey = 26
if key > maxkey:
raise Exception("Enter a key between 0 and 26: ")
elif key < 0:
raise Exception("Enter a key between 0 and 26: ")
freq = []
inpt=input("Please enter the cipher text:")
inpt = inpt.upper()
inpt = inpt.replace(" ", "")
inpt = inpt.replace(",", "")
inpt = inpt.replace(".", "")
for i in range(0,27):
key = i
def decrypt():
for i in range(0,27):
for a in inpt:
b=ord(a)
b-= i
if b > ord("Z"):
b -= 26
elif b < ord("A"):
b+=26
freqlist.append(b)
for a in freqlist:
d=chr(a)
freq.append(d)
freq="".join(freq)
print(freq.lower(),"\n")
decrypt()
I am trying to use a for loop and I don't think it is really working effectively.
Based on the error you posted, I think this should help.
In Python, you can have local and global variables of the same name. The freq in the function is local, and thus the initialization of the global freq doesn't touch the local one. To use the global freq, you have to tell the function to do so, via the global statement. This is explained a little more in the Python FAQs.
That should be enough to get you back on track.
EDIT:
Below is an edit of your decrypt function, with some explanations of the changes
def decrypt():
# we don't need the zero start value, that's the default
# test all possible shifts
for i in range(27):
# initialize the array
freqlist = []
# shift all the letters in the input
for a in inpt:
b = ord(a)
b -= i
if b > ord("Z"):
b -= 26
elif b < ord("A"):
b+=26
freqlist.append(b)
# now put the shifted letters back together
shifted = ""
for a in freqlist:
d = chr(a)
# append the shifted letter onto our output
shifted += d
# after we put the decrypted string back together, print it
# note this is outside the letter loops,
# but still inside the possible shifts loop
# thus printing all possible shifts for the given message
print(d)

Categories

Resources