如何在Perl中访问二进制标量的第n个字节?

时间:2009-07-16 21:27:08

标签: perl loops bytearray

提前感谢所有人。

我想访问二进制标量的第n个字节。例如,您可以在一个标量变量中获取所有文件数据......

想象一下,二进制数据被收集到标量......

open(SOURCE, "<", "wl.jpg"); 
my $thisByteData = undef; 
while(<SOURCE>){$thisByteData .= $_;} 
close SOURCE; 

$ thisByteData是原始二进制数据。当我使用length($ thisByteData)时,我得到了字节数,所以Perl知道它有多大。我的问题是如何访问第N个字节?

旁注:我的函数将接收这个二进制标量,它在我的函数中我想要访问第N个字节。关于如何收集这些数据的帮助是值得赞赏的,但不是我正在寻找的。无论其他程序员想要收集二进制数据的方式取决于他们,我的工作是在传递给我时获取第N个字节:)

再次非常感谢大家的帮助!


感谢@muteW让我比以往任何时候都更进一步。我想我不理解unpack(...)正确。

print(unpack("N1", $thisByteData));
print(unpack("x N1", $thisByteData));
print(unpack("x0 N1", $thisByteData));

返回以下内容:

4292411360
3640647680
4292411360

我认为这3行都会访问相同的(第一个)字节。不使用“x”只是“x”和“x $ pos”会产生意想不到的结果。

我也试过了......

print(unpack("x0 N1", $thisByteData));
print(unpack("x1 N1", $thisByteData));
print(unpack("x2 N1", $thisByteData));

返回...与上次测试相同......

4292411360
3640647680
4292411360

我肯定错过了有关解包如何工作的信息。


如果我这样做......

print(oct("0x". unpack("x0 H2", $thisByteData)));
print(oct("0x". unpack("x1 H2", $thisByteData)));
print(oct("0x". unpack("x2 H2", $thisByteData)));

我得到了我所期待的......

255
216
255

无需使用oct(),无法解压缩给我自己?


作为旁注:我认为在使用“x $ pos N1”时,我得到了这些字节整数的2的补码。我期待这些作为前3个字节。

255
216
255

再次感谢所有人的帮助。


特别感谢@brian d foy和@muteW ...我现在知道如何使用unpack(...)访问我的二进制标量的第N个字节。我现在有一个新问题要解决,这与这个问题无关。再次感谢所有帮助人员!

这给了我理想的结果......

print(unpack("x0 C1", $thisByteData));
print(unpack("x1 C1", $thisByteData));
print(unpack("x2 C1", $thisByteData));

unpack(...)有很多选项,所以我建议其他读这篇文章的人阅读pack / unpack文档,以获得他们选择的字节数据结果。我也没有尝试使用@brian提到的Tie选项,我希望尽可能简化代码。

5 个答案:

答案 0 :(得分:8)

如果您有一个字符串中的数据并且想要获得某个字节,请使用substr,只要您将该字符串视为要开头的字节。

但是,你可以直接从文件中读取它,而不是所有这些字符串无意义的人都在填补你的头脑。 :)使用sysopen和正确的选项打开文件,使用seek将自己放在您想要的位置,并使用sysread阅读所需内容。

您跳过了openreadline正在尝试为您做的事情的所有变通办法。如果您要关闭所有功能,请不要使用它们。

答案 1 :(得分:3)

我认为正确的答案涉及打包/解包,但这也可能有效:

use bytes;
while( $bytestring =~ /(.)/g ){
   my $byte = $1;
   ...
}

“use bytes”确保您永远不会看到字符 - 但如果您有一个字符串并将其作为字节处理,那么您做错了。 Perl的内部字符编码是未定义的,因此您在“使用字节”下的字符串中看到的数据几乎没有意义。

答案 2 :(得分:3)

由于您已经在$ thisByteData中拥有文件内容,因此可以使用pack / unpack来访问第n个字节。

sub getNthByte {
  my ($pos) = @_;
  return unpack("x$pos b1", $thisByteData);
}

#x$pos - treats $pos bytes as null bytes(effectively skipping over them) 
#b1    - returns the next byte as a bit string

阅读包文档,了解可在模板中使用的参数,以获得不同的返回值。

编辑 - 您在下面的评论表明您缺少第一个字节的高阶nybble('f')。我不确定为什么会发生这种情况但是这里有一种替代方法可行,与此同时我将进一步了解unpack的行为。

sub getNthByte {
  my ($pos) = @_;
  return unpack("x[$pos]H2", $binData);
}

(my $hex = unpack("H*", $binData)) =~ s/(..)/$1 /g;
#To convert the entire data in one go

使用此前四个字节的输出为 - 0xff 0xd8 0xff 0xe0,与documentation匹配。

答案 3 :(得分:2)

Perl内置变量$/(如果您$INPUT_RECORD_SEPARATOR use,则为English)控制Perl对“行”的概念。默认情况下,它设置为"\n",因此行由换行符(duh)分隔,但您可以将其更改为任何其他字符串。或者将其更改为对数字的引用:

$/ = \1;
while(<FILE>) {
  # read file
}

将其设置为对数字的引用将告诉Perl“行”是该字节数。

现在,你究竟想做什么?可能有许多模块会执行您正在尝试执行的操作,并且可能更有效。如果您只是想学习如何操作,请继续,但如果您有特定的任务,请考虑不要重新发明轮子(除非您想要)。

编辑:感谢评论中的jrockway ......

如果您有Unicode数据,这可能不会读取一个字节,而是一个字符,但如果发生这种情况,您应该能够use bytes;关闭自动字节转换。

现在,您说您希望一次读取所有数据,然后将其传递给函数。我们这样做:

my $data;
{
  local $/;
  $data = <FILE>;
}

或者这个:

my $data = join("", <FILE>);

或者有些人会建议File::Slurp模块,但我认为这有点过分。但是,让我们将整个文件放入一个字节数组中:

use bytes;

...

my @data = split(//, join("", <FILE>));

然后我们有一个字节数组,我们可以传递给一个函数。样的?

答案 4 :(得分:1)

如果不了解您正在尝试处理数据的内容,这样的内容将遍历文件中的字节:

open(SOURCE, "wl.jpg");
my $byte;
while(read SOURCE, $byte, 1) {
    # Do something with the contents of $byte
}
close SOURCE;

小心你的例子中使用的连接;您最终可能会遇到换行符,这绝对不是您在阅读二进制文件时想要发生的事情。 (在阅读标量的同时不断扩展标量也是低效的。)这是将整个文件转换为Perl标量的惯用方法:

open(SOURCE, "<", "wl.jpg");
local $/ = undef;
my $big_binary_data = <SOURCE>;
close SOURCE;