I am trying to create a function which is able to detect when two letters back to back are duplicates, for example the ls in "hello", and split the duplicate letters with the letter "x". Here is my code:
plaintext = input("Enter plaintext here: ")
plaintext = plaintext.lower() # makes plaintext lowercase
plaintext = plaintext.replace(" ", "") # removes all spaces
# this separates all duplicate letters
i = 0 # sets i to 0
for letter in plaintext:
if plaintext[-1] == plaintext[-2]: # if the last letter is the same as the second to last
plaintext = plaintext[:-1] + "x" + plaintext[-1:] # separate them with an x
elif plaintext[i] == plaintext [i+1]: # if one letter is the same as the next letter
# the line above makes an error
plaintext = plaintext[:i+1] + "x" + plaintext[i+1:] #separate them with an x
i += 1
else:
i += 1
This code works when I enter hello there as the input; I receive helxlothere. However, when I test another input, such as heythere, IndexError: string index out of range shows up for elif line (line 12). How can I make this code work for all inputs?
You can use regex to achieve this.
For both approaches, it will work for hello there hey there
The difference comes when more than two character repetition happens.
approach 1
import re
string='hello there'
# find Any repeated character and add x in between them
answer = re.sub(r'(.)\1{1}', r'\1x\1', string)
print(answer)
Here for hellllo there text, you will get output helxllxlo there
approach 2
alternatively, you can use this method.
s="hello there"
for match in re.finditer(r"(.)\1+", s):
old=s[match.start():match.end()]
s=s.replace(old,'x'.join(old))
print(s)
here for hellllo there text, you will get output helxlxlxlo there as output.
I think the second approach will be more appropriate.
The IndexError is caused by the fact that you are looking at plaintext[i+1]. As you can see in the word heythere, there are no letters which match back to back, and therefore the code continues until it hits the end, and so you get an IndexError because there is no element i+1.
You can fix this by using this code instead:
plaintext = input("Enter plaintext here: ")
plaintext = plaintext.lower() # makes plaintext lowercase
plaintext = plaintext.replace(" ", "") # removes all spaces
# this separates all duplicate letters
i = 0 # sets i to 0
for letter in plaintext:
if plaintext[-1] == plaintext[-2]: # if the last letter is the same as the second to last
plaintext = plaintext[:-1] + "x" + plaintext[-1:] # separate them with an x
try:
elif plaintext[i] == plaintext [i+1]: # if one letter is the same as the next letter
# the line above makes an error
plaintext = plaintext[:i+1] + "x" + plaintext[i+1:] #separate them with an x
i += 1
else:
i += 1
except IndexError:
pass
This code should stop your code from crashing in the elif statement, while also completing properly.
Hope this helps, have a nice day!
You are receiving the IndexError because during iterating, when the loop reaches the last letter,
elif plaintext[i] == plaintext [i+1]:
this line checks for the letter after the last letter, which does not exist, which causes the program to run into IndexError.
You have to check till the second last letter, for it to work properly.
A simple and easier way to arrive at the same output with a bit easier logic.
Logic
Create a new string and insert all letters which are in the old string, plaintext, to the new string, newPlainText, and check only for one condition that is whether the last letter is same as current or not and if yes then also insert letter 'x' into the newPlainText and that's it!
plaintext = input("Enter plaintext here: ")
plaintext = plaintext.lower() # makes plaintext lowercase
plaintext = plaintext.replace(" ", "") # removes all spaces
# this separates all duplicate letters
newPlainText = plaintext[0] # copy the first letter of plaintext to the new string which will handle the duplicates and insert `x` between them
i = 1 # sets i to 1
while i < len(plaintext):
if plaintext[i] == plaintext[i-1]: # we check that if prev and current letters are same then also append x to the new string
newPlainText += "x"
newPlainText += plaintext[i] # we insert the required current letter to the new string in each iteration
i += 1
print(newPlainText)
Related
I've created a function that takes a user-inputted guess, compared it to a hidden word taken randomly from a word doc, and returns a string that indicates if any letters match or are in the word at all. Here is the function:
def wordResults(guess, testGuess):
#guess = user inputted guess
#testGuess = secret word
results = ""
for i in range(5):
#Check if letters at given position match
#in each word, append capital letter if so
if guess[i] == testGuess[i]:
results += guess[i].upper()
#Check if letter at given position is in
#the secret word at all, append lowercase
#letter if so
elif testGuess.find(guess[i]) != -1:
results += guess[i]
#Append underscore if neither condition is met
else:
results += "_"
return results
My issue lies with the elif-statement. I would like it to print a lowercase only if that letter appears in the word, but not if the letter is already in the correct spot. Here is the program running to show what I'm referring to:
(Note: the hidden word is also user-inputted until I get the program working as intended)
For Guess #2, I would like it so that the first 'h' does not show up, since it is indicating the 5th letter in 'conch' that is already confirmed with a capital 'H'. Hope that makes sense.
It's a lot easier to work with a list and then make it a string at the end:
guess = guess.lower()
testGuess = testGuess.lower()
result = []
for i, letter in enumerate(guess):
if letter in testGuess:
if letter == testGuess[i]:
result.append(letter)
else:
result.append(letter.upper())
else:
result.append('_')
for i, letter in enumerate(result):
if letter.upper() in result and result[i] != letter:
result[i] = '_'
return ''.join(result)
For guess two, then the second loop checks if each letter is already in the loop and placed correctly and if it's not in the correct spot, makes it back into an _.
disclaimer im new to python
i need to split a string input send if to a function that substitutes a character in the string with a different character (like a substitution cipher) but i just dont know how to go about this
print('Welcome to the encryption protocol for top secret governemt cover ups')
string=input('whats your message?')
def encrypt(string):
alpha = "abcdefghijklmnopqrstuvwyz"
sub_alpha = "pokmenliuytrwqazxcvsdfgbhn"
index=0
while index < len(string):
letter=string[index]
im not really sure what im doing im really bad at python, this has had me stumped for 3 days now ive reviewed my course material and tried videos on youtube im probably just really really dumb
I think the key piece of knowledge you're missing is that strings are iterable. So you can do things like:
for c in "FOO":
print(c)
# prints "F\nO\nO\n"
And you can find the index of a character within a string with str.index. So you can build up your cyphertext like this:
alpha = "abcdefghijklmnopqrstuvwyz "
cypher = "pokmenliuytrw qazxcvsdfgbhn"
plaintext = "some string"
cyphertext = ""
for c in plaintext:
char_index = alpha.index(c)
cyphertext += cypher[char_index]
You can also iterate over things inline - this is called a comprehension. So to transform your string you can do this instead of using the for loop:
cyphertext = "".join(cypher[alpha.index(c)] for c in plaintext)
The example above uses the str.join function to concatenate each character of cyphertext.
Here is a solution that asks the question and then iterates through each letter, finding the index in the alpha key, and replacing it with the sub_alpha key equivalent.
Note this example also checks if it should be lowercase or uppercase.
EDIT: if the input character does not have a valid cipher, it doesn't get altered.
EDIT 2: expanded answer to convert both forwards and backwards.
alpha = "abcdefghijklmnopqrstuvwyz"
sub_alpha = "pokmenliuytrwqazxcvsdfgbhn"
def encrypt(in_char):
is_lower_case = in_char.islower()
index = alpha.find(in_char.lower())
if index < 0:
return in_char
elif is_lower_case:
return sub_alpha[index]
else:
return sub_alpha[index].upper()
def decrypt(in_char):
is_lower_case = in_char.islower()
index = sub_alpha.find(in_char.lower())
if index < 0:
return in_char
elif is_lower_case:
return alpha[index]
else:
return alpha[index].upper()
print('Welcome to the encryption protocol for top secret governemt cover ups')
input_str=input('whats your message? ')
output_str=""
for letter in input_str:
output_str += encrypt(letter)
print("Encrypted: ")
print(output_str)
input_str=""
for letter in output_str:
input_str+= decrypt(letter)
print("Decrypted: ")
print(input_str)
string = input("Enter a string:")
character = input ("Enter a character:")
if character in string: #checks string to see if character is in it
print ("Character found!")
else:
print ("Character not found!")
blank = (" _ " * len(string))
print (blank)
I am making a hangman game and I am stuck at this part.
How can I make it so that when the person guesses a letter it replaces the specific " _ " for where the letter should be? Should I be using a for loop to go through all the " _ " 's and then use an if statement?
If someone could show me how it would be great.
You can use a method similar to the following to update your guess string each time the player guesses something.
def update_guesses(current_guess_string, key_string, character_guessed):
out_string = ""
for i in range(len(current_guess_string)):
if current_guess_string[i] == "_" and key_string[i] == character_guessed):
out_string += character_guessed
else: out_string += current_guess_string[i]
return out_string
Building on your code, here's a set of changes that get you to a working game:
Set your string input to a fixed case and move your blank initialization to just after string input. Change them both to lists so you can modify
them as needed:
# normalize case and convert to a mutable data structure
string = list(input("Enter a string: ").lower())
# the blank list matches the string except it starts all blanks
blank = list("_" * len(string))
Get a loop going for handling guesses. Here I'm stopping the loop when the string list is all one character, presumably blanks, as we'll be swapping characters between the blank and input string lists:
while len(set(string)) > 1: # when the string is all blanks, stop
Make sure to cleanup the user input to be only one character and change it to the same case as the string input:
# normalize case on input and make sure to only get 1 character
character = input("Enter a character: ").lower()[0]
After you test if the character is in the string, swap the character, by position, with a character in the blank string. Do this in a loop so you get all instances of the guessed character:
if character in string: # checks string to see if character is in it
print("Character found!")
# find the character in the string and swap it with what's in
# the blank string so we can handle multiple same characters
# correctly
while character in string:
index = string.index(character)
string[index] = blank[index]
blank[index] = character
else:
print("Character not found!")
The last step in the main loop is to print the current state of the guesses:
# print correct guesses and blanks
print(*blank, sep='')
Here is the problematic piece of my function:
def hangman1(word):
global guessesMade
global guessesLeft
currentGuess = '_ ' * len(word)
let = print(input('Please guess a letter: '))
for i in range(len(word)):
if word[i] == let:
print('{} is contained in the word.'.format(let))
if i == 0:
currentGuess = word[0] + currentGuess[1:]
else:
currentGuess = currentGuess[:i] + word[i] + currentGuess[i + 1:]
print(currentGuess)
The user enters a letter at the prompt and it checks if the letter is in the randomWord that was generated outside of the function from a list of words. I can get it to print the blanks correctly, but if the user enters a letter that is in the word it prints out a line of the correct letter instead of the blanks with the correct letter mixed in between.
Any help is appreciated.
The main problem you're having right now is two-fold - one, that the replace() method replaces all instances of any given input within a string, not the first one, and two, that you don't currently have any way of telling which letters you've already uncovered. Calling replace("_", let) will always replace every single instance of "_", and given that you're applying that to a string that is only composed of underscores, it'll always overwrite the entire string. It seems like you're also regenerating hidden_let every time hangman() is called with a guess letter, meaning that best-case with your design now you're only going to ever show every letter the user just guessed and a bunch of underscores otherwise.
What you'd want to do is have two values, correct_word and current_guess. correct_word will be the word the player has to guess, and current_guess will be their progress in guessing the word, starting with a string of only underscores of the same length as correct_word.
Here's a short example. I've taken the liberty of removing your global references - globals are generally frowned upon - and encapsulated the behavior in a small class. You'd want to replace the value in hangmanner.play_hangman() with whatever your random word is.
class Hangmanner:
correct_word = ''
current_guess = ''
def play_hangman(self, word):
self.correct_word = word
self.current_guess = '_' * len(self.correct_word)
while self.current_guess != self.correct_word:
self.guess_letter(input("Please guess a letter: "))
def guess_letter(self, guessed_letter):
for i in range(len(self.correct_word)):
if self.correct_word[i] == guessed_letter:
if i == 0:
self.current_guess = self.correct_word[i] + self.current_guess[1:]
else:
self.current_guess = self.current_guess[:i] + self.correct_word[i] + self.current_guess[i + 1:]
print(self.current_guess)
if __name__ == "__main__":
hangmanner = Hangmanner()
hangmanner.play_hangman("test")
This uses the slicing function in python, where you can use the brackets and the [first:last] syntax to access an arbitrary range of any given collection. If either first or last is missing, the slice continues to the beginning or end of the collection, respectively. Above, current_guess[1:] returns current_guess from the second index to the last. current_guess[:i] returns current_guess from the first index up to the index preceding i, given that last is the exclusive end bound.
hiddenLet.replace('_',let) replaces all occurrences of _ with whatever let represents.
newWordList = [x if x==let else '_' for x in randWord]
newWord = ''.join(newWordList)
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
So the question reads:
Write a program that accepts as input a sentence in which all of the words are run together but the first character of each word is uppercase. Convert the sentence to a string in which the words are separated by spaces and only the first word starts with an uppercase letter. For example the string "StopAndSmellTheRoses." would be converted to " Stop and smell the roses."
I am so confused this my code so far.
def main():
#User enters a sentence
my_string=input('enter a sentence: ')
print(my_string.capitalize())
main()
You can loop through the string and add a character each time to a result:
my_string = "StopAndSmellTheRoses"
i = 0
result = ""
for c in my_string:
if c.isupper() and i > 0:
result += " "
result += c.lower()
else:
result += c
i += 1
print result
We'll use c for each character as we walk through the string and we'll use i to keep track of the position in the string.
There are two possibilities: it's either an uppercase character (excluding the first one) or it's not.
In the first case we'll add a space and that character as lowercase to the result. This ensures a space is inserted before each uppercase character further in the sentence.
In the second case it's a lowercase character or the uppercase character at the beginning of the sentence. We don't have to do anything with these and we'll add it right away.
Lastly we add one to i whenever we're done with a character (i += 1) as this means we correctly know where we are in the sentence.
Welcome to SO!
One way to do this is to loop through your string, checking the chars one by one:
#You've learned how to iterate through something, right?
i = 0 #a counter
for c in my_string: #get the characters of my_string, one by one.
if c.isupper(): #check if it's in upper case
if i == 0: #if it's the first letter
new_string += c #let it be like the original
else:
new_string += ' '+.lower() #it's not the first letter,
#so add space, and lower the letter.
else:
new_string += c #else, only add the letter to the new string
i += 1
Edit added a double-check to see if it's the first letter of the sentence or not. Updated demo.
As an alternative to using a counter, you can also use the built-in function enumerate, which returns a tuple of index and values.
for i,c in enumerate(my_string): #get the characters of my_string, one by one.
if c.isupper(): #check if it's in upper case
if i == 0: #if it's the first letter
new_string += c #let it be like the original
else:
new_string += ' '+c.lower() #it's not the first letter,
#so add space, and lower the letter.
else:
new_string += c #else, only add the letter to the new string
Demo
>>> my_string = 'ImCool'
>>> new_string = ''
>>> i = 0 #a counter
>>> for c in my_string: #get the characters of my_string, one by one.
if c.isupper(): #check if it's in upper case
if i == 0: #if it's the first letter
new_string += c #let it be like the original
else:
new_string += ' '+.lower() #it's not the first letter,
#so add space, and lower the letter.
else:
new_string += c #else, only add the letter to the new string
i += 1
>>> new_string
'Im cool'
Hope this helps!
You'll need a bit of regex.
import re
split = re.findall(r'[A-Z][a-z\.]+', 'HelloThisIsMyString.')
You'll also need to join those together (inserting spaces)
' '.join(...)
and handle case conversions
' '.join(word.lower() for word in split)
(and as you already did, capitalize the first word)
' '.join(word.lower() for word in split).capitalize()
It appears that you are a little confused and this is to be expected if you are new to Python. I'm assuming you take input from the user as opposed to input for a function. Either way I would create a simple function that you could insert the users input into. The function below will accomplish what the problem asks.
def sentenceSplitter(sentence):
result = ""
for i, x in enumerate(sentence): #i is character index, x is the element
if i == 0:
result = result + x
elif x.isupper() == False: #if element is not uppercase, add it to the result
result = result + x
else: # Otherwise, add a space and lowercase the next letter
result = result + " " +x.lower()
return(result)
To reiterate, if you are looking to print out the sentence you would write this after the function:
def main():
#User enters a sentence
my_string=input('enter a sentence: ')
print(sentenceSplitter(my_string))
main()
If you are still confused feel free to ask any further questions.