Как сопоставить числовое значение в регулярном выражении? - PullRequest
3 голосов
/ 05 августа 2010

Хорошо, это довольно интересный вызов, в который я попал.

Мой RegEx принимает в качестве входных строк, как показано ниже:

147.63.23.156/159
94.182.23.55/56
134.56.33.11/12

Мне нужно вывести регулярное выражение , соответствующее представленному диапазону. Позвольте мне объяснить.

Например, если RegEx получает 147.63.23.156/159, ему необходимо вывести RegEx, который соответствует следующему:

147.63.23.156
147.63.23.157
147.63.23.158
147.63.23.159

Как я могу это сделать?

В настоящее время у меня есть:

(\d{1,3}\.\d{1,3}\.\d{1,3}\.)(\d{1,3})/(\d{1,3})
  • $ 1 содержит первую xxx.xxx.xxx. часть
  • $ 2 содержит нижний диапазон для числа
  • $ 3 содержит верхний диапазон для числа

Ответы [ 5 ]

1 голос
/ 05 августа 2010

Если это для Apache ... Я не пробовал, но это может сработать:

RewriteCond %{REMOTE_ADDR} !<147.63.23.156
RewriteCond %{REMOTE_ADDR} !>147.63.23.159

(Два последовательных RewriteCond с объединены по умолчанию логическим AND)

Просто нужно быть осторожным с диапазонами с различным количеством цифр (например, 95-105 следует разбить на 95-99 и 100-105, так как это лексикографическое упорядочение).

1 голос
/ 05 августа 2010

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

Тем не менее, казалось, что это было бы действительно забавное упражнение - написать этот генератор регулярных выражений. И знаешь, что? Это было! Я придумал большой беспорядок в коде Python для генерации этих регулярных выражений. Прежде чем я покажу код, вот пример регулярных выражений, которые он генерирует для пары диапазонов IP:

1.2.3.4 to 1.2.3.4              1\.2\.3\.4

147.63.23.156 to 147.63.23.159  147\.63\.23\.15[6-9]

10.7.7.10 to 10.7.7.88          10\.7\.7\.([1-7]\d|8[0-8])

127.0.0.0 to 127.0.1.255        127\.0\.[0-1]\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))

Я покажу код в двух частях. Во-первых, часть, которая генерирует регулярные выражения для простых целочисленных диапазонов. Во-вторых, часть, которая обрабатывает полные IP-адреса.

Соответствующие диапазоны номеров

Первый шаг - выяснить, как создать регулярное выражение, соответствующее произвольному целочисленному диапазону, скажем, 12-28 или 0-255.

@ Роберт Харви уже разместил ссылку на какой-то код, который делает это, но вот так! Я хорошо писал свой код, пока не увидел его ссылку. Кроме того, речь идет о путешествии, а не о пункте назначения, верно?

Итак, я написал свою собственную реализацию генератора регулярных выражений диапазона чисел. Вот пример регулярных выражений, которые он предлагает:

156 to 159   15[6-9]

1 to 100     [1-9]|[1-9]\d|100

0 to 255     \d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5])

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

import sys, re

def range_regex(lower, upper):
    lower, upper = str(lower), str(upper)

    # Different lengths, for instance 1-100. Combine regex(1-9) and
    # regex(10-100).
    if len(lower) != len(upper):
        return '%s|%s' % (
            range_regex(lower, '9' * len(lower)),
            range_regex(10 ** (len(lower)), upper)
        )

    ll, lr = lower[0], lower[1:]
    ul, ur = upper[0], upper[1:]

    # One digit numbers.
    if lr == '':
        if ll == '0' and ul == '9':
            return '\\d'
        else:
            return '[%s-%s]' % (ll, ul)

    # Same first digit, for instance 12-14. Concatenate "1" and regex(2-4).
    elif ll == ul:
        return ll + sub_range_regex(lr, ur)

    # All zeros to all nines, for instance 100-399. Concatenate regex(1-3)
    # and the appropriate number of \d's.
    elif lr == '0' * len(lr) and ur == '9' * len(ur):
        return range_regex(ll, ul) + '\\d' * len(lr)

    # All zeros on left, for instance 200-649. Combine regex(200-599) and
    # regex(600-649).
    elif lr == '0' * len(lr):
        return '%s|%s' % (
            range_regex(lower, str(int(ul[0]) - 1) + '9' * len(ur)),
            range_regex(ul + '0' * len(ur), upper)
        )

    # All nines on right, for instance 167-499. Combine regex(167-199) and
    # regex(200-499).
    elif ur == '9' * len(ur):
        return '%s|%s' % (
            range_regex(lower, ll + '9' * len(lr)),
            range_regex(str(int(ll[0]) + 1) + '0' * len(lr), upper)
        )

    # First digits are one apart, for instance 12-24. Combine regex(12-19)
    # and regex(20-24).
    elif ord(ul[0]) - ord(ll[0]) == 1:
        return '%s%s|%s%s' % (
            ll, sub_range_regex(lr, '9' * len(lr)),
            ul, sub_range_regex('0' * len(ur), ur)
        )

    # Far apart, uneven numbers, for instance 15-73. Combine regex(15-19),
    # regex(20-69), and regex(70-73).
    else:
        return '%s|%s|%s' % (
            range_regex(lower, ll + '9' * len(lr)),
            range_regex(str(int(ll[0]) + 1) + '0' * len(lr),
                        str(int(ul[0]) - 1) + '9' * len(ur)),
            range_regex(ul + '0' * len(ur), upper)
        )

