2010-06-17 54 views
6

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

举例来说,如果我读一个二进制文件的一些数据到$data

my $data; 

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

,我想第一个字节出来,是substr($data,1,1)合适? (再次假设它是8位数据)

我来自一个主要是C背景,我习惯于将一个char指针传递给一个read()函数。我的问题可能是我不明白Perl中字符串的底层表示是什么。

回答

6

read命令,这里转载捆绑的文档,提供了大量的信息,是有关你的问题。

read FILEHANDLE,SCALAR,LENGTH,OFFSET

read FILEHANDLE,SCALAR,LENGTH

尝试读取LENGTH 字符数据的到从指定变量FILEHANDLE SCALAR 。返回实际读取的 个字符的数量,文件结束时为0,如果 是错误(在后一种情况下也设置$!),则返回undef。将SCALAR 放大或缩小,以便实际读取的最后一个字符是标量的最后一个字符 。

可以指定OFFSET将读取的数据放在 以外的字符串中。负数OFFSET 指定放置在从字符串末尾向后倒数 的许多字符处。大于SCALAR长度的正偏移量会导致在读取结果为 之前,字符串被填充为 所需的大小,其中“\ 0”字节为 。

该调用实际上是通过Perl's或 系统的fread()调用实现的。要获得真正的read(2)系统调用,请参阅 “sysread”。

注意字符:取决于文件句柄的状态, 要么读取(8位)字节或字符。默认情况下,所有 文件句柄都以字节为单位进行操作,但例如,如果文件句柄 已使用“:utf8”I/O层打开(请参阅“打开”,并打开 “open”杂注,open) O将使用UTF-8编码的 Unicode字符,而不是字节。类似于“:编码” 编译指示:在这种情况下几乎可以读取任何字符。

+1

我的性质很迂腐,当我在文档中读到这个时,我发现'character'不明确。我不清楚它是指一个数据单元(即一个字节)还是一个字符串单元(取决于编码) – Mike 2010-06-17 22:03:09

+4

调用'binmode FILE,':raw“或”binmod FILE“:bytes”'将始终无论您的默认IO层如何(例如,如果声明“使用utf8”),以“字节”模式打开文件句柄。 – mob 2010-06-17 22:04:39

+0

我实际上同意使用“字符”对我来说就像是一个bug,尤其是考虑到“Encode(3perl)”中字符,字节和八位字节之间的区别。它恰好是合适的词,但是如果它说“字符(由当前I/O层定义)”,我想我会喜欢它。我猜这也是对你的回答的批评,因为'read'总是读取“字符” - 但有时“字符”被定义为“八位字节”,有时被定义为“UTF-8码位”。 – darch 2012-10-31 16:01:54

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
0

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

是否要将数据转换为Perl数组?如果是这样,packunpack是一个好的开始。 split也可以派上用场。

是否要访问字符串的单个元素而不拆开它?如果是这样,substr是快速的,将为8字节数据做伎俩。如果您需要其他位深度,请查看vec函数,该函数将字符串作为位向量来执行。

是否要扫描字符串并将某些字节转换为其他字节?那么s///tr///构造可能会有用。

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 
1

字符串是“字符”,这比一个字节大的字符串。 1你可以将字节存储在它们中,并将它们作为字符来操纵它们,其中包括substr等等,只要你只是操纵内存中的实体,一切都非常漂亮。数据存储很奇怪,但这大多不是你的问题。 2

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

这是一个有点远的地方,很大程度上是因为编码是一个大而令人困惑的话题。让我在此处留下一些参考资料:请看Encode(3perl)open(3perl),perldoc openperldoc binmode,以获得许多热闹和血腥的细节。

所以总结答案是“是的,你可以把字符串视为它们包含字节,如果它们确实包含字节的话,你可以通过只读和写字节来保证。”

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

2:对于记录来说,Perl中的字符串在内部由称为'PV'的数据结构表示,除了字符指针外,它还知道像字符串长度和当前值pos3

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

use Devel::Peek; 

my $x = "bluh bluh bluh bluh"; 
Dump($x); 
$x =~ /bluh/mg; 
Dump($x); 
$x =~ /bluh/mg; 
Dump($x); 
相关问题