在Perl中,我可以将字符串视为字节数组吗?

时间:2010-06-17 21:52:07

标签: perl string binary

在Perl中,将字符串用作包含8位数据的字节数组是否合适?我在这个主题上找到的所有文档都集中在7位字符串上。

例如,如果我将二进制文件中的一些数据读入$data

my $data;

open FILE, "<", $filepath;
binmode FILE;
read FILE $data 1024;

我希望得到第一个字节,substr($data,1,1)是否合适? (再次,假设它是8位数据)

我来自大多数C背景,我习惯将char指针传递给read()函数。我的问题可能是我不明白字符串的基础表示是什么在Perl中。

6 个答案:

答案 0 :(得分:6)

此处转载的read命令的捆绑文档提供了许多与您的问题相关的信息。

read FILEHANDLE,SCALAR,LENGTH,OFFSET

read FILEHANDLE,SCALAR,LENGTH

  

尝试将LENGTH 字符数据读入变量SCALAR           来自指定的FILEHANDLE。返回的数量           实际读取的字符,文件末尾为0,如果存在,则为undef           是一个错误(在后一种情况下,$!也设置)。 SCALAR会           增长或缩小,以便最后一个字符实际读取           阅读后标量的最后一个字符。

     

可以指定OFFSET将读取的数据放在某个地方           在除了开头之外的字符串中。消极的OFFSET           指定向后计数的多个字符的位置           从字符串的结尾。一个正的OFFSET大于           SCALAR的长度导致字符串被填充到           在读取结果之前,所需大小为“\ 0”字节           追加。

     

呼叫实际上是用Perl或者           system的fread()调用。要获得真正的read(2)系统调用,请参阅           “sysread执行”。

     

注意字符:取决于文件句柄的状态,           读取(8位)字节或字符。 默认全部           filehandles对字节进行操作,但是例如文件句柄           已经打开了“:utf8”I / O层(参见“打开”,和           “open”pragma,open),I / O将以UTF-8编码运行           Unicode字符,而不是字节。类似于“:encoding”           pragma:在这种情况下,几乎任何字符都可以读取。

答案 1 :(得分:2)

有关如何将字符串视为字节数组,请参阅perldoc -f packperldoc -f unpack

答案 2 :(得分:1)

如果要从二进制文件中读取字节,可能需要使用sysopensysread

另见perlopentut

这是否合适或取决于你究竟想做什么。

#!/usr/bin/perl -l

use strict; use warnings;
use autodie;

use Fcntl;

sysopen my $bin, 'test.png', O_RDONLY;
sysread $bin, my $header, 4;

print map { sprintf '%02x', ord($_) } split //, $header;

输出:

C:\Temp> t
89504e47

答案 3 :(得分:1)

字符串是“字符”的字符串,大于一个字节。1您可以在其中存储字节并将它们作为字符进行操作,取其substr个等等,只要你只是操纵内存中的实体,一切都很好看。数据存储很奇怪,但这主要不是你的问题。2

当您尝试从文件读取和写入时,您的字符可能不会映射到字节的事实变得重要且有趣。更不用说烦人了。 Perl尝试在常见情况下做你想做的事情实际上使这种烦恼变得更糟:如果字符串中的所有字符都适合一个字节并且你碰巧在非Windows操作系统上,你实际上并没有做任何特殊的读写字节。但是,Perl会抱怨,如果你已经存储了一个非字节大小的字符并尝试编写它而不给它一个关于如何处理它的线索。

这有点远,主要是因为编码是一个很大且令人困惑的话题。让我留下一些参考资料:查看Encode(3perl)open(3perl)perldoc openperldoc binmode,了解许多热闹和血腥的细节。

所以摘要答案是“是的,你可以将字符串视为包含字节,如果它们实际上包含字节,你可以通过只读取和写入字节来保证。”。

1:或者迂腐地说,“它可以表示比字节更大的值范围,尽管它们在方便时存储为字节”。我想。

2:对于记录,Perl中的字符串在内部由称为“PV”的数据结构表示,除了字符指针之外,它还知道字符串的长度和{{的当前值。 1}}。3

3:如果它开始变得有趣,它将开始存储pos的当前值。另见

pos

答案 4 :(得分:0)

如果你告诉我们你要对字节数组做什么,这可能会有所帮助。有多种方法可以处理二进制数据,每种方法都适用于不同的工具集。

您想将数据转换为Perl数组吗?如果是这样,packunpack是一个良好的开端。 split也可以派上用场。

是否要在不解压缩的情况下访问字符串的各个元素?如果是这样,substr速度很快,将为8字节数据提供技巧。如果您想要其他位深度,请查看vec函数,该函数将字符串作为位向量。

您想扫描字符串并将某些字节转换为其他字节吗?然后,s///tr///结构可能会有用。

答案 5 :(得分:0)

请允许我发布一个关于将字符串视为二进制数组的小例子 - 因为我自己发现很难相信称为“substr”的东西会处理空字节;但看起来确实如此 - 下面是perl调试器终端会话的片段(包括字符串和数组/列表方法):

$ perl -d

Loading DB routines from perl5db.pl version 1.32
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

^D
Debugged program terminated.  Use q to quit or R to restart,
  use o inhibit_exit to avoid stopping after program termination,
  h q, h R or h o to get additional info.  

  DB<1> $str="\x00\x00\x84\x00"

  DB<2> print $str
�
  DB<3> print unpack("H*",$str) # show content of $str as hex via `unpack`
00008400
  DB<4> $str2=substr($str,2,2)

  DB<5> print unpack("H*",$str2)
8400
  DB<6> $str2=substr($str,1,3)

  DB<7> print unpack("H*",$str2)
008400

[...]

  DB<30> @stra=split('',$str); print @stra # convert string to array (by splitting at empty string)
�
  DB<31> print unpack("H*",$stra[3]) # print indiv. elems. of array as hex
00
  DB<32> print unpack("H*",$stra[2]) 
84
  DB<33> print unpack("H*",$stra[1]) 
00
  DB<34> print unpack("H*",$stra[0]) 
00
  DB<35> print unpack("H*",join('',@stra[1..3])) # print only portion of array/list via indexes (using flipflop [two dots] operator) 
008400