Регулярное выражение для определения, соответствует данный IPv4-адрес классу ‘C’ или отсутствует в Tcl

#regex #tcl

#регулярное выражение #tcl

Вопрос:

Это код, который используется для определения, принадлежит ли IP-адрес классу C или нет. Но каждый раз, когда я компилирую, он показывает ошибку. Помогите мне это исправить.

Код:

 gets stdin ip

regexp {(19[2-9]|2[0-1]d|22[0-3]).(0dd|1dd|2[0-5][0-5]).(0dd|1dd|2[0-5][0-5]).(0dd|1dd|2[0-5][0-5])} $ip d

puts "$d is a class c ip address"
  

Ответ №1:

Выполнение этого должным образом с scan

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

 # Check that the string contains a dotted quad
if {
    [scan $ip "%d.%d.%d.%d" a b c d] == 4 amp;amp; 
    ($a >= 192 amp;amp; $a <= 223) amp;amp;
    ($b >= 0 amp;amp; $b <= 255) amp;amp;
    ($c >= 0 amp;amp; $c <= 255) amp;amp;
    ($d >= 0 amp;amp; $d <= 255)
} then {
    puts "$ip is a class C IP address"
}
  

Вам рекомендуется использовать ip::normalize из пакета ipTcllib перед синтаксическим анализом, чтобы справиться со странными вещами, которые на самом деле являются законными IP-адресами.

 package require ip

# Note the simpler expression: normalizing handles the awkward cases for us
scan [ip::normalize $ip] "%d.%d.%d.%d" a b c d
if {$a >= 192 amp;amp; $a <= 223} {
    puts "$ip is a class C IP address"
}
  

Если кто-то введет вам неверный ввод, вы получите хорошую чистую ошибку, описывающую, в чем проблема, а не странно сломанную программу.


Использование регулярных выражений

Именно поэтому существует афоризм Завински:

Некоторые люди, сталкиваясь с проблемой, думают: «Я знаю, я буду использовать регулярные выражения». Теперь у них две проблемы.

Существуют проблемы, для которых регулярные выражения являются правильным решением. ЭТО НЕ ОДИН Из НИХ!

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

 regexp {^(?:19[2-9]|2[0-1]d|22[0-3])(?:.(?:d{1,2}|1dd|2[0-4]d|25[0-5])){3}$} $ip d
  

Да, это ужасно. Вот оно в развернутом виде, с некоторыми комментариями.

 ^                 # Anchored at the start of the string!
(?:               # Parse the first quad; 192..223 (NB: non-capturing group)
    19[2-9]
|
    2[0-1]d
|
    22[0-3]
)
(?:               # And three sets of what matches the other quads
    .            # Literal period
    (?:           # Parse a number in 0..255
        d{1,2}
    |
        1dd
    |
        2[0-4]d
    |
        25[0-5]
    )
) {3}             # Here's where we ask for this three times
$                 # Anchor at the end of the string
  

Причина, по которой это так беспорядочно, заключается в обработке этих диапазонов чисел; RES просто не является отличным инструментом для этого.

Комментарии:

1. Учитывая, что рекомендуется использовать ip пакет tcllib, альтернативой является использование ip::isOverlap , например, для класса C: ip::isOverlap 192.0.0.0/3 [ip::normalize $ip] .