I think I have the right idea of solving this function, but I'm not sure why
I don't get the desired results shown in the docstring. Can anyone please help me fix this?
def check_password(s):
'''(str, bool)
>>> check_password('TopSecret')
False
>>> check_password('TopSecret15')
True
'''
for char in s:
if char.isdigit():
if char.islower():
if char.isupper():
return True
else:
return False
Your logic is flawed it should look like this:
def check_password(s):
has_digit = False
has_lower = False
has_upper = False
for char in s:
if char.isdigit():
has_digit = True
if char.islower():
has_lower = True
if char.isupper():
has_upper = True
# if all three are true return true
if has_digit and has_upper and has_lower:
return True
else:
return False
Now, let's talk about what's wrong with your code.
def check_password(s):
for char in s:
if char.isdigit():
if char.islower(): # we only get to this check if char was a digit
if char.isupper(): # we only get here if char was a digit and lower
# it is not possible to get here
# char would have to be a digit, lower, and upper
return True
else:
return False
As an example let's look at TopSecret15, we start with T
isdigit = False so we immediately return false for 'T'
We will continue to immediately return false until we get to '1'
Let's say we were on 1, isdigit would be true, but islower would be false so again it returns false
Do you see how it is impossible for all three of these to be true for the same char?
Your code is equivalent to:
if char.isdigit and char.islower and char.isupper:
# this will never happen
The reason this doesn't work is that for any given character, it first checks if it's a digit, then if it's lower, then if it's upper. Obviously, one character cannot be all three of these at once. You instead want to check each character to see if it's a digit, lowercase, or uppercase, and then flip a boolean value like has_upper. Then, after the for loop, you'll check to see if all of the boolean values are true.
See this answer:
https://stackoverflow.com/a/2990682/7579116
the best way to check if a password match all the requirements is by using a regex.
I've gone way overboard here (because I'm bored I guess) and whipped up an extensible password-checking system. Perhaps it will help clarify why your version doesn't do what you want (or maybe it won't).
The essential problem with your code is that it checks for a single characteristic instead each of the required characteristics.
def check_password(password):
# These are the characteristics we want a password to have:
characteristics = [
length(8), # At least 8 characters
lower_case(1), # At least 1 lower case letter
upper_case(1), # At least 1 upper case letter
number(1), # At least 1 number
]
# Check to see if the supplied password has *all* of the desired
# characteristics:
return all(
characteristic(password)
for characteristic in characteristics
)
def length(n=10):
# Ensure password has at least N characters
def check(password):
return len(password) >= n
return check
def lower_case(n=1):
# Ensure password has at least N lower case characters
def check(password):
count = 0
for char in password:
if char.islower():
count += 1
if count == n:
return True
return False
return check
def upper_case(n=1):
# Ensure password has at least N upper case characters
def check(password):
count = 0
for char in password:
if char.isupper():
count += 1
if count == n:
return True
return False
return check
def number(n=1):
# Ensure password has at least N numbers
def check(password):
count = 0
for char in password:
if char.isdigit():
count += 1
if count == n:
return True
return False
return check
# Doesn't have any numbers:
>>> print(check_password('TopSecret'))
False
# Acceptable:
>>> print(check_password('TopSecret15'))
True
# Has lower case, upper case, and number, but it's not long enough:
>>> print(check_password('Ab1'))
False
Notes:
Some parts of this are simplified in order to make it more clear what's going on (all of the check functions could be one-liners).
In a production system, password requirements will almost certainly change over time and you want to make sure that implementing additional requirements is easy.
A regular expression might not be as easy to understand or extend (although it might be a little faster, but that probably doesn't matter at all).
The approach above is somewhat similar to Django's password validation system.
Related
I'm trying to create a function that will identify if 0 is the first number in an alphanumeric sequence; for example, the function should evaluate to True if given the string "J02". I came up with a for loop that would work for inputs that are alphanumeric but not strictly alpha inputs.
x = "J02"
def not_starting_zero(m):
zero_string = []
for char in m:
if char.isdigit() == True:
zero_string.append(char)
if m[len(m)-1] == char:
if zero_string[0] == "0":
return False
else:
return True
not_starting_zero(x)
I tried using an else statement that aligned with the indentation of the if char.isdigit() == True: ; but that would make the return of the function true if the first index of the string is a letter.
You could use a regular expression to find the first digit:
import re
def not_starting_zero(m):
first_digit = re.search(r"([0-9])", m)
if first_digit:
return first_digit.group(0) == "0"
else:
return False
Alternatively, you could use your looping version - I think you can just stop after you encounter the first digit:
x = "J02"
def not_starting_zero(m):
for char in m:
if char.isdigit() == True:
if char == "0":
return True
# otherwise, return False
return False
# if we get here, there weren't any numbers
return False
not_starting_zero(x)
I suppose you could use regex and extract the numeric digits into a list. Something like this -
import re
def not_starting_zero(m):
numbers = re.findall('[0-9]', m)
if numbers[0] == '0':
return false
else
return true
I'm assuming you mean the first digit you find is 0 (because otherwise it should return False for "J02" because 02 is the first number)
x = "J02"
def not_starting_zero(m):
for char in m:
if char.isdigit() == True:
if char == "0":
return True
else:
return False
return False
not_starting_zero(x)
This works because once return is executed in a function the rest of the function is not executed. Hope this clears up your doubt.
You don't need to make an array of the digits because you only need to check the first digit in the string; if its a 0 then return True else return False.
Below code can find if 0 is the first number in alphanumeric string.
import regex as re
x = "J02"
True if re.findall('[0-9]', x)[0] == '0' else False
Output:
True
Take note on type safety, how expressive the code is, and how it instills referential transparency for code correctness (accuracy). All those loops cause too many mutations, and mutations cause errors (bugs). See here for referential transparency.
import re
str1 = 'jdh487y3hef8ty483rhfeih89t4389jf0dwiefh38uth'
str2 = '0dh487y3hef8ty483rhfeih89t4389jfdwiefh38uth'
# check if a given char is first in letter in string
def check_for_char(alpha_num:str, _char) -> bool:
if re.search('0', alpha_num).span(0)[0] == _char:
return True
else:
return False
is_false = check_for_char(
alpha_num=str1,
_char=0
)
is_true = check_for_char(
alpha_num=str2,
_char=0
)
print(is_false) # False
print(is_true) # True
# search if a given char exists at all within the alphanum str
print(str1[re.search('0', str1).span(0)[0]]) # 0
So I wrote this code originally for passwordlength(password) to find whether these criterias are met or not...
def passwordlength(password: str):
upper = digit = special = False
for char in password:
if char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" and "1234567890" and "abcdefghijklmnopqrstuvwxyz":
upper = True
if char in "1234567890":
digit = True
if char in "$##%!":
special = True
if upper and digit and special:
return True
return False
and now I created a new function to find whether the length of the string is within 6 to 12 characters AND whether each element in the string appears lesser than 3 times or not.. and here it is
def passwordOK(password: str):
if passwordlength(password)== True:
if len(password) > 6 and len(password) < 12:
return True
for char in password:
if password.count(char) <= 3:
return True
else:
return False
if passwordlength(password) == False:
return False
print(passwordOK(password='aaaa1jd4kjfs$jDhfksfd'))
When I tried to run this, it didn't work at all.. Can someone please help me find the error and correct me? Thank You:)
The following would resemble some workable logic and code:
def passwordlength(password: str):
upper = digit = special = False
for char in password:
if char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" :
upper = True
elif char in "1234567890":
digit = True
elif char in "$##%!":
special = True
return upper and digit and special
def passwordOK(password: str):
if passwordlength(password):
if not (6 < len(password) < 12):
return False # return False early
for char in password:
if password.count(char) > 3:
return False # again: return False early
return True # only when all chars have been checked, you know it's ok
return False
Of course the naming is still weird. If I were to redo it:
from collections import Counter
from string import ascii_uppercase, digits, punctuation
def characterclass_check(password: str):
for char_class in (ascii_uppercase, digits, punctuation):
if not any(c in char_class for c in password):
return False
return True
def passwordOK(password: str):
if not characterclass_check(password):
return False
if not (6 < len(password) < 12):
return False
return max(Counter(password).values()) <= 3
There are many problems with this code.
First
if char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" and "1234567890" and "abcdefghijklmnopqrstuvwxyz"
will be evaluated as
if (char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ") and ("1234567890") and ("abcdefghijklmnopqrstuvwxyz")
thus only checking if the character is uppercase, as the other strings will evaluate to True simply by having content. The variable name you're setting is upper, so I'm not sure why those other strings are there to start with - they're not what you're trying to check against.
Second, you unnecessarily call passwordlength(password) twice when the second one could (and should) be an else.
Third, you return immediately on checking the password length in passwordOK, so if it's between 6 and 12 characters you never check for duplicate characters.
Fourth, when you check for duplicate characters you return as soon as you find one that appears less than 4 times, meaning that "aaaaaabbbbbbcdddddd" would pass. I assume this was not your intention.
Fifth, your else on the for loop is unnecessary as in your case it will always run if you don't return from within the loop. It's only useful if you have a break.
Sixth, you have a function named passwordlength that doesn't actually check the length of the password.
I may have missed other issues.
This is a bit of a puzzler, and I wanted to get an ideal algorithm for this type of question. The problem is focused on kangaroo words, which are words that have all the same letters in order as the main word. It's easiest to provide an example. If we take the word (which seems to be floating about online for this type of question) - courage.
courage
courage -> core
courage -> cog
Here is working code to detect the lines above:
def areAllCharsInWordInOrder(word, lookup_word):
is_kangaroo_word = True
curr_idx = 0
are_all_letters_consecutive = True
for individual_char in lookup_word:
try:
new_idx = word.index(individual_char, curr_idx)
if new_idx - curr_idx == 1:
are_all_letters_consecutive = are_all_letters_consecutive and True
else:
are_all_letters_consecutive = False
curr_idx = new_idx
except:
return False
if are_all_letters_consecutive:
return False
return True
However, the caveat for the question comes with the fact that the letters may not be consecutive. So if we look at devil and evil, these are not kangaroo words because evil is all in order of devil. However, devilishly and evil would be because: devilishly would match evil.
The nuance comes in that now - I believe - we have to explore every possible matching index to see if it's a valid path. Is this true? Is there a more optimal algorithm? This was my cleanest attempt (lightly tested).
def findAllIndexes(char, curr_idx, word):
return [i for i, ltr in enumerate(word) if ltr == char and i > curr_idx]
def kangarooHelper(lookup_word, lookup_idx, curr_idx, are_all_letters_consecutive, word):
if lookup_idx >= len(lookup_word):
# we're done we've iterated through the whole word
if not are_all_letters_consecutive:
return True
else:
return False
new_indices = findAllIndexes(lookup_word[lookup_idx], curr_idx, word)
if len(new_indices) == 0:
return False
return any(kangarooHelper(lookup_word, lookup_idx+1, new_idx, (new_idx - curr_idx == 1) and are_all_letters_consecutive, word) for new_idx in new_indices)
def areAllCharsInWordInOrderFixed(word, lookup_word):
# Should return false if they're in order
is_kangaroo_word, are_all_letters_consecutive = True, True
lookup_idx = 0
if len(lookup_word) == 0:
return True
try:
curr_idx = word.index(lookup_word[lookup_idx], 0)
except:
return False
return kangarooHelper(lookup_word, lookup_idx + 1, curr_idx, are_all_letters_consecutive, word)
Again, it's been lightly tested, but I'd love to clean up both algo and code.
if __name__ == '__main__':
print(areAllCharsInWordInOrderFixed('encourage', 'urge')) # True
print(areAllCharsInWordInOrderFixed('devil', 'evil')) # False
print(areAllCharsInWordInOrderFixed('devilishly', 'evil')) # True
print(areAllCharsInWordInOrderFixed('encourage', 'nrage')) # True
print(areAllCharsInWordInOrderFixed('encourage', 'rage')) # False
Thanks! Any advice and suggestions would be greatly appreciated.
Here's a regex-based approach to the problem. We form a regex from lookup_word by adding .* between each letter in the word. Then we attempt to match the regex against word. Since .* is inherently greedy, you will get the longest possible match inside word. You can then compare the length of the matched string to the length of lookup_word, and if the matched string is longer, then lookup_word is a kangaroo word:
import re
def areAllCharsInWordInOrderFixed(word, lookup_word):
regex = '.*'.join(lookup_word)
match = re.search(regex, word)
return match is not None and len(match.group()) > len(lookup_word)
print(areAllCharsInWordInOrderFixed('encourage', 'urge')) # True
print(areAllCharsInWordInOrderFixed('devil', 'evil')) # False
print(areAllCharsInWordInOrderFixed('devilishly', 'evil')) # True
print(areAllCharsInWordInOrderFixed('encourage', 'nrage')) # True
print(areAllCharsInWordInOrderFixed('encourage', 'rage')) # False
Output:
True
False
True
True
False
Alternatively you can take an iterative approach. There are two conditions that need to be true for the input to be a kangaroo word:
the letters of the lookup word must be present in the word in order
there must be at least one extra letter between the letters of the lookup word
The first condition can be tested by checking each letter in turn to see that there is an occurrence of it after the previous letter in the word. The last condition can be checked by testing that the first occurrence of the first letter is more than the length of the word away from the last occurrence of the last letter. For example:
def areAllCharsInWordInOrderFixed(word, lookup_word):
first = start = word.find(lookup_word[0])
if first == -1:
return False
for c in lookup_word[1:-1]:
start = word.find(c, start+1)
if start == -1:
return False
end = word.rfind(lookup_word[-1], start+1)
# don't need to check for end == -1 as the next test will fail if it is
return end - first >= len(lookup_word)
The results are the same as the regex version.
Hello guys I hope you will help me with a small problem that I did not figured out yet.
Here is the description of the homework to have an idea first about what I am working on.
" In this part of the homework, you will write a program that inputs two different
characters in the form of "c1-c2", where c1
is the first character and c2
is the second
character. You need to check the validity of this input. That is, the input should
start with a letter, followed by a dash (-), and it should end with another letter case
insensitively (it means that "a" and "A" are the same characters). Please note that
the characters to be obtained from the user must be different from each other. In
case that the user enters an invalid input, your program should continuously
prompt for the required value until (s)he enters a valid one."
Here is some sample runs:
Please enter two different characters in the form c1-c2: a-A
Invalid input!
Please enter two different characters in the form c1-c2: a-B
(The program should stop if it is correct.)
Here is what I tried so far:
ascii_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
def process(Z):
if Z[0] in ascii_letters:
return True
if Z[1] == '-':
return True
if Z[2] in ascii_letters:
return True
if Z[0] != Z[2]:
return True
if Z[0] != Z[2].upper():
return True
if Z[0] != Z[2].lower():
return True
X = False
while X == False:
ask = input("Please enter two different characters in the form c1-c2: ")
if process(ask) == False :
print("Invalid input!")
else:
break
Here your process almost always terminate at the first check:
def process(Z):
if Z[0] in ascii_letters:
return True
You need to nest your condition, and 'return' only when all the conditions are filled, a fast example:
def process(Z):
if Z[0] in ascii_letters:
if Z[1] == '-':
if Z[2] in ascii_letters:
return True
return False
Note you will want to check for that Z is the right size before accessing Z[2]. And you need as well to add the check about same-letter. So a solution for your problem would be:
def process(Z):
if len(Z) == 3
and Z[0] in ascii_letters
and Z[1] == '-'
and Z[2] in ascii_letters
and Z[0].upper() != Z[2].upper():
return True
return False
Strings have methods you can use to validate the input. Use them! You can split on '-' and verify you have 2 characters of length 1, check whether they are alphabetic and compare the upper case version of each.
def process(Z):
parts = Z.split("-")
return (len(parts)==2 and len(parts[0])==1 and len(parts[1])==1
and parts[0].isalpha()
and parts[0].upper() == parts[1].upper())
isalpha works for all unicode characters so you don't have to worry whether the user entered text in some strange language.
The inbuilt function -isalpha() will be helpful for your homework.
while True:
ask=input('Please enter two different characters in the form c1-c2: ')
if len(ask)!=3:
continue
if ask[1] == '-' and ask[0].isalpha() and ask[2].isalpha() and ask[0]!=ask[2]:
break
The isalpha() function returns True if the given string has all alphabets.
The correct way to handle this is with regular expressions. Their is a regex library as part of the standard python library.
import re
u_input = 'your string'
def validate(input):
# chars must be different
if input[0] == input[-1:]:
return False
# begin and end with upper or lower a-z with dash in middle
pattern = '[a-zA-Z]{1}-[a-zA-Z]{1}'
result = re.match(pattern, input)
# re.match returns None if no match
if not result:
return False:
else:
return True
I tried to make it as similar to your code as possible and it's quite simple to understand. I think I considered all required conditions. If not, tell me.
from string import ascii_letters
def validity_check(input_chars):
try:
# if cant be splitted raises Exception which results in restart
char_1, char_2 = input_chars.split("-")
# if characters not in alphabet restarts
chars_in_alphabet = char_1 in ascii_letters and char_2 in ascii_letters
if not chars_in_alphabet:
return False
# if characters are the same letter restarts
same_latter = char_1.lower() == char_2.lower()
if same_latter:
return False
# I'm not sure if you want to check if the first letter
# is small and the second one is capital
# if so add this block
# chars_letter_size = char_1 in ascii_letters.lower() and char_2 in ascii_letters.upper()
# if not chars_letter_size:
# return False
return True
except:
return False
while True:
ask = input("Please enter two different characters in the form c1-c2: ")
if validity_check(ask):
break
I'm trying to write a function that checks if given string is binary or not. I'm using a while loop to browse characters in string one by one. If all of them are 0's or 1's it's going to return True, if anything is not - break the loop and return False.
I've just written this tiny part of code, but it doesn't run. Why?
def fnIsBin(string):
count = 0
while count < len(string):
character = string[count]
if character == '0' or character == '1':
print (count, character[count], "OK")
count = count+1
continue
else:
print (count, character[count], "ERROR")
return False
break
EDIT:
I also tried using 'set' to elude iterating with loop, but i don't quite get how does "set(string)" work. I got error that i cant consider it as a list. So how can I compare elements to 0 & 1?
def fnIsBin(string):
charactersList = set(string)
if len(charactersList) > 2:
return False
else:
if (charactersList[0] == '0' or charactersList[0] == '1') and (charactersList[1] == '0' or charactersList[1] == '1'): #there i made error, how to deal with it?
return True
else:
return False
Your function fails because you never actually return True. That means if the string is actually binary, you'd return None which Python would consider as False in any boolean check.
Simply add return True at the end of the function.
As #Barmar mentioned in the comment, you also print the value of character[count] instead of string[count] which would cause IndexError.
An easier way to check if the string is binary would be:
test_string = '010101'
all_binary = all(c in '01' for c in test_string)
There are many ways to do this:
Set:
fnInBin(s):
return set(s).issubset({'0', '1'}) and bool(s)
Regular expression:
fnInBin(s):
return bool(re.fullmatch('[01]+', s))
Integer conversion:
fnIsBin(s):
try:
int(s, 2)
return True
except ValueError:
return False
The last one will strip whitespace from the input, though.
fnIsBin('\n 1 ') == True
You can use the int object and give it a base. It will fail if the object passed doesn't consist of a binary representation. Recall that a binary string is base 2.
def fnIsBin(string):
try:
binary_repr = int(string, 2)
except ValueError as err
print("error: {0}".format(err))
return False
return True
You're printing OK for each character that's 0 or 1, even though there may be a later character that isn't. You need to wait until the end of the loop to know that it's really OK.
def fnIsBin(string):
for character in string:
if character != '0' and character != '1':
print (character, "ERROR")
return False
print "OK"
return True
There's no need for break after return False -- returning from the function automatically breaks out of the loop.
Here's one of the ways you could rewrite your function and maintain a similar structure (looping through each character):
def fnIsBin(string):
for character in string:
if not character in '01':
return False
return True
You could also do it using regular expressions:
import re
def fnIsBin(string):
return re.match(r'^[10]+$', '01') is not None
My two cents (A short way):
test_string = '010101'
result = set(test_string) <= {'0','1'}
print(result)
Using set(test_string) converts the string into list of characters,
{0,1,0,1,0,1}
Using <= operator checks whether the set on the left-side is a proper subset of the set on the right-hand side
Ultimately checking whether there are only 0 and 1 are in the string or not in an in-direct and mathematical way.
Adding a XOR solution to the mix:
bin_string = '010101'
for j in range(0, len(bin_string)):
if int(bin_string[j]) != 0 ^ int(bin_string[j]) != 1:
break