我在使用pack
构建字符串时尝试重新排序字段,但我似乎无法让pack
做我想要的事情。例如,我想填充一个字符串,其中abc
位于偏移量12,defg
位于偏移量8,而hi
位于偏移量3(无论如何,可能是空格或\0
,在抵消0-2和5-7)。
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
,它\0
- 填充和截断为documented。有没有办法阻止它\0
- 填充和截断而不重新排序pack
模板以从左到右生成字段?
从外部源读取字段规范时会出现此问题。当然,可以安排pack
模板以从左到右的顺序生成,并且可以重新排序结果列表以匹配外部字段规范。但是,动态地重新定位pack
“光标”而不填写中间位置或截断将是非常方便的。
在上面的代码中,如果pack(...)
的返回值与$str
的返回值与.
的任何字节相同(例如空白或\0
),我会很高兴
答案 0 :(得分:2)
您不能在字符串内的特定位置写pack
。它不会移动一个带有“ cursor ”的字符串,这些字符串可以重新定位 - 它会在编写新字符串时连接给它的所有内容。
打包模板,列表
获取值的LIST并使用TEMPLATE给出的规则将其转换为字符串。结果字符串是转换值的串联。
在页面的下方,文档也说
您必须自己进行任何对齐或填充,例如,在包装时插入足够的“x”。打包和解包无法知道字符的来源或来源,因此它们处理输出并输入为平面字符序列。
当然,您可以以任何您想要的方式写出字符串,但只能通过重新排列模板(如果按顺序尝试,它会根据需要按@
填充,从零开始,从而覆盖对于每个值),并写出或填写“中间位置”。所以你可以说
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
一次性更高效。
答案 1 :(得分:1)
显然pack
无法直接执行此操作。这是一种方法,它可以避免循环和使用substr
。然而,与unpack
的易于理解相比,它并不十分令人满意。我希望我误解了pack
文档中的某些内容,这些内容确实允许pack
与unpack
相反,以便在pack
ed字符串中放置字段。< / p>
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";