5

提前感谢大家。

我想访问二进制标量的第 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

我肯定错过了一些关于 unpack 如何工作的东西。


如果我这样做...

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(...) 有很多选项,所以我建议阅读此文件的任何其他人阅读打包/解包文档以获取他们选择的字节数据结果。我也没有尝试使用@brian 提到的 Tie 选项,我想让代码尽可能简单。

4

5 回答 5

8

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

但是,您可以直接从文件中读取它,而无需人们一直在用这些字符串胡说八道。:) 使用sysopen和正确的选项打开文件,使用seek将自己放在您想要的位置,然后使用sysread阅读您需要的内容。

您跳过所有解决方法来open尝试readline为您做的事情。如果您只是要关闭它们的所有功能,甚至不要使用它们。

于 2009-07-16T23:31:58.253 回答
3

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

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

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

于 2009-07-16T21:40:03.657 回答
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,与文档匹配。

于 2009-07-17T04:05:35.800 回答
2

Perl 内置变量$/(或者$INPUT_RECORD_SEPARATOR如果你正在使用 in )控制 PerluseEnglish“线”概念。默认情况下,它设置为"\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>));

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

于 2009-07-16T21:35:02.107 回答
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;
于 2009-07-16T21:35:42.637 回答