While loop using strings and if statements - python

Prompt for python program:
5.16 LAB: Password modifier
Many user-created passwords are simple and easy to guess. Write a program that takes a simple password and makes it stronger by replacing characters using the key below, and by appending "!" to the end of the input string.
i becomes 1
a becomes #
m becomes M
B becomes 8
s becomes $
Ex: If the input is:
mypassword
the output is:
Myp#$$word!
Hint: Python strings are immutable, but support string concatenation. Store and build the stronger password in the given password variable.
My code so far:
word = input()
password = ''
i = 0
while i < len(word):
I'm really just confused about changing certain characters in a string using a while loop. I was also trying to use if statements. While loops are hard for me understand. I don't know where to start.

You're doing fine. While loops have this format:
while (condition) == True:
# do this
In your case, you will want to put i += 1 at then end of your loop so it doesn't go on forever, like this:
while i < len(word):
# your code here
i += 1 # this adds 1 to i
# so eventually it will be equal to the length of word, ending the loop
As for if statements, you could try this:
while i < len(word):
if word[i] == 'i':
password += '1'
i += 1
The if statements will only work on the values you want to change, so add an else statement to act as a default.
while i < len(word):
if word[i] == 'i':
password += '1'
else:
password += word[i]
i += 1
Now, if you get a normal letter, like h, it will add it to the password.
Hope this helps you get started!

Related

Why is Python exiting the while loop without fullfilling all the conditions?

