1
\$\begingroup\$

I have written this regex to validate an email. It seems to work fine. Can someone advise on the regex used? Does it work properly or can be done in a better way?

pattern=r"^[A-Za-z]+[-_$.A-Za-z]*@[A-Za-z]*\.[A-Za-z]+$"
x=re.search(pattern,"[email protected])
print x

Note: I have used x and print as Code Review needs minimum 3 lines of code.

\$\endgroup\$
3
  • \$\begingroup\$ Did you test it? Does it appear to work at least? \$\endgroup\$
    – Mast
    Sep 21, 2020 at 4:04
  • \$\begingroup\$ Yes @Mast it seems to work for me \$\endgroup\$
    – gss
    Sep 21, 2020 at 4:37
  • \$\begingroup\$ What is your goal: just learning, need to correctly validate common email addresses, or need to validate all legal email addresses? If the latter, that's a tougher problem: stackoverflow.com/questions/201323 \$\endgroup\$
    – FMc
    Sep 21, 2020 at 7:17

2 Answers 2

4
\$\begingroup\$

That regex will not properly validate the format of an email address. Many web sites get it wrong when validating email addresses.

According to RFC5322, the local part of an address (the part before the @) can contain one or more text strings separated by .s. The test strings can include letters, numbers, and any of these characters: ! # $ % & ' * + - / = ? ^ _ { | } ~ " or a back tick. Alternatively, the local part can be a quoted string. There can be comments in () and possibly whitespace in some places.

That's just for the local part using only US-ASCII characters. There are other RFCs that describe the domain (after the @) and addresses with non-ASCII (e.g., Unicode) characters.

\$\endgroup\$
2
\$\begingroup\$

When you don't care about casing, rather than repeating A-Za-z multiple times, it can be preferable to use the case-insensitive flag; this makes the pattern terser and more readable, as well as less error-prone.

It would also be good to use informative variable names; the variable name x is not very useful. Maybe call it match instead.

You can also consider putting spaces between operators (like =) and arguments (after a ,), to make the code more readable.

You can also consider using re.match instead of re.search; re.match will produce a match only if the pattern matches starting at the beginning of the input string. (In contrast, re.search will permit a match anywhere within the input string.) While this won't alter the meaning of your code since you're using ^, it can make the code a bit more readable when a reader can see at a glance, without even looking at the pattern, "Oh, you're using .match, so the pattern is intended to match from the start of the string."

pattern = r"^[a-z]+[-_$.a-z]*@[a-z]*\.[a-z]+$"
match = re.match(pattern, "[email protected]", re.IGNORECASE)

Another way, rather than re.IGNORECASE, is to put an inline modifier at the beginning of the pattern:

pattern = r"(?i)^[a-z]+[-_$.a-z]*@[a-z]*\.[a-z]+$"

That's how I'd refactor your current code and its logic - but, as has been said, true email address validation is much more complicated.

On a completely different note, I notice this is Python 2, which is end-of-life and no longer supported by bug fixes, security improvements, etc. Upgrading may not be possible in certain codebases, but consider changing to Python 3 if at you can.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.