0
# Utility Functions
1
2
Helper functions for normalizing, converting, and analyzing phone numbers. These utilities support data processing, string manipulation, and number comparison operations.
3
4
## Capabilities
5
6
### String Normalization
7
8
Normalize phone number strings by removing or converting unwanted characters to standard formats.
9
10
```python { .api }
11
def normalize_digits_only(number: str) -> str:
12
"""
13
Normalize string to contain only digits.
14
15
Removes all non-digit characters from the input string.
16
17
Parameters:
18
- number: String that may contain phone number with formatting
19
20
Returns:
21
String containing only digit characters (0-9)
22
"""
23
24
def normalize_diallable_chars_only(number: str) -> str:
25
"""
26
Normalize string to contain only diallable characters.
27
28
Keeps digits and diallable symbols like +, *, #, but removes
29
formatting characters like spaces, dashes, parentheses.
30
31
Parameters:
32
- number: Phone number string with potential formatting
33
34
Returns:
35
String with only diallable characters
36
"""
37
```
38
39
**Usage Examples:**
40
41
```python
42
import phonenumbers
43
44
# Remove all formatting
45
formatted_number = "+44 (20) 8366-1177"
46
digits_only = phonenumbers.normalize_digits_only(formatted_number)
47
print(digits_only) # "442083661177"
48
49
# Keep diallable characters
50
number_with_extension = "+44 20 8366 1177 ext. 123"
51
diallable = phonenumbers.normalize_diallable_chars_only(number_with_extension)
52
print(diallable) # "+442083661177ext123"
53
```
54
55
### Alpha Character Conversion
56
57
Convert alphabetic characters in phone numbers to their corresponding digits (vanity numbers).
58
59
```python { .api }
60
def convert_alpha_characters_in_number(number: str) -> str:
61
"""
62
Convert alphabetic characters to digits using phone keypad mapping.
63
64
Maps letters to digits according to standard phone keypad:
65
ABC=2, DEF=3, GHI=4, JKL=5, MNO=6, PQRS=7, TUV=8, WXYZ=9
66
67
Parameters:
68
- number: Phone number string that may contain letters
69
70
Returns:
71
String with letters converted to corresponding digits
72
"""
73
74
def is_alpha_number(number: str) -> bool:
75
"""
76
Check if a phone number string contains alphabetic characters.
77
78
Parameters:
79
- number: Phone number string to check
80
81
Returns:
82
True if the string contains 3 or more letters, False otherwise
83
"""
84
```
85
86
**Usage Examples:**
87
88
```python
89
import phonenumbers
90
91
# Convert vanity number
92
vanity_number = "1-800-FLOWERS"
93
numeric = phonenumbers.convert_alpha_characters_in_number(vanity_number)
94
print(numeric) # "1-800-3569377"
95
96
# Check for alpha characters
97
has_alpha = phonenumbers.is_alpha_number("1-800-CALL-NOW")
98
print(has_alpha) # True
99
100
no_alpha = phonenumbers.is_alpha_number("1-800-555-1234")
101
print(no_alpha) # False
102
```
103
104
### Number Comparison
105
106
Compare phone numbers to determine if they represent the same number and the quality of the match.
107
108
```python { .api }
109
def is_number_match(first_number: PhoneNumber, second_number: PhoneNumber) -> MatchType:
110
"""
111
Compare two phone numbers and return the type of match.
112
113
Parameters:
114
- first_number: First PhoneNumber object to compare
115
- second_number: Second PhoneNumber object to compare
116
117
Returns:
118
MatchType indicating the quality of the match:
119
- EXACT_MATCH: Numbers are identical in all respects
120
- NSN_MATCH: National significant numbers match exactly
121
- SHORT_NSN_MATCH: One NSN is a shorter version of the other
122
- NO_MATCH: Numbers are different
123
- NOT_A_NUMBER: One or both numbers are invalid
124
"""
125
```
126
127
**Usage Examples:**
128
129
```python
130
import phonenumbers
131
from phonenumbers import MatchType
132
133
# Parse two representations of the same number
134
number1 = phonenumbers.parse("+442083661177")
135
number2 = phonenumbers.parse("020 8366 1177", "GB")
136
137
match_result = phonenumbers.is_number_match(number1, number2)
138
139
if match_result == MatchType.EXACT_MATCH:
140
print("Numbers are exactly the same")
141
elif match_result == MatchType.NSN_MATCH:
142
print("Numbers have the same national significant number")
143
elif match_result == MatchType.SHORT_NSN_MATCH:
144
print("One number is a shorter version of the other")
145
elif match_result == MatchType.NO_MATCH:
146
print("Numbers are different")
147
148
# Compare with extensions
149
number_with_ext = phonenumbers.parse("+442083661177", keep_raw_input=True)
150
number_with_ext.extension = "123"
151
number_without_ext = phonenumbers.parse("+442083661177")
152
153
match_with_extension = phonenumbers.is_number_match(number_with_ext, number_without_ext)
154
print(f"Match with extension: {match_with_extension}")
155
```
156
157
### National Significant Number Extraction
158
159
Extract the national significant number portion from a parsed phone number.
160
161
```python { .api }
162
def national_significant_number(numobj: PhoneNumber) -> str:
163
"""
164
Get the national significant number from a PhoneNumber object.
165
166
The national significant number is the portion of the phone number
167
that is dialed within the country, excluding the country code but
168
including area codes and the subscriber number.
169
170
Parameters:
171
- numobj: PhoneNumber object to extract NSN from
172
173
Returns:
174
String containing the national significant number
175
"""
176
```
177
178
### Area Code and Destination Code Analysis
179
180
Analyze the structure of phone numbers to determine area code and destination code lengths.
181
182
```python { .api }
183
def length_of_geographical_area_code(numobj: PhoneNumber) -> int:
184
"""
185
Get the length of the geographical area code for a phone number.
186
187
Parameters:
188
- numobj: PhoneNumber object to analyze
189
190
Returns:
191
Length of the area code, or 0 if not applicable or determinable
192
"""
193
194
def length_of_national_destination_code(numobj: PhoneNumber) -> int:
195
"""
196
Get the length of the national destination code (area code + carrier code).
197
198
Parameters:
199
- numobj: PhoneNumber object to analyze
200
201
Returns:
202
Length of the national destination code
203
"""
204
```
205
206
### Mobile Token Handling
207
208
Work with mobile tokens used in certain countries before the area code.
209
210
```python { .api }
211
def country_mobile_token(country_calling_code: int) -> str:
212
"""
213
Get the mobile token for a country, if applicable.
214
215
Some countries use a mobile token (like '9' in Argentina) that
216
appears before the area code in mobile numbers.
217
218
Parameters:
219
- country_calling_code: Country calling code to check
220
221
Returns:
222
Mobile token string, or empty string if not applicable
223
"""
224
```
225
226
### NANPA Region Detection
227
228
Check if a region uses the North American Numbering Plan.
229
230
```python { .api }
231
def is_nanpa_country(region_code: str) -> bool:
232
"""
233
Check if a region uses the North American Numbering Plan.
234
235
NANPA regions share country code +1 and similar numbering patterns.
236
237
Parameters:
238
- region_code: Two-letter region code to check
239
240
Returns:
241
True if the region is part of NANPA, False otherwise
242
"""
243
```
244
245
### Mobile Number Portability
246
247
Check if a region supports mobile number portability.
248
249
```python { .api }
250
def is_mobile_number_portable_region(region_code: str) -> bool:
251
"""
252
Check if mobile numbers can be ported between carriers in a region.
253
254
Parameters:
255
- region_code: Two-letter region code to check
256
257
Returns:
258
True if mobile number portability is supported, False otherwise
259
"""
260
```
261
262
## Usage Patterns
263
264
### Data Cleaning Pipeline
265
266
```python
267
import phonenumbers
268
269
def clean_phone_number_data(raw_numbers, default_region="US"):
270
"""Clean a list of raw phone number strings for processing."""
271
cleaned_numbers = []
272
273
for raw_number in raw_numbers:
274
try:
275
# Step 1: Convert alpha characters
276
if phonenumbers.is_alpha_number(raw_number):
277
numeric_number = phonenumbers.convert_alpha_characters_in_number(raw_number)
278
else:
279
numeric_number = raw_number
280
281
# Step 2: Parse the number
282
parsed = phonenumbers.parse(numeric_number, default_region)
283
284
# Step 3: Validate
285
if phonenumbers.is_valid_number(parsed):
286
# Step 4: Normalize to E164 format
287
e164 = phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
288
cleaned_numbers.append({
289
'original': raw_number,
290
'cleaned': e164,
291
'nsn': phonenumbers.national_significant_number(parsed),
292
'type': phonenumbers.number_type(parsed)
293
})
294
295
except phonenumbers.NumberParseException:
296
# Log invalid numbers for review
297
print(f"Could not parse: {raw_number}")
298
299
return cleaned_numbers
300
301
# Example usage
302
raw_data = [
303
"1-800-FLOWERS",
304
"+44 (20) 8366-1177",
305
"555.123.4567",
306
"(555) 123-4567 ext 123"
307
]
308
309
cleaned = clean_phone_number_data(raw_data)
310
for item in cleaned:
311
print(f"{item['original']} -> {item['cleaned']}")
312
```
313
314
### Deduplication Using Number Matching
315
316
```python
317
import phonenumbers
318
from phonenumbers import MatchType
319
320
def deduplicate_phone_numbers(number_strings, region="US"):
321
"""Remove duplicate phone numbers using intelligent matching."""
322
parsed_numbers = []
323
unique_numbers = []
324
325
# Parse all valid numbers
326
for number_str in number_strings:
327
try:
328
parsed = phonenumbers.parse(number_str, region)
329
if phonenumbers.is_valid_number(parsed):
330
parsed_numbers.append((number_str, parsed))
331
except phonenumbers.NumberParseException:
332
continue
333
334
# Find unique numbers
335
for original_str, parsed_num in parsed_numbers:
336
is_duplicate = False
337
338
for unique_str, unique_parsed in unique_numbers:
339
match_type = phonenumbers.is_number_match(parsed_num, unique_parsed)
340
341
if match_type in [MatchType.EXACT_MATCH, MatchType.NSN_MATCH]:
342
is_duplicate = True
343
break
344
345
if not is_duplicate:
346
unique_numbers.append((original_str, parsed_num))
347
348
return [num_str for num_str, _ in unique_numbers]
349
350
# Example usage
351
numbers_with_duplicates = [
352
"+1-800-555-1234",
353
"1 (800) 555-1234",
354
"18005551234",
355
"+1-555-123-4567",
356
"555.123.4567"
357
]
358
359
unique = deduplicate_phone_numbers(numbers_with_duplicates)
360
print(f"Original: {len(numbers_with_duplicates)} numbers")
361
print(f"Unique: {len(unique)} numbers")
362
```
363
364
### Number Structure Analysis
365
366
```python
367
import phonenumbers
368
369
def analyze_number_structure(number_str, region=None):
370
"""Analyze the internal structure of a phone number."""
371
try:
372
number = phonenumbers.parse(number_str, region)
373
374
if not phonenumbers.is_valid_number(number):
375
return {"error": "Invalid number"}
376
377
nsn = phonenumbers.national_significant_number(number)
378
area_code_length = phonenumbers.length_of_geographical_area_code(number)
379
ndc_length = phonenumbers.length_of_national_destination_code(number)
380
381
region_code = phonenumbers.region_code_for_number(number)
382
is_nanpa = phonenumbers.is_nanpa_country(region_code)
383
mobile_token = phonenumbers.country_mobile_token(number.country_code)
384
385
return {
386
'country_code': number.country_code,
387
'nsn': nsn,
388
'area_code_length': area_code_length,
389
'ndc_length': ndc_length,
390
'region': region_code,
391
'is_nanpa': is_nanpa,
392
'mobile_token': mobile_token,
393
'is_geographic': phonenumbers.is_number_geographical(number),
394
'can_dial_internationally': phonenumbers.can_be_internationally_dialled(number)
395
}
396
397
except phonenumbers.NumberParseException as e:
398
return {"error": str(e)}
399
400
# Example usage
401
analysis = analyze_number_structure("+442083661177")
402
print(f"Country code: {analysis['country_code']}")
403
print(f"NSN: {analysis['nsn']}")
404
print(f"Area code length: {analysis['area_code_length']}")
405
print(f"Region: {analysis['region']}")
406
```