I am coding a password checker for an assignment. Here are the requirements:
At least 2 letter between [a-z] and 1 letter between [A-Z]. At least 1 number between [0-9]. At least 1 character from [?$##]. Minimum length 10 characters. Maximum length 14 characters. Moreover, in case of invalid password, the program should keep on asking for new password unless it validates it successfully.
For some odd reason, my code ignores the while loop conditions and jumps out of it after completing the for loop. What am I doing wrong and how could I fix this? Any help is appreciated!
import string
#password
password = input("Enter a password that has at least two lower case letter, one upper case letter, one number between one and nine, and one of the following characters: '?$##.'")
#requirements
ll = list(string.ascii_lowercase)
ul = list(string.ascii_uppercase)
nums = ["1","2","3","4","5","6","7","8","9"]
char = ["?","$","#","#"]
#counters
llcounter = 0
ulcounter = 0
numscounter = 0
charcounter = 0
while llcounter < 2 or ulcounter < 1 or numscounter < 1 or charcounter < 1:
if len(password) > 10 and len(password) < 14:
for x in password:
if x in ll:
llcounter += 1
elif x in ul:
ulcounter += 1
elif x in nums:
numscounter += 1
elif x in char:
charcounter += 1
else:
password = input("Your password has not met the requirements, please try another one.")
well that is because your nested loop logic is flawed. it will keep iterating through the for loop and never checks the while loop conditions. you want the for loop iteration on the outside and break outside when the while conditions are met
for ...:
while ...:
...
break
password = input( ... first prompt ... )
while True:
set all counters = 0
for x in password:
if x in ll:
...etc, just like above...
if llcounter >= 2 and ulcounter and numscounter and charcounter:
break
password = input ( ... second prompt ... )
You need to look up Validate input. You have interleaved steps of your logic. The basic structure is:
valid_pwd = False
while not valid_pwd:
password = input("Enter a password ...")
# Make *one* pass to validate the password.
# Count each type of character;
...
# Also check the length
length_valid = 10 < len(password) < 14
chars_valid = (
ll_count >= 2 and
lu_count >= 1 and
...
)
valid = length_valid and chars_valid
if not valid:
print("Invalid password")
Does that get you going?
When coding in Python you should always try to find the most Pythonic way to implement your idea, namely by making the most of the language's potential to write concise, clear and functional code.
In your case you have redundant code in several places, especially that while loop with so many counters is pretty confusing.
Also, in order to maintain an active input session for the user, you should put the prompt inside the while loop, otherwise the password will always be the same entered for the first (and only) time making the loop itself rather pointless.
For example, here's how you might implement this password check. I commented on the code to try to explain what it's actually doing.
import string
ll = list(string.ascii_lowercase)
ul = list(string.ascii_uppercase)
nums = list(string.digits)[1:] # List of digits except 0
sc = ["?","$","#","#"]
# while True meaning keep prompting forever until password requisites are satisfied.
# This can be changed with a counter to exit the loop after a certain number of attempts.
while True:
password = input("Enter a password that has blablabla...") # Prompt the user at every iteration of the loop.
# The following if block merges all your conditions in the same place avoiding hyper-nested
# conditions that are always confusing and kind of ugly.
# The conditions are based on list comprehension
# (see py docs, since its a super cool feature that is the ultimate tool for conciseness).
if (len([char for char in password if char in ll]) >=2 and
[char for char in password if char in ul] != [] and
[char for char in password if char in nums] != [] and
[char for char in password if char in sc] != []):
print(f" Your new password is: {password}")
break # break interrupts the endless loop as soon as a valid password is entered
else:
print("Your password has not met the requirements, please try another one.")

Why is my program not detecting uppercase characters?

I am asking the user to input a password. The program then determines if the password is valid if it meets certain criteria.
Here is the section in question.
for i in range(0, len(password)):
if(password[i].isdigit()==True):
isNum+=1
elif (password[i].isalpha()==True):
isLetter+=1
elif (password[i].isupper()==True):
isUpper+=1
My program detects numbers and letters just fine, but it doesn't detect uppercase letters. If I put in the password 1234Foxes, it will say that there are 4 letters, 4 numbers, and 8 total characters, but it states that there are 0 uppercase letters.
If isupper is true, isalpha must have been true since an uppercase letter is necessarily alphabetic. Since you're using elif, the conditions are exclusive, and the checks stop once the isalpha check is true.
Just don't use elif there if you want both checks to run:
for character in password:
if(character.isdigit()):
isNum += 1
elif (character.isalpha()):
isLetter += 1
if (character.isupper()):
isUpper += 1
I also got rid of the need to index password by iterating the String directly, and the redundant == True
your logic is correct, but, you need to check if a char is upper before if it is alpha. It's because every upper char is alpha, so, the last elif will never be reached. So, change your code position to this:
for i in range(0, len(password)):
if(password[i].isdigit()==True):
isNum+=1
# isupper() first from isalpha()
elif (password[i].isupper()==True):
isUpper+=1
elif (password[i].isalpha()==True):
isLetter+=1
And, another tip: you can go through every char of string without using indexing, like this:
for char in password:
if (char.isdigit()):
isNum += 1
elif (char.isupper()):
isUpper += 1
elif (char.isalpha()):
isLetter += 1
Since isdigit(), isalpha() and isupper() returns True or False, you don't need to check if it is True or False (no need the == operator), just put it into if statement.

comparison within loop while maintaining value

I am trying to make a python program that
1. compares the first and last letter of a word
2. tell whether the words inputted to the program are in alphabetical order
e.g.) alaska baobab cadillac => would pass
e.g.) alaska baobab greg => would not pass
my code is shown below
num_words = int(input())
while num_words > 0:
my_word = str(input())
num_words -= 1
alphabet_order = ord(my_word[0].lower())
if my_word[0].lower() != my_word[-1].lower() or alphabet_order != ord(my_word[0].lower()):
print(my_word)
break
alphabet_order += 1
if alphabet_order == ord('z') + 1:
alphabet_order = ord('a')
else:
print('no mistake')
Hi everyone on stack.
I am pretty new to programming and I am starting to find assigning variables within a loop very cumbersome.
It seems like my variable alphabet_order keeps getting renewed every time when the loop takes in a new input.
What would be a great way to steadily increase the alphabet_order variable by 1 while only renewing ord(my_word[0]) every loop?
Few notes, while loops are good for when you don't know how many times you are going to loop. for loops are good for when you know how many items you are looping. In this case we have a known number of words we are to check so using a for loop makes more sense. This way we don't need to take an input on telling us how many words we expect.
Also your code wasn't dealing with the words you were dealing with the sentence, so instead you should split() your input by the space to get a list of the words to operate on. for loops can iterate through a list which is useful for us.
Note that for loops also can take an else, the else section runs after the for loop is finished all the elements it's iterating through and we know that if the for is finish all the words in the list, then the else section will kick in. If we break out, then the else doesn't run.
The rest you more or less had it, just need a starting ord('a') would have made life easier.
my_word = input() #take a sentence such as alaska baobab cadillac
current_ord = ord('a')
for each in my_word.split(' '):
if each[0].lower() != each[-1].lower() or current_ord != ord(each[0].lower()):
print(f"bad word {each}")
break
current_ord += 1
if current_ord == ord('z') + 1:
current_ord = ord('a')
else:
print('no mistake')
Maybe this is helpful to you. Instead of initializing it inside the loop, declare it outside the loop and assign it differently based on the conditions.
Tip: having while num_words > 0 is redundant because it will terminate automatically when it hits 0 as it is treated as False. And there is no need to convert int to a str type as it is str by default.
num_words = int(input("Enter a number"))
alphabet_order = None
while num_words:
my_word = input("Enter a word")
num_words -= 1
if alphabet_order is None: # happens only once
alphabet_order = ord(my_word[0].lower())
if ord(my_word[0].lower()) >= alphabet_order:
print('ok')
alphabet_order = ord(my_word[0].lower()) # update alphabet_order
else:
print('not ok EXITING')
break # closing loop

