Perl: Format US phone number based on input
This didn't answer my question.
Possible开发者_JS百科 inputs (may have whitespace):
- full number: (XXX) XXX-XXXX
- full number: XXX/XXX-XXXX
- full number: XXXXXXXXXX
- no area code: XXX-XXXX
- no area code: XXXXXXX
- extension only: XXXX
So if you regex it for digits only s/[^\d]//g
, you'll hopefully get 1 of 3 options:
- XXXXXXXXXX
- XXXXXXX
- XXXX
Which I would like to format as so:
- XXX/XXX-XXXX
- XXX-XXXX
- XXXX
Is the best way to do this to do if
statements based on the length? Or is there a more one-liner approach?
I'd just use if
statements, because they're a lot clearer and easier to extend:
if (length($number) == 4) {
# extension
} elsif (length($number) == 7) {
# no area code
} elsif (length($number) == 10) {
# full number
} else {
die "unsupported number";
}
If you're using Perl 5.10 or higher, you can use the switch
:
use feature "switch";
given (length($number) {
when (4) { # extension }
when (7) { # no area code }
when (10) { # full number }
default { die "unsupported number"; }
}
Either of these give the advantage of being easily modified to, for example, take a number that starts with 1 (i.e. 1-555-123-4567).
You can use a sequence of substitutions.
s#[^\d]##g;
s#(\d{3})(\d{4})$#$1-$2#;
s#(\d{3})(\d{3})-#$1/$2-#;
The second substitution will fail (i.e., have no effect) if the input has less than 7 digits, and the third substitution will fail if the input has less than 10 digits.
This should do it:
perl -pne's;(?:\(?(\d{3})[\)/]?\s*)?(?:(\d{3})-?)?(\d{4});$1/$2-$3;||die("bad #: $_") and s;^/-?;;'
Example:
$ echo "(123)456-7890
(123) 456-7890
123/456-7890
1234567890
456-7890
7890
foobar
" |perl -pne's;(?:\(?(\d{3})[\)/]?\s*)?(?:(\d{3})-?)?(\d{4});$1/$2-$3;||die("bad #: $_") and s;^/-?;;'
123/456-7890
123/456-7890
123/456-7890
123/456-7890
456-7890
7890
bad number: foobar at -e line 1, <> line 7.
CodePad
sub phoneFormat{
my @sigils = ('+','/','-'); # joiners
$_ = reverse(shift); # input; reversed for matches
@_ = grep{defined} unpack "A4A3A3A1",$_; # match, starting with extension; and remove unmatched
$_ = join('', map {$_ . pop @sigils } @_ ); # add in the delimiters
($_ = reverse) =~ s/^[^\d]+//; # reverse back and remove leading non-digits
$_;
}
print phoneFormat('012') , "\n"; # (blank)
print phoneFormat('0123') , "\n"; # 0123
print phoneFormat('0123456') , "\n"; # 012-3456
print phoneFormat('0123456789') , "\n"; # 012/345-6789
print phoneFormat('01234567899') , "\n"; # 0+123/456-7899
print phoneFormat('012345678999') , "\n"; # 1+234/567-8999
for(<DATA>){
/^.*?: \s* \(? (\d{3})?? [)\/]? \s* (\d{3})? \s* [-]? \s* (\d{4}) \s* $/x;
print "$&\t";
print "$1 / " if $1;
print "$2 - " if $2;
print "$3\n" if $3;
}
__DATA__
* full number: (123) 456-7890
* full number: 123/456-7890
* full number: 1234567890
* no area code: 456-7890
* no area code: 4567890
* extension only: 7890
OUTPUT
* full number: (123) 456-7890
123 / 456 - 7890
* full number: 123/456-7890
123 / 456 - 7890
* full number: 1234567890
123 / 456 - 7890
* no area code: 456-7890
456 - 7890
* no area code: 4567890
456 - 7890
* extension only: 7890
7890
You could also use named captures like (?<area>\d{3})
to increase readability!
精彩评论