Могу ли я оптимизировать этот телефон-регулярное выражение? - PullRequest
9 голосов
/ 06 ноября 2008

Хорошо, у меня есть это регулярное выражение:

( |^|>)(((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{2})(-)?( )?)?)([0-9]{7}))|((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{3})(-)?( )?)?)([0-9]{6}))|((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{1})(-)?( )?)?)([0-9]{8})))( |$|<)

Форматирует номера телефонов в Нидерландах и Бельгии (я хочу, чтобы в качестве кода страны использовались только номера 31 и 32).

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

Все следующие номера телефонов в европейском формате принимаются

0031201234567
0031223234567
0031612345678
+31(0)20-1234567
+31(0)223-234567
+31(0)6-12345678
020-1234567
0223-234567
06-12345678
0201234567
0223234567
0612345678

и следующие ложные отформатированные не являются

06-1234567 (mobile phone number in the Netherlands should have 8 numbers after 06 )
0223-1234567 (area code with home phone)

в отличие от этого, что хорошо.

020-1234567 (area code with 3 numbers has 7 numbers for the phone as opposed to a 4 number area code which can only have 6 numbers for phone number)

Как вы можете видеть, это символ "-", который делает его немного сложным, но он мне нужен там, потому что это часть форматирования, обычно используемая людьми, и я хочу иметь возможность проанализировать их все.

Теперь мой вопрос ... Вы видите способ упростить это регулярное выражение (или даже улучшить его, если вы видите в нем ошибку), сохраняя при этом те же правила?

Вы можете проверить это на regextester.com

('(| ^ |>)' - проверить, находится ли оно в начале слова с возможностью того, что ему предшествует новая строка или '>'. Я ищу телефонные номера в HTML стр.)

Ответы [ 5 ]

12 голосов
/ 06 ноября 2008

Первое наблюдение: чтение регулярного выражения - это кошмар. Он взывает к Perl's / x mode.

Второе наблюдение: в выражении есть много, много и много захватывающих скобок (42, если я правильно считаю; и 42, конечно, «Ответ жизни, вселенной и всему» - см. Дуглас Адамс "Руководство по путешествию автостопом по Галактике", если вам нужно, что объяснил).

Bill the Lizard отмечает, что вы используете '(-)?( )?' несколько раз. В этом нет очевидного преимущества по сравнению с '-? ?' или, возможно, '[- ]?', если только вы действительно не намерены захватить фактическую пунктуацию отдельно (но есть так много захватывающих скобок, которые вырабатывают '$ n * Использовать 1009 * предметов было бы сложно).

Итак, давайте попробуем отредактировать копию вашей однострочной:

( |^|>)
(
    ((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{2})(-)?( )?)?)([0-9]{7})) |
    ((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{3})(-)?( )?)?)([0-9]{6})) |
    ((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{1})(-)?( )?)?)([0-9]{8}))
)
( |$|<)

ОК - теперь мы можем видеть регулярную структуру вашего регулярного выражения.

Отсюда возможно гораздо больше анализа. Да, в регулярном выражении могут быть значительные улучшения. Первый, очевидный, один заключается в том, чтобы извлечь часть международного префикса и применить ее один раз (по желанию или требовать начальный ноль), а затем применить национальные правила.

( |^|>)
(
    (((\+|00)(31|32)( )?(\(0\))?)|0)
    (((([0-9]{2})(-)?( )?)?)([0-9]{7})) |
    (((([0-9]{3})(-)?( )?)?)([0-9]{6})) |
    (((([0-9]{1})(-)?( )?)?)([0-9]{8}))
)
( |$|<)

Затем мы можем упростить знаки препинания, как отмечалось выше, удалить некоторые вероятные избыточные скобки и улучшить распознаватель кода страны:

( |^|>)
(
    (((\+|00)3[12] ?(\(0\))?)|0)
    (((([0-9]{2})-? ?)?)[0-9]{7}) |
    (((([0-9]{3})-? ?)?)[0-9]{6}) |
    (((([0-9]{1})-? ?)?)[0-9]{8})
)
( |$|<)

Мы можем заметить, что регулярное выражение не обеспечивает соблюдение правил для кодов мобильных телефонов (поэтому оно не настаивает на том, чтобы за «06» следовали, например, 8 цифр). Кажется также, что допускается необязательное использование 1, 2 или 3-значного кода обмена, даже с международным префиксом - вероятно, не тем, что вы имели в виду, а исправление, которое удаляет еще несколько скобок. После этого мы можем удалить еще несколько скобок, что приведет к:

( |^|>)
(
    (((\+|00)3[12] ?(\(0\))?)|0)    # International prefix or leading zero
    ([0-9]{2}-? ?[0-9]{7}) |        # xx-xxxxxxx
    ([0-9]{3}-? ?[0-9]{6}) |        # xxx-xxxxxx
    ([0-9]{1}-? ?[0-9]{8})          # x-xxxxxxxx
)
( |$|<)

И вы можете продолжить дальнейшую оптимизацию, я надеюсь.

8 голосов
/ 06 ноября 2008

Господи Вседержитель, какой беспорядок! :) Если у вас есть высокоуровневые семантические или бизнес-правила (например, те, которые вы описываете, говоря о европейских числах, числах в Нидерландах и т. Д.), Вам, вероятно, будет лучше разбить этот единственный тест регулярного выражения на несколько отдельных тестов регулярного выражения, по одному на каждое из ваших правил высокого уровня.

if number =~ /...../  # Dutch mobiles
  # ...
elsif number =~ /..../  # Belgian landlines
  # ...
# etc.
end

Будет намного легче читать, поддерживать и изменять таким образом.

3 голосов
/ 06 ноября 2008

(31 | 32) выглядит плохо. При совпадении с 32 механизм регулярных выражений сначала попытается сопоставить 31 (2 символа), потерпеть неудачу и вернуть два символа в соответствие с 31. Более эффективно сначала сопоставить 3 (один символ), попробовать 1 (ошибка), вернуть один символ и матч 2.

Конечно, ваше регулярное выражение не работает на 0800- числах; они не 10 цифр.

3 голосов
/ 06 ноября 2008

Разделите его на несколько выражений. Например (псевдокод) ...

phone_no_patterns = [
    /[0-9]{13}/, # 0031201234567
    /+(31|32)\(0\)\d{2}-\d{7}/ # +31(0)20-1234567
    # ..etc..
]
def check_number(num):
    for pattern in phone_no_patterns:
        if num matches pattern:
            return match.groups

Затем вы просто зацикливаетесь на каждом шаблоне, проверяя, соответствует ли каждый шаблон.

Разделение шаблонов позволяет легко фиксировать определенные числа, которые вызывают проблемы (что было бы ужасно с одним монолитным регулярным выражением)

2 голосов
/ 06 ноября 2008

Это не оптимизация, но вы используете

(-)?( )?

три раза в вашем регулярном выражении. Это приведет к совпадению телефонных номеров, подобных этим

+31(0)6-12345678
+31(0)6 12345678

, но также будет соответствовать числам, содержащим тире и пробел, например

+31(0)6- 12345678

Вы можете заменить

(-)?( )?

с

(-| )?

, чтобы соответствовать или тире или пробел.

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