2009-07-17 5 views
4

données de numéros de téléphone dans différents formats (j'ai choisi ces parce que les données provenant de ne sont pas fiables et non dans des formats attendus):Meilleur moyen d'extraire le numéro de téléphone et de reformater?

+1 480-874-4666 
404-581-4000 
(805) 682-4726 
978-851-7321, Ext 2606 
413- 658-1100 
(513) 287-7000,Toll Free (800) 733-2077 
1 (813) 274-8130 
212-363-3200,Media Relations: 212-668-2251. 
323/221-2164 

Mon code Ruby pour extraire tous les chiffres, retirez les principaux 1 de la USA code pays, puis utilisez les 10 premiers chiffres pour créer le numéro de téléphone « nouveau » dans un format souhaité:

nums = phone_number_string.scan(/[0-9]+/) 
    if nums.size > 0 
    all_nums = nums.join 
    all_nums = all_nums[0..0] == "1" ? all_nums[1..-1] : all_nums 
    if all_nums.size >= 10 
     ten_nums = all_nums[0..9] 
     final_phone = "#{ten_nums[0..2]}-#{ten_nums[3..5]}-#{ten_nums[6..9]}" 
    else 
     final_phone = "" 
    end 
    puts "#{final_phone}" 
    else 
    puts "No number to fix." 
    end 

les résultats sont très bon!

480-874-4666 
404-581-4000 
805-682-4726 
978-851-7321 
413-658-1100 
513-287-7000 
813-274-8130 
212-363-3200 
323-221-2164 

Mais, je pense qu'il y a un meilleur moyen. Pouvez-vous refactoriser cela pour être plus efficace, plus lisible, ou plus utile?

+0

+ 49- 2345-123456789 – Svante

Répondre

13

est ici une approche beaucoup plus simple en utilisant seulement regexes et substitution:

def extract_phone_number(input) 
    if input.gsub(/\D/, "").match(/^1?(\d{3})(\d{3})(\d{4})/) 
    [$1, $2, $3].join("-") 
    end 
end 

Ce bandes non-chiffres (\D), Ignore une option menant un (^1?), puis extrait les 10 premiers chiffres en morceaux restants ((\d{3})(\d{3})(\d{4})) et les formats.

est ici le test:

test_data = { 
    "+1 480-874-4666"        => "480-874-4666", 
    "404-581-4000"        => "404-581-4000", 
    "(805) 682-4726"        => "805-682-4726", 
    "978-851-7321, Ext 2606"      => "978-851-7321", 
    "413- 658-1100"        => "413-658-1100", 
    "(513) 287-7000,Toll Free (800) 733-2077"  => "513-287-7000", 
    "1 (813) 274-8130"       => "813-274-8130", 
    "212-363-3200,Media Relations: 212-668-2251." => "212-363-3200", 
    "323/221-2164"        => "323-221-2164", 
    ""           => nil, 
    "foobar"          => nil, 
    "1234567"          => nil, 
} 

test_data.each do |input, expected_output| 
    extracted = extract_phone_number(input) 
    print "FAIL (expected #{expected_output}): " unless extracted == expected_output 
    puts extracted 
end 
+0

Cette approche est probablement plus rapide. –

0

Pour les numéros dans le plan nord-américain on pourrait extraire le premier numéro à l'aide phone_number_string.gsub(/\D/, '').match(/^1?(\d{10})/)[1]

Par exemple:

test_phone_numbers = ["+1 480-874-4666", 
         "404-581-4000", 
         "(805) 682-4726", 
         "978-851-7321, Ext 2606", 
         "413- 658-1100", 
         "(513) 287-7000,Toll Free (800) 733-2077", 
         "1 (813) 274-8130", 
         "212-363-3200,Media Relations: 212-668-2251.", 
         "323/221-2164", 
         "foobar"] 

test_phone_numbers.each do | phone_number_string | 
    match = phone_number_string.gsub(/\D/, '').match(/^1?(\d{10})/) 
    puts(
    if (match) 
     "#{match[1][0..2]}-#{match[1][3..5]}-#{match[1][6..9]}" 
    else 
     "No number to fix." 
    end 
) 
end 

Comme le code de départ, cela ne tient pas compte de plusieurs numéros, par exemple, "(513) 287-7000, sans frais (800) 733-2077"

FWIW, J'ai trouvé plus facile à long terme de capturer et de stocker des nombres complets, c'est-à-dire, y compris le code du pays et aucun séparateur; faire des suppositions pendant la capture sur lesquelles numbering plan manquant un préfixe sont dans et sélectionner des formats, par exemple NANP v. DE, lors du rendu.

2

Mon approche est un peu différente (et je pense mieux à mon humble avis :-): Je avais besoin de ne pas manquer les numéros de téléphone, même s'il y avait deux sur une ligne. Je ne voulais pas non plus avoir des lignes contenant 3 séries de numéros éloignés (voir l'exemple des cookies), et je ne voulais pas confondre une adresse IP avec un numéro de téléphone.

