PatternRegex

Match a phone number

Find phone numbers in text without fighting every national format: an optional +, then digits with the separators people actually type.

Last updated

Patterng
\+?\d[\d\s().-]{7,}\d
Open in Regex Tester & Builder

Matches

  • +91 90000 00000
  • (022) 2000-0000
  • 9876543210

Doesn’t match

  • 12345
  • 1-800
  • call me maybe

Why it’s written this way

Phone formats differ by country, by app and by the person typing — so instead of encoding one format, this encodes the shape all of them share: an optional +, a digit at each end, and 7-plus digits, spaces, dots, dashes or parentheses in between.

Anchoring both ends on \d stops the match from swallowing the trailing punctuation of a sentence, which is the usual failure of looser phone patterns.

Edge cases to know

  • →It is a finder, not a validator — 999 999 9999 matches. Real validation needs a library that knows national numbering plans (libphonenumber) or an SMS to the number.
  • →Short service numbers (100, 1930) are below the length floor by design; lower {7,} if you need them.
  • →Long digit runs that aren't phones — order ids, timestamps — match too. Filter by context words (call, phone, mob) if your text mixes them.

Part by part

\+?
An optional leading plus for international format. Escaped because a bare plus means "one or more of the previous thing" in regex.
\d
A required first digit, so the match cannot begin on a bracket or a space.
[\d\s().-]{7,}
The body: digits mixed with the separators people actually type — spaces, brackets, dots and hyphens. At least seven characters, which is the loosest credible minimum.
\d
A required final digit, which stops the match running on into trailing punctuation.

Common mistakes

  • \d{10}

    Ten digits describes one country's format and nothing else. It rejects every number written with spaces or brackets, and every international number, which between them is most of the world.

    \+?\d[\d\s().-]{7,}\d
  • \+?[\d\s().-]{7,}

    Without a required digit at each end, the match can start and finish on punctuation, so a run of brackets and dashes in ASCII art registers as a phone number.

  • ^\+?\d[\d\s().-]{7,}\d$

    Anchoring is right for validating a form field and wrong for scanning a document, and the anchored form is often copied into a scanning context where it then matches nothing at all.

Questions

Does a match mean the number is real?

No, and it is worth being blunt about this: the pattern checks shape, not validity. It cannot tell you the country code exists, the area code is assigned, or the line is connected. Only a lookup service or an actual call can.

Why is this pattern so permissive?

Because phone number formats vary enormously and a strict pattern rejects real numbers, which is the more damaging error in a signup form. Permissive matching plus a normalisation step afterwards beats a strict pattern that turns customers away.

How should I store the result?

Strip everything except digits and any leading plus, and store the E.164 form — a plus, the country code, then the national number with no spaces. Keeping the user's original formatting for display is fine, but comparisons and lookups should use the normalised value.

Is there a better approach than a regular expression?

For anything where the number matters, yes: a library such as libphonenumber knows each country's numbering plan and can tell a mobile from a landline. Use this pattern to find candidates in text, and a library to decide what they are.

Related in Patterns