# Helper function which adds parentheses when needed to sub-regexes.
# Sub-regexes need parentheses if they have pipes that aren't already
# contained within parentheses. For example, "6|8" needs parentheses
# but "1(6|8)" doesn't.
def sub_range_regex(lower, upper):
    orig_regex = range_regex(lower, upper)
    old_regex  = orig_regex

    while True:
        new_regex = re.sub(r'\([^()]*\)', '', old_regex)

        if new_regex == old_regex:
            break
        else:
            old_regex = new_regex
            continue

    if '|' in new_regex:
        return '(' + orig_regex + ')'
    else:
        return orig_regex

Соответствующие диапазоны IP-адресов

Имея эту возможность, я затем сделал очень похожую функцию диапазона IP-адресов для работы с полными IP-адресами. Код очень похож на код выше, за исключением того, что мы работаем в базе 256 вместо базы 10, и код разбрасывает списки вместо строк.

import sys, re, socket

def ip_range_regex(lower, upper):
    lower = [ord(c) for c in socket.inet_aton(lower)]
    upper = [ord(c) for c in socket.inet_aton(upper)]

    return ip_array_regex(lower, upper)

def ip_array_regex(lower, upper):
    # One octet left.
    if len(lower) == 1:
        return range_regex(lower[0], upper[0])

    # Same first octet.
    if lower[0] == upper[0]:
        return '%s\.%s' % (lower[0], sub_regex(ip_array_regex(lower[1:], upper[1:])))

    # Full subnet.
    elif lower[1:] == [0] * len(lower[1:]) and upper[1:] == [255] * len(upper[1:]):
        return '%s\.%s' % (
            range_regex(lower[0], upper[0]),
            sub_regex(ip_array_regex(lower[1:], upper[1:]))
        )

    # Partial lower subnet.
    elif lower[1:] == [0] * len(lower[1:]):
        return '%s|%s' % (
            ip_array_regex(lower, [upper[0] - 1] + [255] * len(upper[1:])),
            ip_array_regex([upper[0]] + [0] * len(upper[1:]), upper)
        )

    # Partial upper subnet.
    elif upper[1:] == [255] * len(upper[1:]):
        return '%s|%s' % (
            ip_array_regex(lower, [lower[0]] + [255] * len(lower[1:])),
            ip_array_regex([lower[0] + 1] + [0] * len(lower[1:]), upper)
        )

    # First octets just 1 apart.
    elif upper[0] - lower[0] == 1:
        return '%s|%s' % (
            ip_array_regex(lower, [lower[0]] + [255] * len(lower[1:])),
            ip_array_regex([upper[0]] + [0] * len(upper[1:]), upper)
        )

    # First octets more than 1 apart.
    else:
        return '%s|%s|%s' % (
            ip_array_regex(lower, [lower[0]] + [255] * len(lower[1:])),
            ip_array_regex([lower[0] + 1] + [0]   * len(lower[1:]),
                           [upper[0] - 1] + [255] * len(upper[1:])),
            ip_array_regex([upper[0]] + [0] * len(upper[1:]), upper)
        )
1 голос
/ 05 августа 2010

Если вам просто нужно создать их по одному, этот сайт сделает свое дело.

Если вам нужен код, и вы не против Python, этокод делает это для любого произвольного числового диапазона.

0 голосов
/ 05 августа 2010

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

function make_range($ip){
    $regex = '#(\d{1,3}\.\d{1,3}\.\d{1,3}\.)(\d{1,3})/(\d{1,3})#';
    if ( preg_match($regex, $ip, $matches) ){
        while($matches[1] <= $matches[2]){
            print "{$matches[0]}.{$matches[1]}";
            $matches[1]++;
        }
    } else {
        exit('not a supported IP range');
    } 
}

Чтобы это работало с RewriteCond, я думаю, что какая-то черная магия была бы в порядке ...

Как это будет использоваться с RewriteCond? У вас есть несколько серверов, и вы хотите просто быстро создать файл .htaccess? Если это так, то просто добавьте эту функцию к большему сценарию, который принимает некоторые аргументы и срывает файл .htaccess.

0 голосов
/ 05 августа 2010

Я абсолютно согласен с комментаторами, решение с чистым регулярным выражением было бы неподходящим инструментом для работы здесь. Просто используйте регулярное выражение, которое вам уже нужно, чтобы извлечь префикс, минимальное и максимальное значения,

$prefix, $minimum, $maximum = match('(\d{1,3}\.\d{1,3}\.\d{1,3}\.)(\d{1,3})/(\d{1,3})', $line).groups()

, затем проверьте свой IP-адрес против ${prefix}(\d+),

$lastgroup = match($prefix + '(\d+)', $addr).groups()[0]

и сравните эту последнюю группу, чтобы увидеть, попадает ли она в надлежащий диапазон,

return int($minimum) <= int($lastgroup) <= int($maximum)

Примеры кода являются псевдокодом, конечно - конвертируйте на ваш язык по вашему выбору.

...