2010-06-17 9 views
6

En Perl, ¿es apropiado utilizar una cadena como una matriz de bytes que contiene datos de 8 bits? Toda la documentación que puedo encontrar sobre este tema se centra en cadenas de 7 bits.En Perl, ¿puedo tratar una cadena como una matriz de bytes?

Por ejemplo, si leo algunos datos de un archivo binario en $data

my $data; 

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

y quiero conseguir el primer byte a cabo, es substr($data,1,1) apropiado? (de nuevo, suponiendo que son datos de 8 bits)

Vengo de un fondo en su mayoría C, y estoy acostumbrado a pasar un puntero char a una función read(). Mi problema podría ser que no entiendo cuál es la representación subyacente de una cadena en Perl.

Respuesta

6

La documentación para el comando incluido read, reproducido aquí, proporciona una gran cantidad de información que es relevante a su pregunta.

read FILEHANDLE,SCALAR,LENGTH,OFFSET

read FILEHANDLE,SCALAR,LENGTH

intentos de leer LONGITUD caracteres de datos en ESCALAR variables del FILEHANDLE especificado. Devuelve el número de caracteres realmente leídos, 0 al final del archivo, o undef si hay era un error (en este último caso $! También está configurado). SCALAR será crecido o reducido de modo que el último carácter realmente leído sea el último carácter del escalar después de la lectura.

Se puede especificar un DESPLAZAMIENTO para colocar los datos leídos en algún lugar en la cadena que no sea el comienzo. Un OFFSET negativo especifica la ubicación en los muchos caracteres que cuentan hacia atrás desde el final de la cadena. Un OFFSET positivo mayor que longitud de SCALAR da como resultado que la cadena se rellene al tamaño requerido con "\ 0" bytes antes de que el resultado de la lectura sea adjunto.

La llamada se implementa realmente en términos de la llamada a fread() del sistema de Perl o . Para obtener una verdadera llamada de sistema de lectura (2), consulte "sysread".

Tenga en cuenta caracteres: según el estado de la manejador de archivos, se leen (8 bits) bytes o caracteres.De forma predeterminada, todos los manejadores de archivos operan en bytes, pero por ejemplo si se abre el identificador de archivos con la capa de E/S ": utf8" (vea "abrir", y el pragma "abrir" , abrir), O operará en caracteres Unicode codificados en UTF-8, no en bytes. De forma similar para el ": codificación" pragma: en ese caso casi todos los caracteres se pueden leer.

+1

mi naturaleza es muy pedante, cuando leí esto en la documentación encontré 'character' ambiguo. No estaba claro si esto significa una unidad de datos (es decir, un byte) o una unidad de cadena (depende de la codificación) – Mike

+4

Llamar a 'binmode FILE,": raw "' o 'binmod FILE,": bytes "' siempre abra su manejador de archivos en el modo "bytes", independientemente de su capa IO predeterminada (por ejemplo, si declaró 'use utf8'). – mob

+0

De hecho estoy de acuerdo en que el uso de "caracteres" me parece un error, especialmente teniendo en cuenta la atención que se presta a la distinción entre caracteres, bytes y octetos en 'Codificar (3perl)'. Resulta ser la palabra correcta, pero creo que me gustaría si dijera "caracteres (como se define en la capa de E/S actual)". Supongo que esto también es una crítica a su respuesta, ya que 'leer' siempre dice" caracteres ", pero a veces" carácter "se define como" octeto "y a veces como" punto de código UTF-8 ". – darch

1

Es probable que desee utilizar sysopen y sysread si desea leer los bytes del archivo binario.

Véase también perlopentut.

Si esto es apropiado o necesario depende de qué es exactamente lo que está tratando de hacer.

#!/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; 

Salida:

C:\Temp> t 
89504e47
0

Podría ayudar más si nos dice lo que está tratando de hacer con la matriz de bytes. Hay varias maneras de trabajar con datos binarios, y cada uno se presta a un conjunto diferente de herramientas.

¿Desea convertir los datos en una matriz Perl? Si es así, pack y unpack son un buen comienzo. split también podría ser útil.

¿Desea acceder a elementos individuales de la cuerda sin desembalarla? Si es así, substr es rápido y hará el truco para datos de 8 bytes. Si desea otras profundidades de bits, eche un vistazo a la función vec, que pisa una cadena como un vector de bits.

¿Desea escanear la cadena y convertir ciertos bytes a otros bytes? Entonces las construcciones s/// o tr/// podrían ser útiles.

0

Permítame simplemente publicar un pequeño ejemplo sobre el tratamiento de cadenas como matriz binaria, ya que a mí me costaba creer que algo llamado "substr" manejara bytes nulos; pero al parecer sí - a continuación es un fragmento de una sesión de terminal Perl depurador (tanto con cadena y la matriz/lista de enfoques):

$ 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

Las cadenas son cadenas de caracteres "", que son más grandes que un byte. 1 Puede almacenar bytes en ellos y manipularlos como si fueran caracteres, tomando substr s de ellos y demás, y siempre y cuando solo esté manipulando entidades en la memoria, todo es muy bonito. El almacenamiento de datos es extraño, pero ese no es tu problema. 2

Cuando intenta leer y escribir desde archivos, el hecho de que sus caracteres no se correlacionen con bytes se vuelve importante e interesante. Por no mencionar molesto. Esta molestia en realidad se empeora un poco cuando Perl intenta hacer lo que desea en el caso común: si todos los caracteres de la cadena encajan en un byte y usted está en un sistema operativo que no es de Windows, en realidad no tiene hacer algo especial para leer y escribir bytes. Sin embargo, Perl se quejará si ha almacenado un personaje que no sea de tamaño byte y trate de escribirlo sin darle una pista sobre qué hacer con él.

Esto se está alejando un poco, en gran parte porque la codificación es un tema grande y confuso. Déjame dejarlo allí con algunas referencias: mira Encode(3perl), open(3perl), perldoc open, y perldoc binmode para obtener muchos detalles hilarantes y sangrientos.

Por lo tanto, la respuesta resumida es "Sí, puede tratar las cadenas como si contuvieran bytes si de hecho contienen bytes, lo cual puede asegurar solo leyendo y escribiendo bytes".

1: O pedantemente, "que puede expresar un rango de valores mayor que un byte, aunque se almacenan como bytes cuando es conveniente". Creo.

2: Para el registro, las cadenas en Perl están representadas internamente por una estructura de datos llamada 'PV' que además de un puntero de carácter conoce cosas como la longitud de la cadena y el valor actual de pos. 3

3: Bueno, comenzará a almacenar el valor actual de pos si comienza a ser interesante. Véase también

use Devel::Peek; 

my $x = "bluh bluh bluh bluh"; 
Dump($x); 
$x =~ /bluh/mg; 
Dump($x); 
$x =~ /bluh/mg; 
Dump($x); 
Cuestiones relacionadas