Skip to main content

How to Read a Hex Dump: Offset, Hex Bytes and the ASCII Gutter

Learn to read a hex dump column by column, spot file magic numbers like 25 50 44 46, and understand why the ASCII gutter makes binary inspection fast.

Published By Li Lei
#hex #binary #encoding #debugging #file-formats

How to Read a Hex Dump: Offset, Hex Bytes and the ASCII Gutter

The first time someone showed me a hex dump, it looked like a wall of two-character codes with a smear of garbage down the right edge. I assumed you needed a special kind of brain to read it. You don't. A hex dump is just three columns lined up next to each other, and once you know what each column is for, you can pull a surprising amount of truth out of any file or byte stream in about ten seconds.

This is the view xxd and hexdump -C have shown for decades, and it is still the fastest way to answer questions like "what bytes are actually in this string," "is this really a PNG," or "where did that extra newline come from." Let me walk through how to read it.

The three columns explained

A hex dump shows three columns: the byte offset, the bytes in hex (usually 16 per row), and an ASCII rendering where non-printable bytes appear as dots.

00000000  25 50 44 46 2d 31 2e 37  0a 25 e2 e3 cf d3 0a 31  |%PDF-1.7.%.....1|
00000010  20 30 20 6f 62 6a 0a 3c  3c 2f 54 79 70 65 2f 43  | 0 obj.<</Type/C|

The offset on the left is an 8-digit hexadecimal address. It tells you how many bytes precede this row. The first row begins at 00000000, and because each row holds 16 bytes (0x10), the next row begins at 00000010, then 00000020, and so on. The offset is hex on purpose: 00000010 is byte 16 in decimal, not byte 10. If you read it as decimal, every position calculation will be off by the base.

The hex bytes in the middle are the raw data, two hex digits per byte, often split into two groups of eight so you can count them without losing your place. Each pair is one byte: 25 is one byte with the value 0x25.

The ASCII gutter on the right, usually wrapped in pipes, renders each byte as its printable character when it has one. Bytes 0x20 through 0x7e (space through tilde) print as themselves; everything else collapses to a single dot. That dot is a placeholder, not a real period. A null byte (0x00), a newline (0x0a), and a tab (0x09) all show as a dot even though their hex values differ, so always trust the hex column for the actual value.

Why the ASCII gutter earns its keep

The hex column tells you the exact byte; the ASCII gutter tells you the meaning at a glance. Together they let you skim. In the PDF example above, the hex says 25 50 44 46 2d 31 2e 37, but your eye doesn't need to decode that, because the gutter already spells out %PDF-1.7. Format markers, embedded strings, field names in a binary protocol, stray text inside a binary blob, all of it surfaces in that right column while the hex stays available for precision.

That split is the whole reason the layout has survived. Pure hex is precise but unreadable; pure text hides the bytes that don't print. Putting them side by side means you read structure from the gutter and verify bytes from the middle, in one pass.

Finding magic numbers and headers

Most file formats announce themselves in their first few bytes. This signature is the magic number, and the start of a hex dump is where you find it. A handful worth memorizing:

  • 25 50 44 46%PDF, a PDF document
  • 89 50 4e 47.PNG, a PNG image
  • ff d8 ff → a JPEG image
  • 50 4b 03 04PK, a ZIP archive (and anything built on ZIP: .docx, .jar, .apk)
  • 7f 45 4c 46.ELF, a Linux executable

When a download has the wrong extension or a parser refuses a file, dumping the first row settles it instantly. If a file claiming to be a PDF starts with 50 4b 03 04, it is really a ZIP, and no PDF reader will touch it. The magic number does not lie even when the filename does. The same trick reads network protocols: many wire formats put a fixed header or version byte up front, and the offset column lets you jump straight to a known field position once you know the layout.

A worked example

Say a build step keeps rejecting a file you were told is a PDF. You paste its opening bytes into a hex dump viewer and read the first row:

00000000  25 50 44 46 2d 31 2e 34  0a 25 c4 e5 f2 e5 eb a7  |%PDF-1.4.%......|

Walk it left to right. Offset 00000000 means this is the very start of the file. The bytes 25 50 44 46 are the magic number, and the gutter confirms it: %PDF. The next bytes 2d 31 2e 34 read as -1.4 in the gutter, so this is a PDF version 1.4 header. Byte 0a is a newline (a dot in the gutter), and the 25 c4 e5 f2... that follows is a comment line with high bytes that nudge tools into treating the file as binary. Conclusion: the file genuinely is a valid PDF, so the problem is downstream, not the file itself. That is a five-second diagnosis that no amount of staring at a rendered preview would give you.

Now flip the case. If that same first row had read 7b 22 6e 61 6d 65 with a gutter of {"name, you would know in one glance that someone handed you JSON with a .pdf extension, and the fix is to rename it, not to debug the PDF pipeline.

Bytes, not characters

The catch that trips people up is assuming one character equals one byte. In UTF-8 it often doesn't. An ASCII letter is one byte, an accented letter is two, a CJK character is three, and most emoji are four. Type and the dump shows e4 b8 ad, three separate bytes, each with its own dot in the gutter because none of them is printable ASCII on its own. This is exactly why a "5-character" string can occupy 11 bytes on disk, and the dump makes that gap concrete. If you want to go the other direction and convert text to a flat hex string, the text to hex converter does that without the column layout.

A hex dump is the right tool when you need to inspect what is actually there: a hidden BOM (ef bb bf), a non-breaking space (c2 a0) masquerading as a normal one, a trailing null, or a file's true type. Read the offset to know where you are, read the hex to know what the byte is, and let the ASCII gutter tell you what it means. Once those three columns click into place, binary stops being opaque.


Made by Toolora · Updated 2026-06-13