Лучший способ извлечь номер телефона и переформатировать? - PullRequest
4 голосов
/ 17 июля 2009

Данные телефонных номеров в различных форматах (я выбрал их, потому что поступающие данные ненадежны и не в ожидаемых форматах):

+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

Мой код Ruby для извлечения всех цифр, удаления любых первых 1 для кода страны США, затем используйте первые 10 цифр для создания «нового» телефонного номера в требуемом формате:

  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

Результаты очень хорошо !

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

Но, я думаю, есть лучший способ. Можете ли вы изменить его, чтобы сделать его более эффективным, более разборчивым или более полезным?

Ответы [ 4 ]

13 голосов
/ 17 июля 2009

Вот гораздо более простой подход с использованием только регулярных выражений и подстановок:

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

При этом удаляются все нецифровые символы (\D), пропускается необязательный начальный (^1?), затем извлекаются первые 10 оставшихся цифр в виде фрагментов ((\d{3})(\d{3})(\d{4})) и форматируются.

Вот тест:

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
2 голосов
/ 02 января 2010

Мой подход немного другой (и я думаю, что лучше ИМХО :-): Мне нужно было не пропустить ни одного номера телефона, даже если на линии было 2. Я также не хотел получать линии с тремя наборами номеров, которые были далеко друг от друга (см. Пример с файлами cookie), и я не хотел ошибочно принимать IP-адрес за номер телефона.

Код для разрешения нескольких чисел в строке, но также для того, чтобы наборы цифр были «близки» друг к другу:

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

А вот и тесты (с несколькими дополнительными тестами)

test_data = {
  "DB=Sequel('postgres://user:username@192.168.1.101/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)

Альтернативная реализация: при использовании «сканирования» оно будет автоматически применять регулярное выражение несколько раз, что хорошо, если вы хотите добавить более 1 телефонного номера на линию. Если вы просто хотите получить первый номер телефона на линии, вы также можете использовать:

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

(просто другой способ сделать что-либо, используя функцию "match" в RegExp)

0 голосов
/ 06 февраля 2019

Это старая ветка, хотя думал, что поделюсь решением.

def extract_phone_number(input)
  input.delete!('^0-9').gsub!(/^1?(\d{3})(\d{3})(\d{4})/, '\1-\2-\3')[0..11]
rescue NoMethodError => e
  nil
end

delete! удаляет все нечисловые символы.

gsub! соответствует цифрам, затем преобразует их в разделенную дефисом строку.

[0..11] вырезает нужные цифры (в случае расширений)

Спасательный блок защищает от вызываемых методов модификации nil

Использование тестов, опубликованных выше.

tests = {
  '+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
}

tests.each do |input, expected_output|
  input  = input.dup if input.frozen?
  result = extract_phone_number(input)

  if result == expected_output
    print "PASS: #{result}\n"
  else
    print "FAIL (expected #{expected_output})\n"
  end
end

# Console
=> PASS: 480-874-4666
=> PASS: 404-581-4000
=> PASS: 805-682-4726
=> PASS: 978-851-7321
=> PASS: 413-658-1100
=> PASS: 513-287-7000
=> PASS: 813-274-8130
=> PASS: 212-363-3200
=> PASS: 323-221-2164
=> PASS:
=> PASS:
=> PASS:
0 голосов
/ 17 июля 2009

Для чисел в североамериканском плане можно извлечь первое число, используя phone_number_string.gsub(/\D/, '').match(/^1?(\d{10})/)[1]

Например:

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

Как и в случае с исходным кодом, здесь не учитываются несколько чисел, например, "(513) 287-7000, бесплатный звонок (800) 733-2077"

FWIW, в долгосрочной перспективе мне было проще собирать и хранить полные числа, т. Е. Включая код страны и без разделителей; во время захвата делать предположения о том, в каком плане нумерации находятся пропущенные префиксы, и выбирать форматы, например, NANP v. DE, при визуализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...