How to make Python differentiate between upper and lower characters. - python

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

Related

checking if string has two or more uppercases

I'm writing a function that checks multiple conditions for a string (a "password"). This string has to be between 6 and 20 characters long, must have at least one number, must have special characters AND must have at least two uppercase letters.
This is where I ran into problems because I'm using RegEx and I can't get the "must have two uppercase letters" part to work, everything else works fine.
import re
password = input("Input password: ")
def valid_pw(password):
valid = True
if not re.search(',{6,20}', password):
valid = False
if not re.search('[0-9]', password):
valid = False
if not re.search('[A-Z].*[A-Z]', password):
valid = False
if not re.search('[$&+,:;=?##|<>.^*()%!-]', password):
valid = False
return valid
print(password, "is valid: ", valid_pw(password))
The third "if" statement is my attempt at checking for two uppercase letters but doesn't work as expected. If the input is password: AbC.123 the output should be "AbC.123 is valid: True" since it checks every condition, yet I'm still getting "False" due to the two uppercase part.
None of your checks require regular expressions to implement.
def valid_pw(password):
if not 6 <= len(password) <= 20:
return False
if not any(c.isdigit() for c in password):
return False
if sum(c.isupper() for c in password) < 2:
return False
if not any(c in '[$&+,:;=?##|<>.^*()%!-]' for c in password):
return False
return True
Notice that once any of the conditions returns False, you can stop searching: the password won't become true after that.
In your original formulation, elif would be preferable to if for all the latter clauses. Also, the symbol . means "any character", not ,. Your checks were likely failing due to that, and not the capitalization check. You may have failed to realize that because all the checks were running instead of terminating early.
A slight improvement to your code snippet would be to start with a 'false' and then check for each condition consequently and finally set the valid to 'true'. You can use regex search for special characters and numbers.
Use len() for checking the length parameter, and in a loop (one liner is good) check for upper case in the string (ie looping through every character of the string).
Working code below.
import re
password_str = "Abc#123"
def validate_pw(pwd):
valid = False
if len(pwd) > 6 & len(pwd) <= 20:
if re.search(r'\d', pwd):
regexp = re.compile('[^0-9a-zA-Z]+')
if regexp.search(pwd):
if (any(x.isupper() for x in pwd)):
valid = True
print(valid)
validate_pw(password_str)

When I run the code, it is having an issue with ':' after False in my if statements. idk why because the example has it the same. line 14 and down

def main():
plate = input("Plate: ")
if is_valid(plate):
print("Valid")
else:
print("Invalid")
def is_valid(s):
#vanity plates may contain a maximum of 6 characters (letters or numbers) and a minimum of 2 characters.
if 2 > len(s) > 6:
return False
#All vanity plates must start with at least two letters.
if s[0].isalpha() == False or s[1].isalpha() == False:
return False
#Numbers cannot be used in the middle of a plate; they must come at the end.
#The first number cannot be 0
i = 0
while i < len(s):
if s[i].isalpha() == False:
if s[i] == '0':
return False
else:
break
i += 1
#No periods, spaces, or punctuation marks are allowed.
if s.isalpha() == False or s.isinstance() == False:
return False
#passes all requirements
return True
main()
The regular expression module provides an easy way to approach what you intend to achieve with the code provided in your question. The code below should do what you intend to achieve, so try it out:
import re # regular expressions module
# All vanity plates must start with at least two letters.
# Numbers cannot be used in the middle of a plate; they must come at the end.
# The first number cannot be 0
rexp = re.compile('^[a-zA-Z]{2,6}([1-9][0-9]*)?')
def main():
plate = input("Plate: ")
# e.g. plate = "plat01"
if is_valid(plate):
print("Valid")
else:
print("Invalid")
def is_valid(s):
l = len(s)
#vanity plates may contain a maximum of 6 chars (letters or numbers)
# and a minimum of 2 characters.
if 2 > l or l > 6:
return False
if rexp.match(s).span() == (0,l):
return True
return False
main()
P.S. Check out https://regex101.com/r/LNw2pW/1 for detailed explanations of the used regular expression pattern. The 'trick' is to check if the found match length is the same as the length of the input string. If yes, than the input string is valid.
From the code above I see that you use isinstance() incorrect.
Try changing s.isinstance() == False to isinstance(s, type) == False, and choose the type you want to check (int, str.. etc)
Reference:
https://www.w3schools.com/python/ref_func_isinstance.asp

Why does the execution change after adding second half of the code?

I am facing difficulty while writing a code for testing for a pangram (a string that contains all 26 alphabets at least once).
When executed based on first part as follows:
def ispangram(x):
alphabet="abcdefghijklmnopqrstuvwxyz"
for i in alphabet:
if i in x.lower():
return True
The code works fine.
But if I add the else condition:
def ispangram(x):
alphabet="abcdefghijklmnopqrstuvwxyz"
for i in alphabet:
if i in x.lower():
return True
else i not in x.lower():
return False
The code returns every input as a valid pangram.
Could someone please help me understand what is going wrong here?
You aren't checking every letter in the alphabet. You are only checking if the word contains the letter a. Let's examine the control flow
def is_pangram(x):
x = x.lower()
alphabet="abcdefghijklmnopqrstuvwxyz"
# we begin iteration here
for i in alphabet:
# we check if the letter is in the word
if i in x:
# we return! This will stop execution at the first True
# which isn't always the case
return True
else:
# we return again! since we've covered all cases, you will
# only be checking for a
return False
To fix this, you can do one of two things. Using a loop, you can just check if letters are not in x, returning False if they are not, and returning True at the end:
def is_pangram(x):
x = x.lower()
alphabet="abcdefghijklmnopqrstuvwxyz"
for i in alphabet:
if i not in x:
return False
# this means we made it through the loop with no breaks
return True
Alternatively, you can use the all operator to check if all of the letters in the alphabet are in the word, which returns True, otherwise it returns False
def is_pangram(x):
x = x.lower()
alphabet="abcdefghijklmnopqrstuvwxyz"
return all(i in x for i in alphabet)
Your first function will return true for any string that contains any letter. You need to verify that each letter is in your input string:
import string
def ispangram(x):
x = x.lower()
for c in string.ascii_lowercase:
if c not in x:
# a letter is not in the input string, so the input string is not a pangram
return False
# if we got here, each letter is in the input string
return True

Checking a password

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.

Python, checking if string consists only of 1's and 0's

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

Categories

Resources