Member 13967442 Ответов: 2

Распакуйте двоичный файл на PHP


<?php

$file = "/tmp/kok.bin";

$fp = fopen ($file, 'rb')
    or die ("File $file cannot be opened.");


$data = fread ($fp, filesize($file));
    or die ("Could not read data from file $file");

# Create the format for unpacking the header data
$header_format = 
    'Cid/' .            # Grab 1 byte      
    'Cnumber/' .        # Grab 1 byte
    'CIdentifier/' .    # Grab 1 byte     
    'CTime/' .          # Grab 8 bytes 
    'L# of Records/' .  # Grab 1 byte , number of target in a packet
    'SHeader Size/' .   # Grab 4 bytes
    'SRecord Size';     # Grab 2 bytes

# Unpack the header data
$header = unpack ($header_format, $data);
$targetRecord =$header_format['L# of Records'];

    
# Create the format for unpacking the data that describes the format of the records in the file
$record_format = 
    'CField Name/' .    # Grab 1 byte
    'fField Type1/' .   # Grab 4 bytes 
    'fField Type2/' .   # Grab 4 bytes
    'fField Type3/' .   # Grab 4 bytes 
    'fField number/' .  # Grab 4 bytes           
    'fdistance/' .      # Grab 4 bytes
    'CField id /'       # Grab 1 byte 
    'iField Precision/' # Grab 4 bytes the size of the Field Data
    'SField Data';      # Grab x bytes 
for ($offset = 0; $offset < strlen ($data); $offset ++) {
    print_r (unpack ("$record_format", $data));
}
?>




1 first unpack $header_format to get 'L# of Records' for every packet.(have a total of 200 packets)
2 unpack  $record_format  'L# of Records' times
$record_format = 
     CField Name/' .    # Grab 1 byte
    'fField Type1/' .   # Grab 4 bytes 
    'fField Type2/' .   # Grab 4 bytes
    'fField Type3/' .   # Grab 4 bytes 
    'fField number/' .  # Grab 4 bytes           
    'fdistance/' .      # Grab 4 bytes
    'CField id /'       # Grab 1 byte 
    'iField Precision/' # Grab 4 bytes
    'SField Data';      # Grab x bytes 

3 Step 1 and 2 are for one packet from the file.
4 Repeat 1-3 until end of file is reached.  


Что я уже пробовал:

for ($offset = 0; $offset < strlen ($data); $offset += 32) {
    $unpackeddata= unpack ("@$offset/$header_format", $data);
    print "";
    print_r ($unpackeddata);
    print "";
}

Patrice T

Мы понятия не имеем, о чем вы говорите.
Дайте контекст.
Мы не АНБ, мы не можем видеть ваш экран или получить доступ к вашему жесткому диску

Member 13967442

Я попытался немного объяснить,добавил больше кода

2 Ответов

Рейтинг:
14

Jochen Arndt

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

$header = unpack ($header_format, $data);
// Note that your code used header_format here wrongly
$numRecords = $header['L# of Records'];
// Assuming these are what the name indicates
$offset = $header['SHeader Size'];
$recordSize = $header['SRecord Size'];
for ($i = 0; $i < $numRecords; $i++)
{
    $record = unpack ($record_format, $data, $offset);
    $offset += $recordSize;
}
Для этого необходимо настроить unpack() правильно отформатируйте строки, чтобы они соответствовали данным в файле. То CTime поле, например, выглядит подозрительно, потому что я понятия не имею, как значение времени может храниться в 8 битах.

[РЕДАКТИРОВАТЬ]
Если существует несколько пакетов, начинающихся с заголовка, вы должны поместить вышеизложенное во внешний цикл:
$offset = 0;
$data_len = strlen($data);
while ($offset < $data_len)
{
    // Get package header
    $header = unpack ($header_format, $data, $offset);
    // Number of records in this package
    $numRecords = $header['L# of Records'];
    // Size of each record for this packet
    $recordSize = $header['SRecord Size'];
    // Adjust offset to point to first record of this packet
    $offset += $header['SHeader Size'];
    // Process records of this packet
    for ($i = 0; $i < $numRecords; $i++)
    {
        $record = unpack ($record_format, $data, $offset);
        // Adjust offset to point to next record of this packet
        $offset += $recordSize;
    }
    // $offset points to next package header if not at end of data
}
[/РЕДАКТИРОВАТЬ]


Patrice T

Это файл .dbf.
ОП счастливо перепутал набор записей с тем, что на самом деле является заголовками полей.
Первый заголовок начинается со смещения 32, и каждый заголовок имеет размер 32.

Jochen Arndt

Это было не совсем понятно для меня, потому что предоставленные строки формата не соответствовали структурам DBF.

Но мое решение все еще показывает, как итеративно корректировать смещение по мере необходимости.

Более важно то:
Похоже, что он использовал символы типа данных DBF в своих строках формата PHP unpack вместо использования соответствующих идентификаторов PHP.

Member 13967442

Этот файл не является файлом .dbf, это файл .bin с каждым пакетом, имеющим размер заголовка 18. и "CTime/" является неправильным "dTime", так как это временная метка

Patrice T

Откуда взялась эта линия ?
"# Получить верхнюю часть заголовка файла dbf"

Member 13967442

извините это ошибка

Jochen Arndt

Ваш набор записей на самом деле имеет размер 28 и заголовок 12 с одним байтом для поля времени. Временная метка может иметь 4 или 8 байт, что в общей сложности составляет 15 или 19.

Member 13967442

запись имеет размер 30, а заголовок-18, Вот как я ее посчитал

Member 13967442

$заголовок = распаковать ($header_format, $данных);
$numRecords = $header['L# записей'];

для ($i = 0; i < $numRecords; $i++)
{
$запись = распаковать ($record_format, $данных $я);
}

Я считаю, что приведенный выше код может распаковать первый пакет, как мне сделать это для следующего пакета до конца файла

Jochen Arndt

C = 1 байт, S = 2 байта, L = 4 байта, I = зависит, Q = 8 байт, f = 4 байта (одинарная точность)

Jochen Arndt

Как и в моем решении:
Третий параметр задает смещение в данные.
Так что это должно быть установлено соответствующим образом (размер заголовка для первой записи, а затем увеличен на размер записи).

Member 13967442

а как насчет чтения следующего заголовка пакета?

Jochen Arndt

Я тебя не понимаю.
Если структура файла такая, как предполагается в моем решении, то у вас есть заголовок и наборы записей. Затем вы получаете доступ к заголовку сначала с нулевым смещением, а затем к наборам записей с соответствующим смещением.

Member 13967442

я хочу сказать, что вышеприведенное решение работает только для первого пакета,
$numRecords = $header['L# of Records']; // значение в $numRecords-это количество записей в первом первом пакете. как сделать цикл, чтобы получить $numRecords в заголовочном файле каждого пакета.

Jochen Arndt

На этот вопрос можно ответить только зная файловую структуру, которую я не знаю.

Мое решение заключается в следующем
- один заголовок, содержащий информацию о # следующих записей
- 1. набор записей
- 2. набор записей
- ...
- количество наборов - 1 набор

Если файловая структура отличается, Вы должны сообщить нам ее структуру.

Member 13967442

один заголовок содержит информацию о записях#, которые необходимо распаковать для данного конкретного пакета. и поэтому информация в каждом заголовке различна. например, один пакет может иметь 2 записи или следующий пакет может быть 5...в соответствии с чтением в каждом заголовке $['L# записей'];

Jochen Arndt

Затем вы должны написать внешний цикл вокруг моего решения. Я его обновлю.

Рейтинг:
1

Patrice T

Цитата:
Мне нужно распаковать оставшуюся часть пакета.

Я думаю, что ваша проблема заключается в том, что вы всегда распаковываете файл в начале
unpack ("$record_format", $data)

вместо позиции того, что вы хотите распаковать.
Начало заголовка файла со смещением 0
Первое поле заголовка по смещению 32
Второе поле заголовка по смещению 64
...


Но $данные вот и весь файл.
.dbf - Википедия[^]

[Обновление]
- Нет!
Это начало цикла в 32 и это не header_format:
for ($offset = 0; $offset < strlen ($data); $offset += 32) {
    $unpackeddata= unpack ("@$offset/$header_format", $data);
    print "";
    print_r ($unpackeddata);
    print "";
}


[Обновление]
Все, что касается .dbf в решении, неверно, потому что
# Get the top of the dbf file header

в исходном коде это тоже неправильно.


Patrice T

Воспользуйся Улучшить вопрос чтобы обновить ваш вопрос.
Чтобы каждый мог обратить внимание на эту информацию.