CS circles Coding Exercise: 26 Letters - python

This coding exercise is giving me a lot of trouble and I need help.
Here is the problem.
Write a program which does the reverse of the example above: it should
take a character as input and output the corresponding number (between
1 and 26). Your program should only accept capital letters. As
error-checking, print invalid if the input is not a capital letter.
Here is what I have so far.
inp = input()
if (len(inp) > 1 or inp != inp.upper()):
print("invalid input")
else:
print(ord(inp)-ord("A")+1)

letter = input()
if letter>='A' and letter<='Z':
print(int(ord(letter))-64)
else:
print('invalid')

As clarified in the comment, the problem appears to be that the validation doesn't cover enough cases.
To check that the number you calculate is a valid upper-case character you could just check if it is between 1 and 26:
if 1 < ord(inp) - ord("A") + 1 < 26:
print(ord(inp) - ord("A") + 1)
else:
print("Invalid input")
To add this to your current validation:
inp = input()
if (len(inp) > 1 or inp != inp.upper() or
ord(inp) - ord("A") + 1 < 1 or ord(inp) - ord("A") + 1 > 26):
print("invalid input")
else:
print(ord(inp)-ord("A")+1)

This might help you out.
Add whatever you want to test to the test_data list.
# take advantage of the string module to get a string of all upper-case characters
from string import ascii_uppercase # equiv of 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
test_data = ['z', 'AB', '', 10, None, "#", *ascii_uppercase] # some test data including all the upper-case characters
for item in test_data:
string = str(item) # turn the item into type str
length_is_one = len(string) == 1 # get the length
if all((length_is_one, string in ascii_uppercase)): # if all these are true
print(ascii_uppercase.index(item) + 1) # print the index of the letter + 1
else:
print(f'"{item}" is {INVALID}') # I modified the invalid output
OUTPUT:
"z" is invalid
"AB" is invalid
"" is invalid
"10" is invalid
"None" is invalid
"#" is invalid
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Related

How to keep spaces in caesar cipher program

I am programming a Caeser Cipher encoder/decoder for my computer science class in Python 3 and I'm struggling to find out how to exclude spaces when each character is shifted.
message = input("type in a message")
messageInt = float(input("type in an integer"))
newStrs = []
for letter in message:
x = ord(letter)
x = int(x + messageInt)
newStrs.append(chr(x))
print("".join(newStrs))
This happends when I try to use an example sentance with spaces, I've tried looking online for answers but I wasn't able to find anything that seemed to work in Python 3 or would output what my teacher expects.
type in a message hello there
type in an integer 3
#khoor#wkhuh
Process finished with exit code 0
Use a simple list comprehension with a ternary to check for the spaces:
message = input("type in a message")
messageInt = int(input("type in an integer"))
print("".join([c if c == ' ' else chr((ord(c)-ord('a')+messageInt)%26+ord('a')) for c in message]))
You can easily generalize to a whitelist of characters using a set:
keep = set(' .,?!')
print("".join([c if c in keep else chr((ord(c)-ord('a')+messageInt)%26+ord('a')) for c in message]))
Output:
khoor wkhuh
To exclude spaces when shifting the characters in your message, you can simply add an if statement to your loop that checks if the current letter is a space. If it is, you can skip the shifting step and just append the space to the newStrs list.
I modified your code as an example (code updated):
# Get the message and shift amount from the user
message = input("type in a message: ")
shift = int(input("type in an integer: "))
# Create a list to store the shifted characters
new_str = []
# Iterate through each character in the message
for letter in message:
# Check if the character is a space
if letter == " ":
# If it is, append a space to the new string and skip the rest of the loop
new_str.append(" ")
continue
# Shift the character by the specified amount
# First, get the ASCII value of the character
x = ord(letter)
# Shift the character and wrap around the alphabet if necessary
x = (x + shift - 97) % 26 + 97
# Convert the shifted ASCII value back to a character and append it to the new string
new_str.append(chr(x))
# Join the shifted characters into a single string and print it
print("".join(new_str))
If you are wondering whats going on with
x = (x + shift - 97) % 26 + 97
This will shift all characters except spaces, so the output will preserve the spaces in the original message.
x = (x + shift - 97) % 26 + 97
This line of code is used to shift the ASCII value of a character by a specified amount and wrap around the alphabet if necessary.
First, the value of x is added to the value of shift:
x + shift
Then, 97 is subtracted from the result:
x + shift - 97
The result is then passed to the % operator, which returns the remainder of the division of the left operand by the right operand. In this case, the remainder of the division of x + shift - 97 by 26 is calculated:
(x + shift - 97) % 26
Finally, 97 is added back to the result:
(x + shift - 97) % 26 + 97
This has the effect of shifting the character by the specified amount, wrapping around the alphabet if necessary, and ensuring that the resulting ASCII value is in the range 97 to 122 (inclusive).
For example, if the value of x is 120 (the ASCII value of 'x'), the value of shift is 3, and the line of code is executed, the following steps will be taken:
120 + 3 = 123
123 - 97 = 26
26 % 26 = 0
0 + 97 = 97
The final result, 97, is the ASCII value of 'a', so the resulting character will be 'a'.

Unsure why I'm getting IndexError: list index out of range when hardcoding for a whitespace in a decoding problem

As above, am learning Python3 and going through a message decoding problem. I have my code working for letters only, but when I introduce a string with a space (' ') my solution to try and hardcode the ASCII values of space and punctuation in to my nest if loop (within a for loop) I get the IndexError: list index out of range. I understand the error itself, but not how my code is triggering it.
This line of code is my attempt at creating a rule for space:
if newnumberval == 32 + 107:
outputmessage.append(' ')
This is my code so far:
import string
alphabet = list(string.ascii_lowercase)
#check each letter in message, take 10 from its alphanumeric value, add new val
# to outputlist
# if val - 10 is lower than 0, newval(negative) is taken from 26
# remove and store spaces and punctuation
# solution1: hardcode spaces and punctuation values
# i.e. if newnumberval(" ")= 22 then outputmessage.append(" ")
# solution2: if letter not in alphabet, outputmessage.append(letter)
def decodemessage(message):
outputmessage = []
for letter in list(message):
numberval = ord(letter) - 97
newnumberval = numberval - 10
if newnumberval == 32 + 107:
outputmessage.append(' ')
elif newnumberval < 0:
newnumberval += 26
outputmessage.append(alphabet[newnumberval])
else:
outputmessage.append(alphabet[newnumberval])
return outputmessage
print(decodemessage('hello '))

How can I resolve maximum recursion depth error

I'm generating a random password with a desired length. I want it to have at least 2 uppercase letters, 2 lowercase letters, 2 digits and 2 special characters. I've tried multiple things, but every time I get this recursion depth error.
Can anybody tell me what I've done wrong?
list_lower =['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']
list_upper = ['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']
list_digit = [1,2,3,4,5,6,7,8,9,0]
def generatePassword(desiredLength: int) -> str:
x = 0
password = ""
for x in range (desiredLength):
password = password + chr(random.randint(33,126))
list(password)
list_password = list(password)
times_lower = 0
times_upper = 0
times_digit = 0
times_special = 0
for character in list_password:
if character in list_lower:
times_lower += 1
elif character in list_upper:
times_upper += 1
elif character in list_digit:
times_digit += 1
else:
times_special +=1
if times_lower >= 2 and times_upper >= 2 and times_digit >= 2 and times_special >= 2:
return password
else:
return generatePassword(desiredLength)
generatePassword(7)
I get the error in line 30 which makes the function recursive.
Calling generatePassword(7) will never generate a password with 2 of each of 4 distinct categories.
You don't need recursion at all.
def generatePassword(desiredLength: int) -> str:
while True:
password = ""
for x in range (desiredLength):
password = password + chr(random.randint(33,126))
times_lower = 0
times_upper = 0
times_digit = 0
times_special = 0
for character in password:
if character in list_lower:
times_lower += 1
elif character in list_upper:
times_upper += 1
elif character in list_digit:
times_digit += 1
else:
times_special +=1
if times_lower >= 2 and times_upper >= 2 and times_digit >= 2 and times_special >= 2:
return password
else
print ("Rejecting ", password)
That will loop forever if asked to generate a password of length 7 or less. We can improve that by checking the desired length first
if desiredLength < 8:
raise ArgumentError("Cannot generate passwords shorter than 8 characters")
times_digit will never be >= 2 because it tests stings (e.g. "2" against the integers in your list, (e.g. 2) change your list_digit to
list_digit = ["1","2","3","4","5","6","7","8","9","0"]
and try again.
By the way this could be done much simpler and doensn't need a recursive function.
If you are generating passwords, it's important that you generate ones that actually have enough randomness to not be predictable.
Random string generation with upper case letters and digits in Python
Has a great breakdown of how to generate a password that's truly random:
''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))
(adding "special characters" and "lowercase characters" omitted to preserve the existing code)
I know that this is a somewhat oblique answer (i.e. it does not answer the question directly), so here's a potential solution if you still need the "it must contain these types of characters" (even though that would actually reduce security):
import random
import string
from collections import Counter
def gen(N):
return ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(N))
while True:
pw = gen(8)
counts = Counter(pw)
upper = lower = digit = special = 0
for (letter, count) in counts.items():
if (letter in string.ascii_lowercase):
lower += 1
elif (letter in string.ascii_uppercase):
upper += 1
elif (letter in string.digits):
digit += 1
else:
special += 1
pass
if (lower > 1 and upper > 1 and digit > 1 and special > 1):
print("password is {}".format(pw))
break
print("failed password: {}".format(pw))

Python Caesar cipher changes capitalization of the given input strings

In a ceasar cipher I need it to be that my upper characters remain upper and that non letter character remain non letters/the same. I have it that I can work with lower letters.
Upper letters however are converted to lower and a different letter. Non-letter characters are converted to a lower letter as well. Upper letters must shift but remain upper. Non-letter characters must remain as non-letter character.
p = raw_input(("enter a word"))
n = input(("how many must it shift"))
a = 0
b = 0
c = 0
d = 0
for i in p:
if i.isupper():
a += 1
elif i.islower():
b += 1
elif i.isdigit():
c += 1
else:
d += 1
e = ""
for i in p:
if i == "":
e += i
else:
integerValue = ord(i)
integerValue-= 97
integerValue += n
integerValue %= 26
integerValue += 97
e += chr(integerValue)
print e
You can use i.isalpha() to check if the current character is a letter or not and you can use i.isupper() to check if the current letter is uppercase or not. When you convert the letter you will need to make the letter lowercase and then convert it back to upper. On top of those changes you have too many parenthesis for your inputs. I used raw_input since I'm using python 2.7. Your formatting is so off your code that's posted won't run due to indentation errors and your line if i == "" checks for an empty string instead of a space which I am assuming you were going for. All that said here is what I did to your code to try and keep it similar to what you had while cutting out extraneous bits.
p = raw_input("enter a word")
n = int(raw_input("how many must it shift"))
e = ''
for i in p:
if not i.isalpha():
e+=i
else:
integerValue = ord(i.lower())
integerValue-= 97
integerValue += n
integerValue %= 26
integerValue += 97
if i.isupper():
e += chr(integerValue).upper()
else:
e += chr(integerValue)
print e

Appending to the front of for statement output

I have this code;
offset = -0
print ("In Command 3 - Brute force")
string = input("Please enter a string to Brute Force:")
while offset > -26:
offset = offset - 1
print("")
for letter in string:
letter = (ord(letter))
letter = letter + offset
if letter > 126:
letter - 95
elif letter < 32:
letter + 32
output = (chr(letter))
print(output,end='')
choice = 0
Output depending on the string something like this;
rc`rcr
qb_qbq
pa^pap
o`]o`o
n_\n_n
m^[m^m
l]Zl]l
k\Yk\k
j[Xj[j
iZWiZi
hYVhYh
gXUgXg
fWTfWf
eVSeVe
dURdUd
cTQcTc
bSPbSb
aROaRa
`QN`Q`
_PM_P_
^OL^O^
]NK]N]
\MJ\M\
[LI[L[
ZKHZKZ
YJGYJY
Now, I need some text before the output for example;
Decryption string rc`rcr
Decryption string qb_qbq
etc...
I have tried;
print("Decryption",output,end='')
and
print("Decryption"+output,end='')
However this gives me that text in front of every letter.
Please assist if you can, and explanation would also be preferred.
Thanks for your time.
Ben
You want to do something like this:
offset = -0
print ("In Command 3 - Brute force")
string = input("Please enter a string to Brute Force:")
while offset > -26:
offset = offset - 1
word = ""
for letter in string:
letter = (ord(letter))
letter = letter + offset
if letter > 126:
letter - 95
elif letter < 32:
letter + 32
output = (chr(letter))
word = word + output
choice = 0
print("Decryption: "+word)
The problem with what you were trying is that it will print the 'Decrypting:' message for each character not for each word, so you need to build the word before printing it.
You are printing the output letter by letter, so adding print("Decryption"+output,end='') will just add the 'Decryption' part to each printout. I suggest doing a:
print("Decryption" + string, end=' ')
before you start your for loop.
You need to build your output string and then print it after the for loop
offset = -0
print ("In Command 3 - Brute force")
string = input("Please enter a string to Brute Force:")
while offset > -26:
offset = offset - 1
output_final = None
for letter in string:
letter = (ord(letter))
letter = letter + offset
if letter > 126:
letter - 95
elif letter < 32:
letter + 32
output_final += (chr(letter))
choice = 0
print 'Description:', output_final

Categories