19

I would like to get the phone numbers from a file. I know the numbers have different forms, I can handle for a single one, but don't know how to get a uniform regex. For example

  1. xxx-xxx-xxxx

  2. (xxx)xxx-xxxx

  3. xxx xxx xxxx

  4. xxxxxxxxxx

I can only handle 1, 2, and 4 together

grep '[0-9]\{3\}[ -]\?[0-9]\{3\}[ -]\?[0-9]\{4\}' file

Is there any one single regex can handle all of these four forms?

Alan Moore
  • 73,866
  • 12
  • 100
  • 156
skydoor
  • 25,218
  • 52
  • 147
  • 201
  • You would have to handle 2 separately via alternation (|). The issue is that using basic regex there's no way to tell whether the parens are balanced otherwise. – Joel Feb 15 '10 at 23:32
  • Check out Regexr for regex help... http://www.gskinner.com/RegExr/ – Moshe Feb 16 '10 at 01:10

11 Answers11

19
grep '\(([0-9]\{3\})\|[0-9]\{3\}\)[ -]\?[0-9]\{3\}[ -]\?[0-9]\{4\}' file

Explanation:

([0-9]\{3\}) three digits inside parentheses

\| or

[0-9]\{3\} three digits not inside parens

...with grouping parentheses - \(...\) - around the alternation so the rest of the regex behaves the same no matter which alternative matches.

Alan Moore
  • 73,866
  • 12
  • 100
  • 156
  • 1
    The [slight] problem this RegEx is, it also matches a number with more than 4 digits in the last part, e.g. 123-123-12345 or a number with more than 10 digits in it. This: `grep '\(\(([0-9]\{3\})\|[0-9]\{3\}\)[ -]\?\)\{2\}[0-9]\{4\} '` should handle that nicely. See my reply below for explanation. Cheers!! – MacUsers Apr 07 '13 at 11:23
  • @MacUsers: Good point. The OP only asked how to get the regex to match everything it should match, and I answered that. Getting it to *not* match the things it shouldn't is much more interesting. – Alan Moore Apr 07 '13 at 21:30
  • Note that another trick used here is the sequence "[ -]\?" This allows for matching a space, a hyphen, or any other character used to separate the groups of digits in the phone number. – Max West Apr 25 '15 at 17:15
9

There are usually four patterns of phone numbers

1. xxx-xxx-xxxx         grep -o '[0-9]\{3\}\-[0-9]\{3\}\-[0-9]\{4\}'  file.txt
2. (xxx)xxx-xxxx        grep -o '([0-9]\{3\})[0-9]\{3\}\-[0-9]\{4\}'  file.txt
3. xxx xxx xxxx         grep -o '[0-9]\{3\}\s[0-9]\{3\}\s[0-9]\{4\}'  file.txt
4. xxxxxxxxxx           grep -o '[0-9]\{10\}' file.txt

In all

grep -o '\([0-9]\{3\}\-[0-9]\{3\}\-[0-9]\{4\}\)\|\(([0-9]\{3\})[0-9]\{3\}\-[0-9]\{4\}\)\|\([0-9]\{10\}\)\|\([0-9]\{3\}\s[0-9]\{3\}\s[0-9]\{4\}\)' file.txt

Of course, one could simplify the regex above but we can also leave this simplification to grep itself ~

Raullen Chai
  • 315
  • 2
  • 2
7

This is just a modified version of Alan Moore's solution. This is protected against some race condition where the last part of the number has more than four digits in it or the if the total number of digits are more than 10:

grep '\(\(([0-9]\{3\})\|[0-9]\{3\}\)[ -]\?\)\{2\}[0-9]\{4\} '

Explanation:

  1. \(([0-9]\{3\})\|[0-9]\{3\}\) matches exactly three digits (e.g. 234) with or without surrounded by parentheses. \| performs the 'OR' operation.
  2. The first \( ... \) groups together the above format followed by a space or - or no space at all - ([ -]\?) does that.
  3. The \{2\} matches exactly two occurrences of the above
  4. The [0-9]\{4\} ' matches exactly one occurrence for a 4 digit number followed by a space

And it's a bit shorter as well. Tested on RHEL and Ubuntu. Cheers!!

MacUsers
  • 2,091
  • 3
  • 35
  • 56
2

You can just OR (|) your regexes together -- will be more readable that way too!

Arkady
  • 14,305
  • 8
  • 42
  • 46
1

My first thought is that you may find it easier to see if your candidate number matches against one of four regular expressions. That will be easier to develop/debug, especially as/when you have to handle additional formats in the future.

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
1
grep -P '[0-9]{3}-[0-9]{3}-[0-9]{3}|[0-9]{3}\ [0-9]{3}\ [0-9]{3}|[0-9]{9}|\([0-9]{3}\)[0-9]{3}-[0-9]{3}'
D W
  • 2,979
  • 4
  • 34
  • 45
1

We can put all the required phone number validations one by one using an or condition which is more likely to work well (but tiresome coding).

grep '^[0-9]\{10\}$\|^[0-9]\{3\}[-][0-9]\{3\}[-][0-9]\{4\}$\|^[0-9]\{3\}[ ][0-9]\{3\}[ ][0-9]\{4\}$\|^[(][0-9]\{3\}[)][0-9]\{3\}[-][0-9]\{4\}$' phone_number.txt

returns all the specific formats :

  • 920-702-9999
  • (920)702-9999
  • 920 702 9999
  • 9207029999
Jon Surrell
  • 9,444
  • 8
  • 48
  • 54
Aparna
  • 286
  • 1
  • 11
0

Try this one:

^(\d{10}|((([0-9]{3})\s){2})[0-9]{4}|((([0-9]{3})\-){2})[0-9]{4}|([(][0-9]{3}[)])[0-9]{3}[-][0-9]{4})$

This is only applicable for the formate you mention above like:

  1. xxxxxxxxxx
  2. xxx xxx xxxx
  3. xxx-xxx-xxxx
  4. (xxx)xxx-xxxx
Nikola
  • 14,888
  • 21
  • 101
  • 165
Tahir khan
  • 13
  • 1
  • 4
0

+?(1[ -])?((\d{3})[ -]|(\d{3}[ -]?)){2}\d{4}

works for:

123-678-1234

123 678 1234

(123)-678-1234

+1-(123)-678-1234

1-(123)-678-1234

1 123 678 1234

1 (123) 678 1234

gein
  • 1
0
grep -oE '\(?\<[0-9]{3}[-) ]?[0-9]{3}[ -]?[0-9]{4}\>'

Matches all your formats.

The \< and \> word boundaries prevent matching numbers that are too long, such as 123-123-12345 or 1234-123-1234

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
-2

I got this:

debian:tmp$ cat p.txt
333-444-5555
(333)333-6666
123 456 7890
1234567890
debian:tmp$ egrep '\(?[0-9]{3}[ )-]?[0-9]{3}[ -]?[0-9]{4}' p.txt
333-444-5555
(333)333-6666
123 456 7890
1234567890
debian:tmp$ egrep --version
GNU grep 2.5.3

Copyright (C) 1988, 1992-2002, 2004, 2005  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

debian:tmp$
Segfault
  • 8,036
  • 3
  • 35
  • 54