Python Vigenere working, but I can't account for the spaces and non alphabetical characters using functions

I am currently working on a cipher program for a beginners python course. We first were told to create a function that would return the position of a given letter, using a string of the alphabet as a reference (that is my alphabet_position function.) Next, we were told to make a function that would allow for a single letter to be rotated by a chosen number(that is my rotate_character function). Third, we were tasked with creating a basic caesar cipher using the previous two functions. All of those I was able to make work as demonstrated by my code below.
The vigenere, however, is proving much more difficult. I was actually able to find a snippet of code that I was able to modify with my first function (alphabet_position) to make work if only alphabetic characters are used, but as soon as i enter any non alphabetical character (such as ! or ?) I get a return of ValueError: Substring Not found. When the program encounters these non alphabetical characters, the key is supposed to skip over them and carry the Nth character of the key to the next alphabetical character.
I am guessing the answer lies in somehow incorporating my rotate_character function into my Encrypt function, but I am unsure how to do this, as the rotate_character function expects a alphabetical character, and the vigenere function turns that parameter into an int before running it through.
Any advice? And as I am a new programmer, I will gladly take any other helpful criticism on my coding practices you may want to instill!`
> #Create function alphabet_position(letter) to turn letter into number
> #such as a=0 or e=4, using lowercase to make sure case doesnt matter.
alphabet = "abcdefghijklmnopqrstuvwxyz"
def alphabet_position(letter):
> lower_letter = letter.lower() #Makes any input lowercase.
> return alphabet.index(lower_letter) #Returns the position of input
as a number.
>
> def rotate_character(char, rot):
> if char.isalpha():
> a = alphabet_position(char);
> a = (a + rot) % (int(len(alphabet))); #needs modulo
> a = (alphabet[a]);
> if char.isupper():
> a = a.title()
> return a
> else:
> return char
>
> def caesar(text, rot):
> list1 = ""
> for char in text:
> list1 += rotate_character(char, rot)
> return list1
>
> def vigenere(text,key):
m = len(key)
>
> newList = ""
>
> for i in range(len(text)):
text_position = alphabet_position(text[i])
key_position = alphabet_position(key[i % m])
value = (text_position + key_position) % 26
newList += alphabet[value]
return newList
>
> def main():
> x = input("Type a message: ")
> y = input("Rotate by: ")
> result = vigenere(x, y)
> print (result)
>
> if __name__ == '__main__':
main()
No, you don't need the rotate function anymore. You just need to directly add any character that is not in the alphabet to newlist and then skip the encryption part.
Now a sub-optimal way of doing this is to use if ... in ...:
if text[i] in alphabet:
# do your thing
else:
newList += text[i]
Of course more optimal is to only go through the alphabet once and use a variable:
pt_c = text[i]
pt_i = alphabet.find(pt_c) # returns -1 instead of an error when not found
if pt_i == -1:
newList += pt_c
else:
newList += pt_c
# do your thing *with the given index*
This won't make any difference in the runtime for a Vigenère cipher of course. But it shows you how to think of efficient programming for later: there is no need to search twice.
You could also continue the loop instead of having an else statement:
pt_c = text[i]
pt_i = alphabet.find(pt_c) # returns -1 instead of an error when not found
if pt_i == -1:
continue
# do your thing with the given index
this will make the indentation depth of your loop (the amount of scopes) less, with the unfortunate side effect of making your loop more complex (creating a local exit point).

Python 3.5.0 - Break is outside loop

I have the following code written:
Value = input("LOL")
LetterNum = 1
for Letter in Value :
pass
print("Letter ",LetterNum,"is",Letter)
LetterNum += 1
break
I can't get it to display the numbers corresponding to the letters, all I'm getting is a break outside loop, what is causing that error?
You need to spend some serous time at python tutorial sites or somehow get familiar with the language.
Your code won't work, not even close to what you want it to. I'm assuming this is what you want:
Value = "LOL"
LetterNum = 1
for Letter in Value :
print("Letter ",LetterNum,"is",Letter)
LetterNum += 1
Which will give you:
Letter 1 is L
Letter 2 is O
Letter 3 is L
Indentation, Indentation, Indentation, Indentation...
Understand how variable are assigned (very basic)
No need for break in that code if you already have a pre-set loop (start to end)
Indentation, Indentation, Indentation, Indentation...

Categories