The short answer, then the honest context: the pattern below catches typos, and a confirmation email catches everything else. That combination is what production systems actually do.
🎙️ Published & recorded: ·
This pattern checks the useful shape: some allowed characters, one at sign, a domain, and a suffix after a dot. It is deliberately a typo filter, not a proof of delivery. Match the whole input so a valid-looking fragment cannot rescue surrounding junk.
^[\w.+-]+@[\w-]+\.[\w.-]+$
# read it: one-or-more of [letters/digits/_/./+/-] @
# domain chunk . rest of domain (dots allowed)
# anchored ^...$ because validation must match the WHOLE stringTrim accidental outer whitespace, then run the same whole-string check in either language. Keep a short test list beside the pattern. Positive tests protect real addresses such as tagged inboxes, while negative tests catch missing pieces, spaces, doubled separators, and extra at signs.
# Python
import re
EMAIL_RE = re.compile(r"^[\w.+-]+@[\w-]+\.[\w.-]+$")
def looks_like_email(s):
return bool(EMAIL_RE.match(s.strip()))
// JavaScript
const EMAIL_RE = /^[\w.+-]+@[\w-]+\.[\w.-]+$/;
const looksLikeEmail = s => EMAIL_RE.test(s.trim());✓ should match:
[email protected]
[email protected]
[email protected]
✗ should NOT match:
ada@example (no TLD)
@example.com (no local part)
ada @example.com (space)
ada@@example.com
"ada"@example..com[email protected] is valid and deliberate. People use tags to sort mail and identify leaks. A form that says “Please enter a valid email address” for a tagged address is broken; keep the plus sign in the allowed set.Do not build the enormous standards-perfect pattern. Mail systems disagree at the edges, and syntax cannot tell you whether an inbox exists. Use the short check to catch obvious mistakes, then send a confirmation message. When finding addresses inside prose, use the same core shape without requiring it to occupy the whole string.
# unanchored — finding, not validating:
[\w.+-]+@[\w-]+\.[\w.-]+
re.findall(r"[\w.+-]+@[\w-]+\.[\w.-]+", big_text)
# validation = anchored ^...$. extraction = unanchored. never mix.noise [email protected] noise, it is searching instead of validating. Require a full-string match. If an extractor returns nothing from a paragraph, check that you did not reuse the anchored validation pattern.