Converting hex like 48656C6C6F to text in Python is a bytes problem first: parse hex pairs into a bytes object, then .decode() with the right encoding (usually UTF-8).
This page is the method chooser. For edge cases of one API, see bytes.fromhex() deep dive and binascii.unhexlify(). Try results in the browser with Hex to String.
Recommended default: bytes.fromhex()
def hex_to_text(hex_str: str, encoding: str = "utf-8") -> str:
return bytes.fromhex(hex_str).decode(encoding)
print(hex_to_text("48656C6C6F20576F726C64")) # Hello World
print(hex_to_text("48 65 6C 6C 6F")) # spaces ignored
Use this in new Python 3 code. It is readable, fast (C implementation), and ignores ASCII whitespace between digits.
When to use binascii.unhexlify()
import binascii
def hex_to_text_strict(hex_str: str, encoding: str = "utf-8") -> str:
clean = "".join(hex_str.split()) # unhexlify does not skip spaces
return binascii.unhexlify(clean).decode(encoding)
Prefer this when you already depend on binascii, or when you want strict “no surprises” behavior and will sanitize input yourself. Details: unhexlify guide.
Optional: codecs.decode(..., "hex")
import codecs
text = codecs.decode("48656C6C6F", "hex").decode("utf-8")
Useful if your codebase already routes everything through codecs. Otherwise bytes.fromhex is clearer.
Manual loop (teaching / custom validation)
def hex_to_text_manual(hex_str: str, encoding: str = "utf-8") -> str:
h = "".join(hex_str.split())
if len(h) % 2:
raise ValueError("hex length must be even")
data = bytes(int(h[i:i+2], 16) for i in range(0, len(h), 2))
return data.decode(encoding)
Good for learning and for injecting custom rules. Slower than the C helpers — not the production default.
Quick comparison
| Method | Whitespace | Best for |
|---|---|---|
bytes.fromhex | Ignored | Default Python 3 |
binascii.unhexlify | Must strip | Legacy / strict pipelines |
codecs.decode(..., "hex") | Must strip | Codecs-centric code |
Manual int(..., 16) | Your choice | Teaching / custom checks |
Shared rules that matter more than the API name
- Odd length → error (do not invent a leading zero unless a format requires it).
- Decode with the encoding that produced the bytes.
- Validate untrusted input; wrap
ValueError.
def safe_hex_to_text(hex_str: str, encoding: str = "utf-8") -> str:
if not hex_str or not hex_str.strip():
return ""
try:
return bytes.fromhex(hex_str).decode(encoding)
except ValueError as exc:
raise ValueError(f"invalid hex: {exc}") from exc
Opposite direction: Python string to hex. Conceptual background: What is hexadecimal encoding?.