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
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.