I'm working on learning by making a hangman game and I'm trying to validate user input to require a user enter a five character word, with no spaces, special characters or numbers.
Everything worked until I defined the containsspec() function and tried to implement it.
Here's the main game file, and then the file it's calling which contains the functions.
#text-based hangman project
#import defined functions from fun.py
from fun import *
#get player1 input and validate that it's at least five characters long, with no numbers or spaces
print("Please select any word with five characters or more:")
c = 0
while c == 0:
w = getpass.getpass(prompt = "")
w = list(w)
if len(w) < 5 or containsnumber(w) or containsspace(w) or containsspec(w):
print("Your input is invalid. Please try again.")
else:
print("Got it - thank you!")
c += 1
File that contains the functions:
#contains functions for 1.py
import getpass
def containsnumber(value):
for character in value:
if character.isdigit():
return True
return False
def containsspace(value):
for character in value:
if character == " ":
return True
return False
def containsspec(value):
for character in value:
if character.isalnum():
return False
return True
So if w contains a character that isn't alphanumeric, it should return True, and the main game should print "Your input is invalid. Please try again. Right?
I'm just super confused about why this isn't working. I'm learning, so I'm not interested in hearing about how I could change the whole code. I'm really interested in just why containsspec() isn't working.
Try this:
def containsspec(value):
return not all(character.isalnum() for character in value)
I believe your issue is that you are immediately returning false when you detect an alphanumeric character, which isn't what you want because you need to make sure every letter is alphanumeric.
The code above applies a boolean .isalnum() to each character in value, and if every character is alphanumeric, it will return False (indicating that it does not contain special characters), else it will return True. Notice the not! That is important!
You could also make it say any(not character.isalnum() for character in value) -- the two are equivalent.
Related
Im very new to python and I am creating a user login system, I am currently on a bit of creating a username and password with user input that must meet some conditions e.g
username:
Cannot contain any spaces
Must be at least 5 characters
Cannot include special characters
Your system must display a message to the user telling them what they did wrong if they did not meet
one or more of these criteria (so you will need at least 4 error messages).
My code is as below, but surely theress a better way to do this?
while True:
sNewUser1 = input("""Please enter a new username.
The username must NOT contain any spaces, it must have at least 5 characters and
it cannot include any special characters: \n\n""")
if len(sNewUser1) < 5:
print("Your username is too short, please enter 5 or more characters! Please try again!\n")
elif sNewUser1.count(" ") > 0:
print("Your username contains one or more spaces, this is not allowed! Please try again! \n")
elif sNewUser1.isalnum() == False:
print("Your username contains a special character please try again! \n")
else:
greetuser()
break
while True:
sNewPass1 = input("""\n\nPlease enter a new password.
It must contain:
At least one Capital letter
At least one lower case letter
At least one special character
It has to be at least 6 characters in length:\n\n""")
if len(sNewPass1) < 6:
print("Your username is too short, please enter 5 or more characters! Please try again!\n")
Input prompts can be shortened and more direct.
The whole code should be wrapped in a main() function which is called at the end using the if __name__ == "__main__" condition. This is a convention.
Use is with boolean values, in those conditional statements.
Use the snake_case naming style.
The long strings can be broken into multiple smaller ones.
By now, linters and code-formatters (even Pylint) will not complain about anything.
Your complete code may look something like this, although there is still room for improvement:
"""This is a program for validating usernames and passwords"""
def main():
"""This is the main function"""
while True:
new_user_1 = input("\nPlease enter a new username. "
"It should be at least 5 characters long "
"and not contain spaces or special characters: ")
if len(new_user_1) < 5:
print("Your username is too short. Please try again: ")
elif new_user_1.count(" ") > 0:
print("Your username contains spaces. Please try again: ")
elif new_user_1.isalnum() is False:
print("Your username contains a special character. "
"Please try again: ")
else:
# call another function
break
while True:
new_pass_1 = input("\nPlease enter a new password. "
"It should be 6 characters long "
"with atleast one uppercase letter, "
"one lowercase letter, and one special character: ")
if len(new_pass_1) < 6:
print("\nYour password is too short. Please try again: ")
elif any(lower.islower() for lower in new_pass_1) is False:
print("\nYour password does not contain lowercase letters. "
"Please try again: ")
elif any(upper.isupper() for upper in new_pass_1) is False:
print("\nYour password does not contain uppercase letters. "
"Please try again: ")
elif any(digit.isdigit() for digit in new_pass_1) is False:
print("\nYour password does not contain digits. "
"Please try again: ")
elif any(not char.isalnum() for char in new_pass_1) is False:
print("\nYour password does not contain special characters. "
"Please try again: ")
elif new_pass_1.replace(" ", "") != new_pass_1:
print("\nYour password contains whitespaces. "
"Please try again: ")
else:
# call another function
break
if __name__ == "__main__":
main()
Edit: The previous answer had some bugs. The new one works as intended:
Strings were broken down into smaller ones, but spaces in the end were ommitted.
Simple for loops were used, which checked whether the whole string was uppercase, lowercase, digits, special characters or not, instead of checking each character in the string. The new code fixes this with the use of a built-in function called any. From Python's official documentation:
any(iterable):
Return True if any element of the iterable is true. If the iterable is empty, return False. Equivalent to:
def any(iterable):
for element in iterable:
if element:
return True
return False
Here is an explanation of why/how this works:
elif any(lower.islower() for lower in new_pass_1) means: for any lower in new_pass_1, if lower.islower() returns True, then: code goes here. Therefore, it returns True if any character in the string is lowercase. So if it returns False, it would mean that it did not find any lowercase character in the whole string. The same applies to the checks for uppercase letters and digits.
elif any(not char.isalnum() for char in new_pass_1) means: for any char in new_pass_1, if char.isalnum() does not return True (i.e. the char is a special character), then: code goes here. Therefore, it returns True if any character in the string is a special character (i.e not an uppercase letter, lowercase letter or digit). So if it returns False, it would mean that it did not find any special character in the whole string.
If you're confused about not char.isalnum(): any_string.isalnum() checks whether any_string is alphanumeric or not, i.e whether it is made up of alphabets and numbers only. So inverting it with not char.isalnum() would now check whether any_string is not alphanumeric. And we know that if something is not alphanumeric, it is a special character. Note that this will consider whitespaces to be special characters as well, so I've added a final if statement to check for whitespaces.
For example, "hello!!" should return true, whereas "45!!","!!ok" should return false. The only case where it should return true is when the string has English characters (a-z) with 0 or more exclamation marks in the end.
The following is my solution using an iterative method. However, I want to know some clean method having fewer lines of code (maybe by using some Python library).
def fun(str):
i=-1
for i in range(0,len(str)):
if str[i]=='!':
break
elif (str[i]>='a' and str[i]<='z'):
continue
else:
return 0
while i<len(str):
if(str[i]!='!'):
return 0
i+=1
return 1
print(fun("hello!!"))
Regex can help you out here.
The regular expression you're looking for here is:
^[a-z]+!*$
This will allow one or more English letters (lowered case, you can add upper case as well if you'll go with ^[a-zA-Z]+!*$, or any other letters you'd like to add inside the square brackets)
and zero or more exclamation marks at the end of the word.
Wrapping it up with python code:
import re
pattern = re.compile(r'^[a-z ]+!*$')
word = pattern.search("hello!!")
print(f"Found word: {word.group()}")
I'm fairly new to Python (and programming in general), so I often end up facing really silly issues, such as the one below.
What I want is to repeatedly check if all the characters in a user input are symbols. When such an input is entered, I want to print that string.
Since there doesn't seem to be a way to specifically test for symbols, I decided to test for letters first, and then for numbers, and if both of them come up as negative, then it should print the text.
Here is my code:
while True:
symbols = input("Enter symbol string:")
if symbols == symbols.isalpha():
print (symbols.isalpha())
elif not a == a.isalpha():
print (a.isalpha())
break
Symbols is a little vague but here is a strategy:
symbols = input("Enter symbol string:")
valid_symbols = "!##$%^&*()_-+={}[]"
has_only_symbols = True
for ch in symbols:
if ch not in symbols:
has_only_symbols = False
break
if has_only_symbols:
print('Input only had symbols')
else:
print('Input had something other than just symbols')
The above code first creates a list of symbols which you want to ensure the string is created out of called valid_symbols. Next it creates a variable called has_only_symbols that holds a value of True to begin with. Next it begins to check that each character in the input string exists in valid_symbols. If it hits a character which is invalid then it changes the value of has_only_symbols to False and breaks out of the for loop (no need to check the rest of the string). After the loop is done it checks whether has_only_symbols is True or False and does something accordingly.
Also as a side not, some of your code is not doing what you think it is:
if symbols == symbols.isalpha():
...
will test if symbols, your input string, is equal to the result of symbols.isalpha() which returns a boolean True or False. What you probably meant is just:
if symbols.isalpha():
...
The elif statement is strange as well. You have begun referencing some variable called a but you do not have it defined anywhere in the code you posted. From your description and code it seems you meant to have this elif statement also reference symbols and call the isdigit method:
if symbols.isalpha():
...
elif symbols.isdigit():
...
else:
...
However this is not logically complete as a string with mixed letter, digit, and symbol will slip through. For example abc123!## will fail both the tests and get printed. You want something more exclusive like the above code I have written.
This is how i solved it..
import re
def start():
global count
for letter in SYMBOLS:
if re.search(reg,SYMBOLS):
count=count+1#just to count how many exist
global S#if you want to store the result
S=letter
else:
print(letter,': is not a symbol')
count = 0
SYMBOLS= input('Enter text\n')#INPUT
reg =('[#_!#$£%^&*()<>?/\|}{~:]')#SYMBOL SEARCH
start()
Slightly updated version of Farmer Joe's code. Better processing of input included.
symbols = input("Enter any characters:")
valid_symbols = "!##$%^&*()_-+={}[]"
if symbols == '':
print("None of characters have been entered. Please try again")
else:
for ch in symbols:
if symbols.isalnum() is True:
print('No symbols have been detected in the input')
break
else:
if ch not in valid_symbols:
print('There are symbols mixed with other characters')
break
else:
print('Your input contains symbols only')
so I'm quite new to programming and I'm trying to learn python as a starter.
I'm trying to make a function that does multiple things (I'm going to use it for limiting inputs on names).
Rejects purely numerical inputs
Rejects inputs made purely of spaces
Rejects null inputs
Changes the input into a title
def debugstr(inputa):
inputa = inputa.replace(" ", "")
try:
int(inputa)
inputb = debugstr(input("Invalid input, please enter Alphabetic strings only: "))
except:
if inputa == "":
debugstr(input("Invalid input, please enter Alphabetic strings only: "))
else:
return inputa.title()
The issue that I have is that the code will only reject blank inputs on the first try when running the function, if something is rejected once and the user inputs a series of spaces again, then it will just accept it as an input.
Thanks for your time in advance! It's very appreciated :D
A more natural way of handling this (without calling the same function from within itself) is:
def make_title():
def get_user_input():
return input('Enter an alphabetic string: ')
while True:
s = get_user_input()
s = s.strip()
if not s:
print('blank input!')
continue
if s.isdigit():
print('contains only digits!')
continue
return s.title()
print(make_title())
Some notes:
Try not to repeat yourself (e.g. the duplicated error message in your code)
Python contains many useful string methods and s.isdigit() returns True if s contains only numbers
You can strip the whitespace from your input with s.strip() and if you're left with the empty string, '', if not s will be True (the empty string is equivalent to False.
In python 3, you can use isinstance to check if an object is a string.
word = input("Enter string: ")
def checkString(s):
if isinstance(s, str):
print('is a string')
elif not s:
print('empty')
else:
print('not a string')
I'm writing a function that will take a word as a parameter and will look at each character and if there is a number in the word, it will return the word
This is my string that I will iterate through
'Let us look at pg11.'
and I want to look at each character in each word and if there is a digit in the word, I want to return the word just the way it is.
import string
def containsDigit(word):
for ch in word:
if ch == string.digits
return word
if any(ch.isdigit() for ch in word):
print word, 'contains a digit'
To make your code work use the in keyword (which will check if an item is in a sequence), add a colon after your if statement, and indent your return statement.
import string
def containsDigit(word):
for ch in word:
if ch in string.digits:
return word
Why not use Regex?
>>> import re
>>> word = "super1"
>>> if re.search("\d", word):
... print("y")
...
y
>>>
So, in your function, just do:
import re
def containsDigit(word):
if re.search("\d", word):
return word
print(containsDigit("super1"))
output:
'super1'
You are missing a colon:
for ch in word:
if ch.isdigit(): #<-- you are missing this colon
print "%s contains a digit" % word
return word
Often when you want to know if "something" contains "something_else" sets may be usefull.
digits = set('0123456789')
def containsDigit(word):
if set(word) & digits:
return word
print containsDigit('hello')
If you desperately want to use the string module. Here is the code:
import string
def search(raw_string):
for raw_array in string.digits:
for listed_digits in raw_array:
if listed_digits in raw_string:
return True
return False
If I run it in the shell here I get the wanted resuts. (True if contains. False if not)
>>> search("Give me 2 eggs")
True
>>> search("Sorry, I don't have any eggs.")
False
Code Break Down
This is how the code works
The string.digits is a string. If we loop through that string we get a list of the parent string broke down into pieces. Then we get a list containing every character in a string with'n a list. So, we have every single characters in the string! Now we loop over it again! Producing strings which we can see if the string given contains a digit because every single line of code inside the loop takes a step, changing the string we looped through. So, that means ever single line in the loop gets executed every time the variable changes. So, when we get to; for example 5. It agains execute the code but the variable in the loop is now changed to 5. It runs it agin and again and again until it finally got to the end of the string.