Checking input and replacing text
A program can't trust its input. A user types a time as 9.30, a phone number with spaces, or their name where a number should go. A pattern describes exactly what's acceptable, and re.fullmatch checks the whole input against it. A loop asks again until the input fits:
import re
while True:
time = input("Time (HH:MM): ")
if re.fullmatch(r"([01]\d|2[0-3]):[0-5]\d", time):
break
print("Please use the form HH:MM, such as 09:30.")
print("Alarm set for", time)
9.30
24:00
07:45Time (HH:MM): 9.30
Please use the form HH:MM, such as 09:30.
Time (HH:MM): 24:00
Please use the form HH:MM, such as 09:30.
Time (HH:MM): 07:45
Alarm set for 07:45Read the pattern in parts. The hour is either 0 or 1 followed by any digit, or 2 followed by a digit from 0 to 3. Then comes a colon. The minutes are a digit from 0 to 5 followed by any digit. A regular expression sees characters, not numbers, so a range of numbers such as 0 to 23 has to be spelled out digit by digit.
A check that you need in several places fits in a function that returns True or False:
def is_valid_time(text):
return re.fullmatch(r"([01]\d|2[0-3]):[0-5]\d", text) is not None
Replacing with re.sub
re.sub(pattern, replacement, text) returns a new string in which every match of the pattern is replaced with the replacement. It's handy for tidying up text:
>>> import re
>>> re.sub(r"\s+", " ", "too many spaces")
'too many spaces'
>>> re.sub(r"[^0-9]", "", "+1 (555) 010-0199")
'15550100199'
>>> re.sub(r"\d", "*", "PIN 4821, card 7734")
'PIN ****, card ****'
>>> re.sub(r"colou?r", "hue", "Color? Pick a colour.")
'Color? Pick a hue.'The first line squeezes every run of whitespace into one space. The second removes everything that isn't a digit, by replacing it with the empty string. The last one leaves Color alone, since patterns are case sensitive. Like the string methods, re.sub never changes the original string: strings can't be changed, so it returns a new one.
Tidying and checking work well together. This function removes the spaces, brackets and dashes people like to type in phone numbers, and then checks what's left:
import re
def clean_phone(text):
digits = re.sub(r"[ ()-]", "", text)
if re.fullmatch(r"\+?\d{7,15}", digits):
return digits
return None
for number in ["+358 40 123 4567", "(555) 010-0199", "call me"]:
print(clean_phone(number))
+358401234567
5550100199
NoneThe class [ ()-] lists a space, two round brackets and a dash. Inside square brackets, round brackets are ordinary characters, and the dash is an ordinary dash because it comes last. In the middle of the class, as in [ -)], it would make a range from the space to ), which takes in ! and # too, and the dash itself would no longer be in the class.