2016-06-28 14 views
1

Я пытаюсь изменить порядок полей при построении строки с помощью pack, но я не могу получить pack, чтобы делать то, что хочу. Например, я хочу заполнить строку abc со смещением 12, defg со смещением 8 и hi со смещением 3 (и что угодно, предположительно пробел или \0, при смещениях 0-2 и 5-7).Как использовать функцию пакета perl для переопределения полей

perl -e ' 
    use strict; use warnings; 
    my $str = "...hi...defgabc"; 
    my $fmt = q{@12 a3 @8 a4 @3 a2}; 

    my @a = unpack $fmt, $str; 
    print "<$_>\n" for @a; 
    print "\n"; 

    print unpack("H*", pack($fmt, @a)), "\n"; 
' 

Это прекрасно работает для unpack ИНГ полей в произвольном порядке из строки. Но для pack ing, это \0 - заполняет и усекает как documented. Есть ли способ остановить его с \0-заполнение и усечение без изменения порядка pack шаблона для создания полей слева направо?

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

В приведенном выше коде, я был бы рад, если возвращаемое значение pack(...) была такой же, как $str с любым байтом для . (например, пустой или \0).

+0

Я не думаю, что 'pack' может быть сделано, чтобы просто почитать то, что находится в строке на этих других позициях, если это то, что вы имеете в виду. Он объединяется. Это задача 'substr' делать это (но с одним в то время). – zdim

+0

Итак, что вы хотите быть в тех других местах, до '3' и между' hi' и '8'? Исходные части '$ str'? Затем, что происходит с теми диапазонами, которые были распакованы - они также остаются такими, какими они были (тогда они дублируются в выходной строке)? – zdim

+0

@zdim На данный момент меня не волнуют другие позиции. Вопрос заключается в том, как изменить порядок позиций полей в аргументах 'pack' для шаблона пакета. Разумеется, было бы здорово, если бы можно было пустить или выбрать NUL по своему выбору. –

ответ

1

Видимо, нет никакого способа для pack сделать это напрямую. Вот один из способов сделать это, что позволяет избежать циклирования и использования substr. Однако, по сравнению с легкостью понимания unpack ing, это не очень удовлетворительно. Я надеялся, что я неправильно понял что-то в документации pack, которая действительно позволила бы pack быть обратным unpack для размещения полей в пределах строки pack.

use strict; use warnings; 
my $str = "...hi...defgabc"; 
my @pos = (
    { pos => 12, len => 3 }, 
    { pos => 8, len => 4 }, 
    { pos => 3, len => 2 }, 
); 
my $fmt = join " ", map { "\@$_->{pos} a$_->{len}" } @pos; 
# q{@12 a3 @8 a4 @3 a2}; 

my @a = unpack $fmt, $str; 
print "<$_>\n" for @a; 
print "\n"; 

my @sorted_idxes = 
    sort { $pos[$a]{pos} <=> $pos[$b]{pos} 
     or $pos[$a]{len} <=> $pos[$b]{len} } 
    0..$#pos; 

my $sorted_fmt = join " ", 
    map { "\@$pos[$_]->{pos} a$pos[$_]->{len}" } @sorted_idxes; 

my $out = pack $sorted_fmt, @a[@sorted_idxes]; 
$out =~ s/\0/./g; 
print "$out\n"; 
2

Вы не можете указывать pack в определенных местах внутри строки. Он не перемещается по строке с помощью «курсора», который можно переместить - он объединяет все предоставленное ему при написании новой строки.

пакет TEMPLATE, LIST
принимает список значений и преобразует его в строку, используя правила, приведенные в ОБРАЗЦА. Полученная строка представляет собой конкатенацию преобразованных значений.

Далее вниз страницы, то документы также говорят

Вы сами должны сделать любое выравнивание или отступы, вставив, например, достаточно «х» Ес при упаковке. Нет никакой возможности для пакета и распаковки знать, куда идут символы или из них, поэтому они обрабатывают свой вывод и вводят как плоские последовательности символов.

Вы можете, конечно, записать строку в любом случае вы хотите, но только перестановка шаблона (при попытке сделать из того, что заполняет по мере необходимости в @, переходя от нуля, и, таким образом, перезапись для каждое значение) и выписывание или заполнение «промежуточных позиций». Так что вы можете сказать

my $str = "...hi...defgabc"; 
my $fmt = q{@12 a3 @8 a4 @3 a2}; 

my @parts = unpack $fmt, $str; 
# Add to @parts and template what need be in between, or change $fmt to get all 
my $res = pack "A3A4A2", @parts; 

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

Что касается написания частей строки, это как раз задача substr.Поэтому, возможно, вы могли бы написать небольшой цикл, используя @fmt и/или @parts, в котором substr собирается заменить последовательности заданных длин в необходимых местах. Тем не менее, pack-все это сразу должно быть гораздо более эффективным.

+0

Спасибо, @zdim. См. Мой ответ ниже для одного способа, который позволяет избежать «substr» и looping. Я надеялся, что я просто что-то упустил в документации 'pack', так как' unpack' был настолько прост в использовании, чтобы извлекать поля, используя спецификацию вне порядка. –

+0

@JohnWiersba Приятно, вот что я имел в виду - вы можете обрабатывать вещи по-разному, а 'join' и' map' подходят для такого рода манипуляций. Я предложил 'substr' придерживаться вашей оригинальной идеи, чтобы эффективно писать части строки (это очень эффективно). Кроме того, учитывая его синтаксис, он должен быть очень чистым кодом. Или, поскольку вы добирались до 'pack', чтобы использовать его, но все части были перегруппированы. Я могу добавить эти подходы к тому, что я разместил, если это будет полезно. – zdim

+0

@JohnWiersba Они не совпадают - 'unpack' начинается со строки, и, таким образом, _can_ вытащить вещи (в то время как это не основное использование). Но 'pack' должен выписывать новую строку, и ей некуда идти, а конкат 'все части, которые она дана. Функциональность, которую вы хотели, предоставляется 'substr'. – zdim