unpack()
Scopri la funzione PHP unpack(): panoramica, codici di formato, endianness, insidie comuni ed esempi pratici di utilizzo.
Le stringhe PHP sono fondamentalmente sequenze di byte grezzi, il che le rende un contenitore naturale per dati binari — intestazioni di immagini, pacchetti di rete, formati di file e frame di protocollo. La funzione unpack() legge quel flusso di byte grezzi e lo converte in valori PHP ordinari (interi, float, stringhe) con cui puoi lavorare. Questo articolo tratta la firma della funzione, i suoi codici di formato, l'endianness, le insidie più comuni e come si abbina a pack().
Sintassi
unpack(string $format, string $data, int $offset = 0): array|false| Parametro | Descrizione |
|---|---|
$format | Una stringa di formato che descrive come interpretare i byte (i codici sono elencati di seguito). |
$data | La stringa binaria da leggere. |
$offset | Posizione in byte da cui iniziare la lettura (aggiunto in PHP 7.1). Il valore predefinito è 0. |
Restituisce un array associativo dei valori decompressi, oppure false in caso di errore. unpack() è l'inverso di pack(): qualsiasi layout scritto con pack() può essere riletto con gli stessi codici di formato.
Primo esempio
La stringa di formato è una sequenza di uno o più codici. Ogni codice è una singola lettera per un tipo di dato, un conteggio di ripetizioni opzionale e un nome opzionale.
Qui "C*" significa "leggi ogni byte rimanente come un intero senza segno a 8 bit". Il conteggio di ripetizioni * consuma tutti i byte disponibili. Quando non si fornisce un nome, unpack() numera i risultati a partire da 1 (non da 0):
Array
(
[1] => 1
[2] => 2
[3] => 3
[4] => 4
[5] => 5
)Codici di formato
Ogni codice corrisponde a un numero fisso di byte. I più comuni:
| Codice | Tipo | Dimensione |
|---|---|---|
C / c | Char senza segno / con segno | 1 byte |
n | Short senza segno, big-endian | 2 byte |
v | Short senza segno, little-endian | 2 byte |
S / s | Short senza segno / con segno, ordine byte della macchina | 2 byte |
N | Long senza segno, big-endian | 4 byte |
V | Long senza segno, little-endian | 4 byte |
L / l | Long senza segno / con segno, ordine byte della macchina | 4 byte |
f / d | Float / double, ordine della macchina | 4 / 8 byte |
a / A | String (NUL-padded / space-padded) | come specificato |
H / h | Stringa esadecimale, nibble alto / basso per primo | per nibble |
Un numero dopo un codice lo ripete (C4 legge quattro char); un * legge tutti i byte rimanenti.
Assegnare nomi ai campi
I formati binari reali sono composti da campi misti, quindi di solito si assegna un nome a ciascuno e si separano i codici con /:
"C2chars/Sint/Nlong" legge i primi due byte come chars1/chars2, i successivi due come uno short int nell'ordine della macchina, e gli ultimi quattro come un long big-endian long:
Array
(
[chars1] => 1
[chars2] => 2
[int] => 1027
[long] => 84281096
)Quando un codice ha un conteggio di ripetizioni e un nome, unpack() aggiunge un indice al nome (chars1, chars2, …) in modo che i valori non collidano.
L'endianness è importante
Gli stessi quattro byte significano numeri diversi a seconda dell'ordine dei byte. N/n sono big-endian (ordine di rete); V/v sono little-endian (nativo x86); S/L seguono la macchina host e quindi non sono portabili. Per dati che transitano tra macchine diverse — un formato di file o un protocollo di rete — scegli sempre un codice con endianness esplicita in modo che il risultato sia lo stesso ovunque.
<?php
$bytes = "\x01\x00\x00\x00";
print_r(unpack("Vlittle", $bytes)); // little-endian: 1
print_r(unpack("Nbig", $bytes)); // big-endian: 16777216
?>Array
(
[little] => 1
)
Array
(
[big] => 16777216
)Round-trip con pack()
Poiché unpack() è l'immagine speculare di pack(), puoi serializzare valori in un blob binario compatto e rileggerli direttamente con il formato corrispondente:
<?php
$packed = pack("nN", 1027, 84281096); // build the bytes
$result = unpack("nshort/Nlong", $packed);
print_r($result);
?>Array
(
[short] => 1027
[long] => 84281096
)Insidie comuni
- Le chiavi partono da 1. I risultati senza nome sono indicizzati a partire da 1, il che può creare confusione nei cicli. Assegna nomi ai campi o ricorda l'offset.
- I nomi con conteggio di ripetizioni ottengono un suffisso indice (
byte1,byte2), quindiunpack("C4byte", ...)restituiscebyte1…byte4, non un unicobyte. - I codici nell'ordine della macchina (
S,L,s,l) non sono portabili. Usan/Nov/Vper qualsiasi dato memorizzato o trasmesso. falsecon dati insufficienti. Se il formato richiede più byte di quanti ne contenga$data,unpack()restituiscefalseed emette un avviso — controlla il valore restituito prima di usarlo.
Conclusione
La funzione unpack() converte byte grezzi in valori PHP usando codici di formato compatti, ed è la metà di lettura della coppia pack(). Padroneggia i codici di endianness e la sintassi di denominazione dei campi e potrai analizzare praticamente qualsiasi intestazione di file binario o frame di rete. Per convertire dati binari in una stringa esadecimale leggibile, vedi bin2hex().