2010-06-17 2 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

Попытки прочитать Длина символов данных в переменную SCALAR из указанного FILEHANDLE. Возвращает число символов, действительно прочитанных, 0 в конце файла или undef, если был ошибкой (в последнем случае также установлен $!). SCALAR будет быть выращенным или сжатым, так что последний символ, фактически прочитанный, - это последний символ скаляра после чтения.

Смещение может быть указано для размещения прочитанных данных в каком-либо месте в строке, отличной от начала. Отрицательный OFFSET определяет размещение на этом количестве символов, отсчитывающих назад от конца строки. Положительное СМЕЩЕНИЕ больше длины SCALAR приводит к тому, что строка заполняется нулевым размером с байтами «\ 0» до того, как результат чтения будет добавлен .

Звонок фактически реализован в терминах вызова Fread() для Perl или . Чтобы получить реальный системный вызов read (2), см. «sysread».

Обратите внимание на символы : в зависимости от состояния дескриптора файла, считываются либо (8-разрядные) байты, либо символы.По умолчанию все дескрипторы файлов работают с байтами, но, например, если файл-дескриптор был открыт слоем ввода-вывода «: utf8» (см. «Открыть» и открыта «открытая» правая кнопка ) O будет работать с кодировкой UTF-8 Юникодными символами, а не байтами. Аналогично для «: encoding» прагма: в этом случае почти любые символы могут быть прочитаны.

+1

моя природа была очень педантичной, когда я прочитал это в документации, я обнаружил «характер» неоднозначным. Я был неясно, если это означает единицу данных (то есть один байт) или единицу строки (в зависимости от кодировки) – Mike

+4

Вызов 'binmode FILE ': raw« 'или' binmod FILE »: байты« всегда будут откройте свой дескриптор файла в режиме «байт», независимо от вашего уровня ввода-вывода по умолчанию (скажем, если вы объявили 'use utf8'). – mob

+0

Я действительно согласен с тем, что использование «символов» читается как ошибка для меня, особенно учитывая, сколько внимания уделяется различию между символами, байтами и октетами в «Encode (3perl)». Это, оказывается, правильное слово, но я думаю, мне понравилось бы, если бы он сказал «символы (как определено текущим уровнем ввода/вывода)». Я думаю, это также критика вашего ответа, поскольку 'read' всегда читает« символы », но иногда« символ »определяется как« октет », а иногда как« кодовая точка UTF-8 ». – darch

1

Возможно, вы хотите использовать sysopen и sysread, если хотите прочитать байты из двоичного файла.

См. Также 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? Если да, то pack и unpack - хорошее начало. 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 open и perldoc binmode для множества веселых и подробных деталей.

Таким образом, итоговый ответ «Да, вы можете обрабатывать строки так, как если бы они содержали байты, если они действительно содержат байты, которые вы можете обеспечить только чтением и записью байтов».

1: Или педантично ", который может выражать больший диапазон значений, чем байт, хотя они сохраняются как байты, когда это удобно". Я думаю.

2: Для записи строки в Perl внутренне представлены структурой данных, называемой «PV», которая в дополнение к указателю символа знает такие вещи, как длина строки и текущее значение pos. 3

3: Ну, он начнет хранить текущее значение pos, если оно начнет быть интересным. См. Также

use Devel::Peek; 

my $x = "bluh bluh bluh bluh"; 
Dump($x); 
$x =~ /bluh/mg; 
Dump($x); 
$x =~ /bluh/mg; 
Dump($x); 
Смежные вопросы