Выбрать главу

Например, целочисленное значение, возвращаемое функцией time(), и дробное значение, возвращаемое функцией rand(), можно упаковать в переменную $record с помощью шаблона 'l1 d1', который означает: "одно длинное целое число (long) и одно число с плавающей точкой двойной точности (double)".

$record = pack 'l1 d1', time(), rand(); #

Вот еще несколько несложных примеров использования разных шаблонов для функции pack():

$bin = pack('a5', 'Yes'); # в $bin будет: 'Yes\0\0'

$bin = pack('A5', 'Yes'); # в $bin будет: 'Yes '

$bin = pack('a4', 'abcd','x','y','z'); # в $bin: 'abcd'

$bin = pack('aaaa', 'abcd','x','y','z'); # в $bin: 'axyz'

$bin = pack('C2', 65,66,67); # в $bin будет: 'AB'

$bin = pack('U2', 0x263A, 0x263B); # в $bin будет: '??'

$bin = pack ('cxxc', 65,66); # в $bin будет: 'A\0\0B'

Для преобразования данных из двоичного вида применяется функция unpack(), которая распаковывает из скалярной переменной в список или массив значения двоичных данных в соответствии с указанным шаблоном.

@list_of_values = unpack($template, $binary_record);

Кроме того, с помощью функции unpack() можно из строки извлекать подстроки фиксированной длины. Например, так можно извлечь из записи файла поля определенной длины в переменные:

# Поля данных в записи файла:

# c 1 по 7 байт - номер телефона

# с 8 длиной 30 - фамилия, имя, отчество абонента

# с 38 длиной 25 - адрес

# 1234567Бендер Остап Ибрагимович РСФСР, Черноморск

($phone, $name, $address)= unpack('A7A30A25', $record);

Чтобы пропустить ненужные поля, достаточно указать в шаблоне пропуск определенного количества байтов. Например, так можно не извлекать поле с телефонным номером:

($name, $address)= unpack('x7A30A25', $record);

Подробное описание шаблонов и работы функций pack() и unpack() можно найти в стандартной документации с помощью все той же утилиты чтения документации:

perldoc perlpacktut

Для чтения двоичных данных или текстовых данных фиксированной длины применяется функция read(), которой в качестве аргументов передаются файловый манипулятор, скалярная переменная для вводимых данных и размер считываемого блока данных. Вот так, например, выглядит типичный цикл чтения двоичных данных:

until(eof($fh)) { # читать до достижения конца файла

# считать очередной блок данных и проверить его длину

read($fh, $record, $record_size) == $record_size

or die('Неправильная длина данных');

# распаковать данные по шаблону из $record в @data

@data = unpack($template, $record);

# обработать введенные данные...

}

При работе с данными фиксированной длины обычной практикой является считывание или запись данных в произвольном месте файла, например, при изменении только что считанного блока данных. Для этого нужно позиционировать позицию чтения или записи. Это делается с помощью функции seek(), которой передается три аргумента: файловый манипулятор, смещение в байтах и указатель позиции отсчета. Позиция отсчета задается числами: 0 - от начала файла, 1 - от текущей позиции, 2 - от конца файла. Например:

seek($handle, 64, 0); # переместиться на 64 байта от начала

seek($handle, 25, 1); # сместиться на 25 байт вперед

seek($handle, -10, 2); # установиться на 10 байт до конца

seek($handle, 0, 0); # установить позицию в начало файла

С помощью функции tell(), которая возвращает смещение относительно начала файла, можно узнать текущую позицию чтения-записи и использовать ее для дальнейших перемещений по файлу.

$pos = tell($handle); # запомнить текущую позицию в $pos

seek($handle, $pos-5, 1); # сместиться на 5 байт назад

В следующем примере увеличивается поле счетчика длиной 2 байта, расположенное в файле с позиции $new_pos: