Created
May 7, 2026 09:01
-
-
Save s1037989/86eaf9061211c0a53940b0d1e00c4e66 to your computer and use it in GitHub Desktop.
perl pack / unpack
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Perl’s pack and unpack templates define how binary data is converted to and from Perl scalars. Below are practical examples for the most commonly used templates. | |
| Basic Integer Templates | |
| Signed / Unsigned 8-bit | |
| c — signed char | |
| my $bin = pack('c', -5); | |
| my $num = unpack('c', $bin); | |
| print "$num\n"; # -5 | |
| C — unsigned char | |
| my $bin = pack('C', 250); | |
| my $num = unpack('C', $bin); | |
| print "$num\n"; # 250 | |
| 16-bit Integers | |
| s / S — native endian short | |
| my $bin = pack('S', 65535); | |
| my $num = unpack('S', $bin); | |
| print "$num\n"; | |
| Little endian | |
| v — unsigned 16-bit little endian | |
| my $bin = pack('v', 0x1234); | |
| print unpack('H*', $bin), "\n"; | |
| # 3412 | |
| Big endian | |
| n — unsigned 16-bit network order | |
| my $bin = pack('n', 0x1234); | |
| print unpack('H*', $bin), "\n"; | |
| # 1234 | |
| 32-bit Integers | |
| l / L — native long | |
| my $bin = pack('L', 123456789); | |
| my $num = unpack('L', $bin); | |
| print "$num\n"; | |
| Little endian | |
| V — unsigned 32-bit little endian | |
| my $bin = pack('V', 0x12345678); | |
| print unpack('H*', $bin), "\n"; | |
| # 78563412 | |
| Big endian | |
| N — unsigned 32-bit network order | |
| my $bin = pack('N', 0x12345678); | |
| print unpack('H*', $bin), "\n"; | |
| # 12345678 | |
| 64-bit Integers | |
| q / Q — signed/unsigned quad | |
| my $bin = pack('Q', 0x1122334455667788); | |
| printf "%x\n", unpack('Q', $bin); | |
| Floating Point | |
| f — float | |
| my $bin = pack('f', 3.14); | |
| my $num = unpack('f', $bin); | |
| print "$num\n"; | |
| d — double | |
| my $bin = pack('d', 3.1415926535); | |
| my $num = unpack('d', $bin); | |
| print "$num\n"; | |
| Strings | |
| a — null padded string | |
| my $bin = pack('a10', 'abc'); | |
| print unpack('H*', $bin), "\n"; | |
| # 61626300000000000000 | |
| A — space padded string | |
| my $bin = pack('A10', 'abc'); | |
| print unpack('H*', $bin), "\n"; | |
| # 61626320202020202020 | |
| Z — null terminated string | |
| my $bin = pack('Z10', 'abc'); | |
| print unpack('H*', $bin), "\n"; | |
| # 61626300000000000000 | |
| Raw Binary / Hex | |
| H — high nibble first hex | |
| my $bin = pack('H*', 'deadbeef'); | |
| print unpack('H*', $bin), "\n"; | |
| # deadbeef | |
| h — low nibble first hex | |
| my $bin = pack('h*', '1234'); | |
| print unpack('H*', $bin), "\n"; | |
| Bit Strings | |
| B — descending bit order | |
| my $bin = pack('B8', '10101010'); | |
| print unpack('H*', $bin), "\n"; | |
| # aa | |
| b — ascending bit order | |
| my $bin = pack('b8', '10101010'); | |
| print unpack('H*', $bin), "\n"; | |
| # 55 | |
| Unicode | |
| U — Unicode code point | |
| my $bin = pack('U', 0x263A); | |
| print $bin, "\n"; # ☺ | |
| Positioning / Padding | |
| x — null byte padding | |
| my $bin = pack('CxC', 1, 2); | |
| print unpack('H*', $bin), "\n"; | |
| # 010002 | |
| @ — absolute position | |
| my $bin = pack('C @4 C', 1, 2); | |
| print unpack('H*', $bin), "\n"; | |
| # 0100000002 | |
| Repetition Counts | |
| Fixed repeat count | |
| my $bin = pack('C4', 1,2,3,4); | |
| print unpack('H*', $bin), "\n"; | |
| # 01020304 | |
| * repeat all arguments | |
| my $bin = pack('C*', 1..10); | |
| my @nums = unpack('C*', $bin); | |
| print "@nums\n"; | |
| Complex Structure Example | |
| Suppose a binary structure: | |
| Field Type | |
| magic uint32 BE | |
| version uint16 LE | |
| flags uint8 | |
| name 8-byte string | |
| my $bin = pack( | |
| 'N v C A8', | |
| 0xDEADBEEF, | |
| 2, | |
| 7, | |
| 'test' | |
| ); | |
| my ($magic, $version, $flags, $name) | |
| = unpack('N v C A8', $bin); | |
| printf "magic=%08X\n", $magic; | |
| print "version=$version\n"; | |
| print "flags=$flags\n"; | |
| print "name=$name\n"; | |
| Useful Debugging Trick | |
| Hex dump packed data | |
| print unpack('H*', $binary), "\n"; | |
| or formatted: | |
| printf "%v02X\n", $binary; | |
| Common Real-World Uses | |
| Purpose Templates | |
| TCP/IP headers n, N | |
| Binary files C, v, V, a | |
| ELF parsing C, v, V, Q | |
| UUIDs H* | |
| Bitfields B, b | |
| Floating point protocols f, d | |
| Fixed-width records A, a, Z | |
| Full Reference Cheat Sheet | |
| Template Meaning | |
| c signed char | |
| C unsigned char | |
| s signed short | |
| S unsigned short | |
| l signed long | |
| L unsigned long | |
| q signed 64-bit | |
| Q unsigned 64-bit | |
| n uint16 BE | |
| v uint16 LE | |
| N uint32 BE | |
| V uint32 LE | |
| f float | |
| d double | |
| a null padded string | |
| A space padded string | |
| Z null terminated string | |
| H hex high nibble first | |
| h hex low nibble first | |
| B bit string descending | |
| b bit string ascending | |
| U Unicode | |
| x pad byte | |
| @ absolute position | |
| X move backward | |
| * repeat all | |
| Show how to use pack/unpack to convert 0x0a1b2c3d4e5f to 0x1b0a3d2c5f4e | |
| This transformation swaps every adjacent pair of bytes: | |
| 0a 1b 2c 3d 4e 5f | |
| ↓ | |
| 1b 0a 3d 2c 5f 4e | |
| You can do this cleanly with Perl pack and unpack. | |
| use strict; | |
| use warnings; | |
| my $hex = '0a1b2c3d4e5f'; | |
| # Convert hex string to raw bytes | |
| my $bin = pack('H*', $hex); | |
| # Read bytes individually | |
| my @bytes = unpack('C*', $bin); | |
| # Swap adjacent bytes | |
| for (my $i = 0; $i < @bytes; $i += 2) { | |
| @bytes[$i, $i + 1] = @bytes[$i + 1, $i]; | |
| } | |
| # Convert back to hex | |
| my $out = unpack('H*', pack('C*', @bytes)); | |
| print "$out\n"; | |
| Output: | |
| 1b0a3d2c5f4e | |
| Shorter Version | |
| my $out = unpack( | |
| 'H*', | |
| pack( | |
| 'C*', | |
| map { @$_[1,0] } | |
| unpack('(CC)*', pack('H*', '0a1b2c3d4e5f')) | |
| ) | |
| ); | |
| print "$out\n"; | |
| Using Pure pack/unpack Templates | |
| This version uses grouped unpacking: | |
| my $hex = '0a1b2c3d4e5f'; | |
| my @pairs = unpack('(a2 a2)*', pack('H*', $hex)); | |
| my $swapped = join '', map { $_->[1] . $_->[0] } @pairs; | |
| print unpack('H*', $swapped), "\n"; | |
| Most Efficient Binary-Oriented Version | |
| For large data: | |
| my $data = pack('H*', '0a1b2c3d4e5f'); | |
| $data =~ s/(.)(.)/$2$1/gs; | |
| print unpack('H*', $data), "\n"; | |
| This regex version is typically the fastest and simplest for pairwise byte swapping. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment