Caesar Cipher python program looping issue - python

Okay, so I have gotten this far with a Caesar cipher program in python and cannot see any reason why this doesn't work, but it doesn't... it outputs the new word as 'a' 'a' 'a' (for however many letters were in the word). My guess is its some kind of loop that results in each letter being changed into 'a' but I just can't figure it out. Can anybody out there help me? Thanks.
My code:
word = input("please enter the word you wish to encrypt: ")
seperated = list(word)
length = len(word)
alphabet1 = ["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"]
alphabet2 = ["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","a"]
length2 = len(alphabet1)
length3 = len(alphabet2)
for eachposition in range(length):
for letter in range(length2):
if seperated[eachposition] == alphabet1[letter]:
seperated.pop(eachposition)
seperated.insert(eachposition, alphabet2[letter])
print(seperated)

I added some print calls to your code and got lots of false positives where seperated[eachposition] == alphabet1[letter] was evaluating as True when it shouldn't. From that I realised the problem pretty quickly.
You never use break from your loop, so here's what's happening:
I enter "word"
The loop runs until it finds that the first letter is the same as the letter 'w' in alphabet
The first letter of separated is set as 'x', due to how your cipher works
The loop keeps running, now testing if the first letter is 'x'
Since your cipher just advances each letter on one, this loop will always run until it reaches the last letter in your list, 'a'.
The simple fix is to use break. It will end a for loop prematurely. Meaning that when you have replaced a letter, you want to break out of that inner loop and move onto the next character.
for eachposition in range(length):
for letter in range(length2):
if seperated[eachposition] == alphabet1[letter]:
seperated.pop(eachposition)
seperated.insert(eachposition, alphabet2[letter])
break

#SuperBiasedMan gave you the working solution for keeping your code, but i would propose a simpler loop for your task(no need for poping and inserting in place since you are iterating orderly in the whole word).
new = []
for c in word:
for i in range(length2):
if c == alphabet1[i]:
new.append(alphabet2[i])
break
new = ''.join(new)
print new
Also consider using dictionaries. They come in handy for problems like that.

Another approach is to convert each character to its ASCII number, ie a = 97, b = 98 etc asciitable.com This can be done with ord()
num = ord(character) - 97
Then you can add the key to obtain a new ascii code and convert it back to a character. You have use modulo to allow for cycling around from the end to the start of the alphabet.
encr_char = chr((num + key)% 26 + 97)
You need to add code to handle upper case letter (ascii 65 - 90) and non alphabet characters. isupper() and isalpha() will do the job here.
This approach allows for varying the key.

Related

How to replace a string with the user input in its designated spot - hangman

displayed_word = ["-"*length]
#how do you write the guess in the designated spot in hangman?? and to replace the "-" ]
designated_spot = s.find(guess)
how_many = s.count(guess)
print(f"the word had {how_many} of your guess in it")
print(designated_spot)
newletter = s.replace(guess, designated_spot)
print(newletter)
I know a did a lot wrong here but i dont know what to do and how to write it.
I tried to do replace but i realised you couldnt use an integer and the integer is designated_spot and fhsdjkfsh
also s is the random.choice of my list
So I am trying to replace a user input aka "guess" in the designated spot where the guess fits in the random choice that python has generated from a list. As in change the "-" in its designated spot with the letter that the user guessed when the letter that is guessed is in the random word that the computer has generated.
I think what you're looking for is something like this:
word = "apple" # example output from your list
displayed_word = "-"*len(word) # make a string the same length of the word that shows guessed letters
def make_guess(s: str, guess: str, displayed_word: str) -> str:
# convert the displayed word to a list so that it is mutable (allows us to change characters)
displayed_word = list(displayed_word)
# loop through the string you are guessing, s, one character at a time.
for i, letter in enumerate(s):
if letter == guess: # if the current letter in s matches the guess, reveal that letter.
displayed_word[i] = letter
return "".join(displayed_word) # finally convert the displayed word back into a string.
displayed_word = make_guess(word, "p", displayed_word)
print(displayed_word) # outputs: "-pp--"
displayed_word = make_guess(word, "e", displayed_word)
print(displayed_word) # outputs: "-pp-e"
Check the documentation for replace for information about how to use str.replace, as it doesn't replace characters at a specific index as you might have misunderstood, I hope this helps.

I wrote a program to replace all characters in the inputted string with the alphabet in order, but I dont get why it isn't working as intended