code

pour permettre à plusieurs numéros par ligne, mais exigent également des ensembles de chiffres pour être « proche » de l'autre:

def extract_phone_number(input) 
    result = input.scan(/(\d{3})\D{0,3}(\d{3})\D{0,3}(\d{4})/).map{|e| e.join('-')} 
    # <result> is an Array of whatever phone numbers were extracted, and the remapping 
    # takes care of cleaning up each number in the Array into a format of 800-432-1234 
    result = result.join(' :: ') 
    # <result> is now a String, with the numbers separated by ' :: ' 
    # ... or there is another way to do it (see text below the code) that only gets the 
    # first phone number only. 

    # Details of the Regular Expressions and what they're doing 
    # 1. (\d{3}) -- get 3 digits (and keep them) 
    # 2. \D{0,3} -- allow skipping of up to 3 non-digits. This handles hyphens, parentheses, periods, etc. 
    # 3. (\d{3}) -- get 3 more digits (and keep them) 
    # 4. \D{0,3} -- skip up to 0-3 non-digits 
    # 5. (\d{4}) -- keep the final 4 digits 

    result.empty? ? nil : result 
end 

Et voici les tests (avec quelques tests supplémentaires)

test_data = { 
    "DB=Sequel('postgres://user:[email protected]/test_test')" => nil, # DON'T MISTAKE IP ADDRESSES AS PHONE NUMBERS 
    "100 cookies + 950 cookes = 1050 cookies"  => nil, # THIS IS NEW 
    "this 123 is a 456 bad number 7890"   => nil, # THIS IS NEW 
    "212-363-3200,Media Relations: 212-668-2251." => "212-363-3200 :: 212-668-2251", # THIS IS CHANGED 
    "this is +1 480-874-4666"      => "480-874-4666", 
    "something 404-581-4000"      => "404-581-4000", 
    "other (805) 682-4726"      => "805-682-4726", 
    "978-851-7321, Ext 2606"      => "978-851-7321", 
    "413- 658-1100"        => "413-658-1100", 
    "(513) 287-7000,Toll Free (800) 733-2077"  => "513-287-7000 :: 800-733-2077", # THIS IS CHANGED 
    "1 (813) 274-8130"       => "813-274-8130", 
    "323/221-2164"        => "323-221-2164", 
    ""           => nil, 
    "foobar"          => nil, 
    "1234567"          => nil, 
} 

def test_it(test_data) 
    test_data.each do |input, expected_output| 
    extracted = extract_phone_number(input) 
    puts "#{extracted == expected_output ? 'good': 'BAD!'} ::#{input} => #{extracted.inspect}" 
    end 
end 

test_it(test_data) 

Une implémentation alternative: en utilisant "scan", l'expression régulière sera automatiquement appliquée plusieurs fois, ce qui est bon si vous voulez ajouter plus de 1 numéro de téléphone par ligne.Si vous voulez juste pour obtenir le premier numéro de téléphone sur une ligne, vous pouvez également utiliser:

first_phone_number = begin 
    m = /(\d{3})\D{0,3}(\d{3})\D{0,3}(\d{4})/.match(input) 
    [m[1],m[2],m[3]].join('-') 
rescue nil; end 

(juste une façon différente de faire les choses, en utilisant la fonction « match » de RegExp)

Questions connexes