Угловые скобки ( )обозначают «32 -битный массив целых чисел без знака», и я полагаю, что их часто называют «ячейками». В случае со свойством reg
первое значение — это (адрес смещения ), а второе — длина регистра (с ). Обратите внимание, что также возможно иметь несколько диапазонов, т.е.
reg =
Источники:
В итоге я получил следующую функцию BASH, использующую массив для переноса идентифицированных диапазонов. Входная строка является первым аргументом функции, а результат передается через второй аргумент :
.function compact_range {
arr=()
start=""
for cpu in ${1//,/ }; do
# Start a new range definition if necessary
[ -z "$start" ] && start=$cpu && range=$cpu && last=$cpu && continue
prev=$(( $cpu - 1 ))
# If the current CPU is not adjacent to the last CPU, start a new range
[ "$prev" -ne "$last" ] && arr+=($range) && start=$cpu && range=$cpu && last=$cpu && continue
# Current CPU is adjacent to an existing range, expand the current range
range="${start}-${cpu}" && last=$cpu
done
# Append the last range to the array of ranges
arr+=($range)
# Return a comma delimited list of ranges
eval $2=$(IFS=,;printf "%s" "${arr[*]}")
}
Спасибо всем за идеи.
Вот короткий awk
скрипт, который проходит через -разделенный запятыми список отсортированных целых чисел и заполняет два массива, a
и b
, при этом.
Массив a
будет содержать начальное целое число для каждого диапазона монотонно возрастающих целых чисел, а массив b
будет содержать соответствующее конечное целое число. Переменная n
в коде содержит количество найденных диапазонов.
BEGIN {
OFS = FS = ","
}
{
n = 0
a[++n] = $1
for (i = 1; i < NF; ++i)
if ($i != $(i+1) - 1) {
b[n] = $i
a[++n] = $(i+1)
}
b[n] = $NF
$0 = ""
for (i = 1; i <= n; ++i)
if (a[i] == b[i])
$i = a[i]
else
$i = sprintf("%d-%d", a[i], b[i])
print
}
Выходные данные создаются путем итерации по n
найденным различным диапазонам и построения записи, в которой каждое поле представляет собой либо одно целое число (для диапазонов длиной 1 ), либо строку, представляющую начало и конец диапазона. спектр.
Тестирование на предоставленных вами данных, чтение данных из файла:
$ awk -f script.awk file
1-3,5-9,12,14
Очевидно, вы могли бы передать это строкой из стандартного ввода, например так:
$ awk -f script.awk <<<"1,2,3,5,9,10,11,12,13"
1-3,5,9-13
Вместо этого с помощью zsh
вы можете определить такую функцию, как:
reduce() {
local i=1
argv=(${(nus:,:)1}) # split $1 on ",", numerically sort and remove dups
while ((i < $#)) {
if ((${argv[i]#*-} + 1 == ${argv[i+1]%-*})) {
argv[i]=${argv[i]%-*}-${argv[i+1]#*-}
argv[i+1]=()
} else {
((i++))
}
}
print ${(j:,:)@}
}
Который также будет принимать диапазоны на вход:
$ reduce 1,2,3,5,6,7,8,9,12,14
1-3,5-9,12,14
$ reduce 1,2,3,5-7,8,9-11,12,13-20
1-3,5-20
$ reduce 5,2,4,5,6
2,4-6
Обратите внимание, что это не будет работать должным образом, если входные данные имеют перекрывающиеся диапазоны:
$ reduce 1-3,2
1-3,2
$ reduce 1-3,2-4
1-3,2-4
Из bash
вы бы определили функцию как:
reduce() { zsh -c '
i=1
argv=(${(nus:,:)1}) # split $1 on ",", numerically sort and remove dups
while ((i < $#)) {
if ((${argv[i]#*-} + 1 == ${argv[i+1]%-*})) {
argv[i]=${argv[i]%-*}-${argv[i+1]#*-}
argv[i+1]=()
} else {
((i++))
}
}
print ${(j:,:)@}' zsh "$@"
}
Я был бы очень удивлен, если бы это можно было сделать в sed
. Вы можете написать чистый bash-скрипт, но в awk
это довольно просто. Эта программа чем-то похожа на Кусалананды . но не использует массив.
awk -F, '
{
begin=$1
prev=$1
for (i=2; i<=NF; i++) {
if ($i == prev+1) {
prev=$i
continue
}
if (begin==prev) {
printf "%s,", begin
} else {
printf "%s-%s,", begin, prev
}
begin=$i
prev=$i
}
if (begin==prev) {
printf "%s", begin
} else {
printf "%s-%s", begin, prev
}
print ""
}'
-F,
устанавливает разделитель полей, поэтому мы можем рассматривать каждое число как поле. begin
— первое число в текущем диапазоне последовательных номеров. prev
всегда является самым последним числом, которое мы просматривали. Мы могли бы просто сказать $(i-1)
; Я подумал, что будет понятнее дать ему имя. print ""
, чтобы напечатать новую строку. Использование Perl:
perl -pe 's/\b(\d+)(?{$q=$1+1})(?:,(??{$q})\b(?{$p=$q++})){2,}/$1-$p/g'
Здесь используется регулярное выражение со встроенным кодом Perl через выражения (?{...})
и (??{...})
; первый просто оценивает встроенный код, а второй использует возвращаемое им значение в качестве шаблона. Полное описание см. в perlre(1)
.
Замените квантификатор {2,}
на +
, если вам также нужны диапазоны только из двух чисел (, например.1,2,7
->1-2,7
).
Очень короткий awk ответ для однострочного ввода, обрабатывающий все случаи ниже(сканирование чисел выполняется слева -в -справа во всех случаях )в отличие от других существующих в настоящее время ответов, которые терпят неудачу во всех этих случаях, и в основном они обрабатывают только случай #2:
$ awk -v RS=, 'function prnt(){ printf sep start (end==start?"":"—"end) ; sep=RS }
end!="" && ( end==$0-1 || end==$0+1) { end=$0; next }
end!="" { prnt() }
{ start=end=$0 }
END{ prnt() }'
1. числа в порядке убывания:
$ awk -v RS=, 'function prnt(){ printf sep start (end==start?"":"—"end) ; sep=RS }
end!="" && ( end==$0-1 || end==$0+1) { end=$0; next }
end!="" { prnt() }
{ start=end=$0 }
END{ prnt() }' <<<'14,13,12,11,9,8,7,3,2,1,0,-1'
14—11,9—7,3—-1
2. цифры в порядке возрастания:
$ awk -v RS=, 'function prnt(){ printf sep start (end==start?"":"—"end) ; sep=RS }
end!="" && ( end==$0-1 || end==$0+1) { end=$0; next }
end!="" { prnt() }
{ start=end=$0 }
END{ prnt() }' <<<'1,2,3,5,6,7,8,9,12,14'
1—3,5—9,12,14
3. числа в несортированном порядке:
$ awk -v RS=, 'function prnt(){ printf sep start (end==start?"":"—"end) ; sep=RS }
end!="" && ( end==$0-1 || end==$0+1) { end=$0; next }
end!="" { prnt() }
{ start=end=$0 }
END{ prnt() }' <<<'10,3,4,5,6,2,1,5,6,7,9,7,2,3'
10,3—6,2—1,5—7,9,7,2—3
Для применения к файлу, содержащему несколько таких строк, например:
$ cat infile
14,13,12,11,9,8,7,3,2,1,0,-1
1,2,3,5,6,7,8,9,12,14
-1,0,1,2,4,3,2,1,0,-1,-2,-2,-2,-2,-4
10,3,4,5,6,2,1,5,6,7,9,7,2,3
и немного подкорректировать скрипт:
$ awk -v RS=, 'function prnt(){ printf sep start (end==start?"":"—"end) ; sep=RS }
/\n/{
printf "%s\n", sep start ((end==$1-1 || end==$1+1)?"—":sep) $1;
sep=""; start=end=$2; next
}
end!="" && ( end==$0-1 || end==$0+1) { end=$0; next }
end!="" { prnt() }
{ start=end=$0 }' infile
Выход:
14—11,9—7,3—-1
1—3,5—9,12,14
-1—2,4—-2,-2,-2,-2,-4
10,3—6,2—1,5—7,9,7,2—3