This may seem like a beginner question, but I'm new to python and don't know much.
So this program is supposed to take the inputted string and replace all the characters in it with the alphabet in order, for eg. inputting "python" should output "abcdef" and inputting "program" should output "abcdefg" etc.
import string
char = input()
new_char = "-"
for i, let in enumerate(char):
new_char = char.replace(let, string.ascii_letters[i])
char = new_char
print(new_char)
For some reason, it only replaces some of the characters with random letters and not in order. But when I replace the i in string.ascii_letters[i] with any index from 0 - 51, then the program works as intended. Could someone explain to me why this program is not working as intended?
You don't need to complicate it. ascii_lowercase prints all the lower case letters. It returns a string. So taking advantage of slicing, you can do string slicing
import string
char = input()
new_char = string.ascii_lowercase[:len(char)]
print(new_char)
.replace replaces all the occurrences of a particular substring. So, if you do program and you replace it, it becomes arogram. Notice that you assign that to new_char. With next iteration, it will become abogbam
Use chr() to convert from int to characters (don't forget that the input is ASCII char codes)
If you don't care about the value in the input string, just iterate over for i in range(len(input_string)):
char.replace(old_chr, new_chr) will replace all instances of old_chr throughout the whole string (what if your input is hello?)
How about:
import string
char = input()
new_char = ''
for x in range(len(char)):
new_char += string.ascii_letters[x]
print(new_char)

Python - Bug in code

This might be an easy one, but I can't spot where I am making the mistake.
I wrote a simple program to read words from a wordfile (don't have to be dictionary words), sum the characters and print them out from lowest to highest. (PART1)
Then, I wrote a small script after this program to filter and search for only those words which have only alphabetic, characters in them. (PART2)
While the first part works correctly, the second part prints nothing. I think the error is at the line 'print ch' where a character of a list converted to string is not being printed. Please advise what could be the error
#!/usr/bin/python
# compares two words and checks if word1 has smaller sum of chars than word2
def cmp_words(word_with_sum1,word_with_sum2):
(word1_sum,__)=word_with_sum1
(word2_sum,__)=word_with_sum2
return word1_sum.__cmp__(word2_sum)
# PART1
word_data=[]
with open('smalllist.txt') as f:
for l in f:
word=l.strip()
word_sum=sum(map(ord,(list(word))))
word_data.append((word_sum,word))
word_data.sort(cmp_words)
for index,each_word_data in enumerate(word_data):
(word_sum,word)=each_word_data
#PART2
# we only display words that contain alphabetic characters and numebrs
valid_characters=[chr(ord('A')+x) for x in range(0,26)] + [x for x in range(0,10)]
# returns true if only alphabetic characters found
def only_alphabetic(word_with_sum):
(__,single_word)=word_with_sum
map(single_word.charAt,range(0,len(single_word)))
for ch in list(single_word):
print ch # problem might be in this loop -- can't see ch
if not ch in valid_characters:
return False
return True
valid_words=filter(only_alphabetic,word_data)
for w in valid_words:
print w
Thanks in advance,
John
The problem is that charAt does not exist in python.
You can use directly: 'for ch in my_word`.
Notes:
you can use the builtin str.isalnum() for you test
valid_characters contains only the uppercase version of the alphabet

Python error TypeError: string indices must be integers

I'm kind of new to Python so if this is a silly mistake, please forgive me!
I have been working on a Password Generator to present at tech club. How it works is that it asks for you to type in a word. The word you enter is turned into a list. Then it changes each letter in the list to something else to make a unique password (it's flawed, I know). When I run the code, it says TypeError: string indices must be integers. What is wrong with my code?
print ("Leo's Password Generator")
print ('Please enter a word')
word = input()
print ('Enter another word:')
word2 = input()
word = word2 + word
word.split()
def print_list(toon):
for i in toon:
if toon[i] == 'e':
toon[i] = '0'
print_list(word)
print (word)
The problem is that you're passing a string to print_list. When you iterate through a string, it splits it into single-character strings. So, essentially what you're doing is calling toon['a'], which doesn't work, because you have to use an integer to access an iterable by index.
Note also that both you and Batuhan are making a mistake in the way you're dealing with strings. Even once you fix the error above, you're still going to get another one immediately afterwards. In python, string doesn't allow item assignment, so you're going to have to create an entirely new string rather than reassigning a single character therein.
If you wanted, you could probably use a list comprehension to accomplish the same task in significantly less space. Here's an example:
def print_list(toon):
return ''.join([ch if ch != 'e' else '0' for ch in toon])
This creates a new string from toon where all incidences of 'e' have been replaced with '0', and all non-'e' characters are left as before.
Edit: I might have misunderstood your purpose. word.split() as the entirety of a statement doesn't do anything - split doesn't reassign, and you'd have to do word = word.split() if you wanted to word to equal a list of strings after that statement. But - is there a reason you're trying to split the string in the first place? And why are you assigning two separate words to a single variable called word? That doesn't make any sense, and makes it very difficult for us to tell what you're trying to accomplish.
For loop already gives you the value of the next available item. In your case, i is not an index, it is the value itself.
However, if you want to reach to both index and the value, you can use enumerate:
def print_list(toon):
for i, ch in enumerate(toon):
if ch == 'e':
toon = toon[:i] + '0' + toon[i+1:]
print(toon)
or you can iterate over the string in a traditional method:
def print_list(toon):
for i in range(len(toon)):
if toon[i] == 'e':
toon = toon[:i] + '0' + toon[i+1:]
print(toon)
EDIT:
As #furkle pointed out, since strings are immutable, they cannot be changed using indexes. So use concatenation, or replace method.

Can't convert 'list'object to str implicitly Python

I am trying to import the alphabet but split it so that each character is in one array but not one string. splitting it works but when I try to use it to find how many characters are in an inputted word I get the error 'TypeError: Can't convert 'list' object to str implicitly'. Does anyone know how I would go around solving this? Any help appreciated. The code is below.
import string
alphabet = string.ascii_letters
print (alphabet)
splitalphabet = list(alphabet)
print (splitalphabet)
x = 1
j = year3wordlist[x].find(splitalphabet)
k = year3studentwordlist[x].find(splitalphabet)
print (j)
EDIT: Sorry, my explanation is kinda bad, I was in a rush. What I am wanting to do is count each individual letter of a word because I am coding a spelling bee program. For example, if the correct word is 'because', and the user who is taking part in the spelling bee has entered 'becuase', I want the program to count the characters and location of the characters of the correct word AND the user's inputted word and compare them to give the student a mark - possibly by using some kind of point system. The problem I have is that I can't simply say if it is right or wrong, I have to award 1 mark if the word is close to being right, which is what I am trying to do. What I have tried to do in the code above is split the alphabet and then use this to try and find which characters have been used in the inputted word (the one in year3studentwordlist) versus the correct word (year3wordlist).
There is a much simpler solution if you use the in keyword. You don't even need to split the alphabet in order to check if a given character is in it:
year3wordlist = ['asdf123', 'dsfgsdfg435']
total_sum = 0
for word in year3wordlist:
word_sum = 0
for char in word:
if char in string.ascii_letters:
word_sum += 1
total_sum += word_sum
# Length of characters in the ascii letters alphabet:
# total_sum == 12
# Length of all characters in all words:
# sum([len(w) for w in year3wordlist]) == 18
EDIT:
Since the OP comments he is trying to create a spelling bee contest, let me try to answer more specifically. The distance between a correctly spelled word and a similar string can be measured in many different ways. One of the most common ways is called 'edit distance' or 'Levenshtein distance'. This represents the number of insertions, deletions or substitutions that would be needed to rewrite the input string into the 'correct' one.
You can find that distance implemented in the Python-Levenshtein package. You can install it via pip:
$ sudo pip install python-Levenshtein
And then use it like this:
from __future__ import division
import Levenshtein
correct = 'because'
student = 'becuase'
distance = Levenshtein.distance(correct, student) # distance == 2
mark = ( 1 - distance / len(correct)) * 10 # mark == 7.14
The last line is just a suggestion on how you could derive a grade from the distance between the student's input and the correct answer.
I think what you need is join:
>>> "".join(splitalphabet)
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
join is a class method of str, you can do
''.join(splitalphabet)
or
str.join('', splitalphabet)
To convert the list splitalphabet to a string, so you can use it with the find() function you can use separator.join(iterable):
"".join(splitalphabet)
Using it in your code:
j = year3wordlist[x].find("".join(splitalphabet))
I don't know why half the answers are telling you how to put the split alphabet back together...
To count the number of characters in a word that appear in the splitalphabet, do it the functional way:
count = len([c for c in word if c in splitalphabet])
import string
# making letters a set makes "ch in letters" very fast
letters = set(string.ascii_letters)
def letters_in_word(word):
return sum(ch in letters for ch in word)
Edit: it sounds like you should look at Levenshtein edit distance:
from Levenshtein import distance
distance("because", "becuase") # => 2
While join creates the string from the split, you would not have to do that as you can issue the find on the original string (alphabet). However, I do not think is what you are trying to do. Note that the find that you are trying attempts to find the splitalphabet (actually alphabet) within year3wordlist[x] which will always fail (-1 result)
If what you are trying to do is to get the indices of all the letters of the word list within the alphabet, then you would need to handle it as
for each letter in the word of the word list, determine the index within alphabet.
j = []
for c in word:
j.append(alphabet.find(c))
print j
On the other hand if you are attempting to find the index of each character within the alphabet within the word, then you need to loop over splitalphabet to get an individual character to find within the word. That is
l = []
for c within splitalphabet:
j = word.find(c)
if j != -1:
l.append((c, j))
print l
This gives the list of tuples showing those characters found and the index.
I just saw that you talk about counting the number of letters. I am not sure what you mean by this as len(word) gives the number of characters in each word while len(set(word)) gives the number of unique characters. On the other hand, are you saying that your word might have non-ascii characters in it and you want to count the number of ascii characters in that word? I think that you need to be more specific in what you want to determine.
If what you are doing is attempting to determine if the characters are all alphabetic, then all you need to do is use the isalpha() method on the word. You can either say word.isalpha() and get True or False or check each character of word to be isalpha()

Categories