Ниже приводится вариант ответа Джеффа в том смысле, что он генерирует sed
сценарий, который будет выполнять сортировку пузыря, но достаточно отличается, чтобы оправдать свой собственный ответ.
Разница в том, что вместо генерации O (n ^ 2) базовых регулярных выражений генерируется O (n) расширенных регулярных выражений. Полученный скрипт будет размером около 15 КБ. Время выполнения sed
скрипта составляет доли секунды (генерация скрипта занимает немного больше времени).
Он ограничен сортировкой положительных целых чисел, разделенных точками, но не ограничен размером целых чисел (просто увеличение 255
в основном цикле) или количеством целых чисел. Разделитель можно изменить, изменив delim='.'
код.
Это сделано для того, чтобы получить правильные выражения, поэтому я оставлю описание деталей на следующий день.
#!/bin/bash
# This function creates a extended regular expression
# that matches a positive number less than the given parameter.
lt_pattern() {
local n="$1" # Our number.
local -a res # Our result, an array of regular expressions that we
# later join into a string.
for (( i = 1; i < ${#n}; ++i )); do
d=$(( ${n: -i:1} - 1 )) # The i:th digit of the number, from right to left, minus one.
if (( d >= 0 )); then
res+=( "$( printf '%d[0-%d][0-9]{%d}' "${n:0:-i}" "$d" "$(( i - 1 ))" )" )
fi
done
d=${n:0:1} # The first digit of the number.
if (( d > 1 )); then
res+=( "$( printf '[1-%d][0-9]{%d}' "$(( d - 1 ))" "$(( ${#n} - 1 ))" )" )
fi
if (( n > 9 )); then
# The number is 10 or larger.
res+=( "$( printf '[0-9]{1,%d}' "$(( ${#n} - 1 ))" )" )
fi
if (( n == 1 )); then
# The number is 1. The only thing smaller is zero.
res+=( 0 )
fi
# Join our res array of expressions into a '|'-delimited string.
( IFS='|'; printf '%s\n' "${res[*]}" )
}
echo ':top'
delim='.'
for (( n = 255; n > 0; --n )); do
printf 's/\\<%d\\>\\%s\\<(%s)\\>/\\1%s%d/g\n' \
"$n" "$delim" "$( lt_pattern "$n" )" "$delim" "$n"
done
echo 'ttop'
Сценарий будет выглядеть примерно так:
$ bash generator.sh >script.sed
$ head -n 5 script.sed
:top
s/\<255\>\.\<(25[0-4][0-9]{0}|2[0-4][0-9]{1}|[1-1][0-9]{2}|[0-9]{1,2})\>/\1.255/g
s/\<254\>\.\<(25[0-3][0-9]{0}|2[0-4][0-9]{1}|[1-1][0-9]{2}|[0-9]{1,2})\>/\1.254/g
s/\<253\>\.\<(25[0-2][0-9]{0}|2[0-4][0-9]{1}|[1-1][0-9]{2}|[0-9]{1,2})\>/\1.253/g
s/\<252\>\.\<(25[0-1][0-9]{0}|2[0-4][0-9]{1}|[1-1][0-9]{2}|[0-9]{1,2})\>/\1.252/g
$ tail -n 5 script.sed
s/\<4\>\.\<([1-3][0-9]{0})\>/\1.4/g
s/\<3\>\.\<([1-2][0-9]{0})\>/\1.3/g
s/\<2\>\.\<([1-1][0-9]{0})\>/\1.2/g
s/\<1\>\.\<(0)\>/\1.1/g
ttop
Идея сгенерированных регулярных выражений состоит в том, чтобы сопоставлять образцы для чисел, которые меньше, чем каждое целое число; эти два числа будут не в порядке, и поэтому поменяются местами. Регулярные выражения сгруппированы в несколько опций OR. Обратите пристальное внимание на диапазоны, добавленные к каждому элементу, иногда они есть {0}
, то есть немедленно предшествующий элемент должен быть исключен из поиска. Параметры регулярного выражения слева направо сопоставляют числа, которые меньше заданного числа, на:
- те места
- десятки место
- место сотни
- (продолжение по мере необходимости, для больших чисел)
- или быть меньше по величине (количество цифр)
Чтобы разобрать пример, возьмите 101
(с дополнительными пробелами для удобства чтения):
s/ \<101\> \. \<(10[0-0][0-9]{0} | [0-9]{1,2})\> / \1.101 /g
Здесь первое чередование допускает числа от 100 до 100; второе чередование позволяет от 0 до 99.
Другой пример 154
:
s/ \<154\> \. \<(15[0-3][0-9]{0} | 1[0-4][0-9]{1} | [0-9]{1,2})\> / \1.154 /g
Здесь первый вариант позволяет от 150 до 153; вторая позволяет от 100 до 149, а последняя - от 0 до 99.
Тестирование четыре раза в цикле:
for test_run in {1..4}; do
nums=$(( RANDOM%256 )).$(( RANDOM%256 )).$(( RANDOM%256 )).$(( RANDOM%256 ))
printf 'nums=%s\n' "$nums"
sed -E -f script.sed <<<"$nums"
done
Выход:
nums=90.19.146.232
19.90.146.232
nums=8.226.70.154
8.70.154.226
nums=1.64.96.143
1.64.96.143
nums=67.6.203.56
6.56.67.203