Skip to main content

Text to Hex: How Bytes Become Two Hexadecimal Digits Each

How text to hex conversion really works: each byte becomes two hex digits, why UTF-8 multi-byte characters matter, reading hex dumps, and decoding back.

Published By Li Lei
#encoding #hexadecimal #utf-8 #developer-tools

Text to Hex: How Bytes Become Two Hexadecimal Digits Each

If you have ever stared at a packet capture, a hex editor, or a log line full of pairs like 48 65 6c 6c 6f and wondered what they spell, you already understand why text to hex conversion is worth getting right. Hexadecimal is just a compact way to write the raw bytes that a file, a socket, or a database column actually stores. The catch is that "the bytes" and "the characters you typed" are not always the same thing, and a sloppy converter will quietly hand you the wrong answer.

This post walks through the mechanics: how a single byte maps to exactly two hex digits, why non-ASCII text expands into multiple bytes under UTF-8, how to read a hex dump, and how to decode hex back into readable text. You can follow along in the Text to Hex Converter, which does all of this in your browser without uploading anything.

One Byte, Two Hex Digits

A byte holds a number from 0 to 255. Hexadecimal is base 16, and 255 in base 16 is ff. That is the whole reason hex pairs up so neatly with bytes: every possible byte value fits in exactly two hex digits, from 00 to ff. No byte ever needs one digit or three. When you see an odd number of hex characters in a string, something is wrong — a digit got dropped, usually a leading zero.

For plain English text, the mapping is direct. Each character in the basic Latin set is one byte, and that byte equals the character's ASCII code. The letter A is 65 in decimal, which is 41 in hex, so A becomes 0x41. Lowercase a is 97, or 61. A space is 32, or 20. The digit 0 is 48, or 30. None of these require lookups once you have the converter in front of you; type the character and read the two digits back.

Worked Example: "Hi" Becomes 48 69

Take the two-letter word Hi. The capital H is ASCII 72, and 72 in hex is 48. The lowercase i is ASCII 105, which is 69. So Hi encodes to 48 69 — two bytes, one per letter, each rendered as two hex digits. There is nothing else in there: no length prefix, no hidden terminator, just the two byte values laid out in order.

Decoding reverses the same steps. Given 48 69, read each pair as a byte: 48 is 72 is H, 69 is 105 is i. Put them together and you have Hi again. The round trip is lossless because hex is a faithful representation of the bytes, not a lossy summary of them.

Why Non-ASCII Text Takes More Than One Byte

This is the part that trips up naive tools. Plenty of converters call something like charCodeAt and emit the character's code point, which works by accident for ASCII but breaks the moment you leave it. UTF-8 — the encoding the web, modern files, and most APIs use — represents characters above the ASCII range with two, three, or four bytes.

Here is the pattern in concrete terms:

  • The letter A is one byte: 41.
  • An accented é is two bytes: c3 a9.
  • The Chinese character is three bytes: e4 b8 ad.
  • The emoji 😀 is four bytes: f0 9f 98 80.

So 你好 is not two values, it is six bytes: e4 bd a0 e5 a5 bd. A tool that prints 4e2d for is showing you the Unicode code point, which is real but is not what any byte stream contains. No file on disk and no packet on the wire holds 4e2d for that character; they all hold e4 b8 ad. If you are debugging storage or transport, the code point is the wrong number to compare against.

This distinction is also why naive substring code mangles emoji: 😀 is one code point spread across four bytes, so slicing it in the middle leaves you with half a character. Watching the byte count change as you add characters is the fastest way to internalize how the encoding works. If you want to go further and see the code points themselves alongside the bytes, the Unicode Character Inspector breaks a string down character by character.

Reading a Hex Dump and Choosing a Separator

Real hex shows up in many shapes. A hex editor groups bytes in rows of sixteen. C source uses comma lists like 0x48, 0x69. Shell and Python string literals use the \x escape, as in "\x48\x65\x6c\x6c\x6f". The byte values are identical across all of these — 0x48 and \x48 both mean 72, the letter H — only the surrounding notation differs.

When you encode text, picking the right separator saves cleanup later:

  • A bare run (4869) when you want the most compact form.
  • Space groups (48 69) for reading a dump or pasting into a fuzzer.
  • Comma lists (0x48, 0x69) for a C array initializer.
  • \x escapes for a shell or Python string literal.
  • One byte per line when you want to annotate each value.

Decoding should be forgiving in return. A good converter strips whatever mix of spaces, commas, 0x, \x, and newlines you paste, then validates that what remains is an even number of valid hex nibbles before rebuilding the text.

When the Decode Comes Out as Garbage

The single most common failure is assuming every hex string is UTF-8. If the bytes were produced as Latin-1, Windows-1252, or UTF-16, the byte boundaries do not line up with UTF-8's rules, and strict decoding either errors out or yields the wrong characters. A lone ff or a stray 80 is never valid UTF-8 on its own. Rather than printing a replacement square and pretending it worked, the converter raises a clear error so you know to decode in whatever encoding the bytes were actually written.

The other recurring mistake is an odd digit count. Because every byte is exactly two hex characters, a string like 414 cannot decode — one digit is missing, often a leading zero that got typed as a single character (09 written as 9). The right move is to fix the source, not trust a half-parsed result.

I keep this tool open in a pinned tab whenever I am chasing an encoding bug. The last time a form was double-encoding Chinese names, I pasted 中文 and got e4 b8 ad e6 96 87, compared it against what the API returned, and the off-by-a-byte mismatch was obvious within seconds. Reading the actual bytes turns "the characters look weird" into a precise, checkable fact.

Hex Versus Other Byte Encodings

Hex is the right tool when you want to inspect or hand-edit individual bytes, but it is not the most compact. Base64 packs three bytes into four characters, so Hi is SGk= there versus 48 69 in hex — shorter, but you cannot read off a single byte at a glance. Use hex for debugging protocols, reading dumps, and writing byte literals; reach for the Base64 Encoder when you want the smallest text form for transport, like embedding a file in JSON. Both represent the exact same bytes; they just trade readability for density in opposite directions.

Once the byte-to-two-digits idea clicks, the rest follows: count the bytes a character takes in UTF-8, render each as a pair of hex digits, and reverse the steps to decode. That is the whole model, and it holds for ASCII, accented Latin, CJK, and emoji alike.


Made by Toolora · Updated 2026-06-13