Hex to string decoder
The hex to string converter turns pairs of hexadecimal digits back into bytes and reads those bytes as UTF-8 text. It is the reverse of String to Hex and is useful for reading values from packet captures, database dumps, firmware, hex editors and debug logs.
Flexible input
The parser strips the usual decorations before decoding, so you can paste hex straight from where you found it:
- Plain:
48656c6c6f - Spaced or on several lines:
48 65 6c 6c 6f - Prefixed:
0x48 0x65or\x48\x65 - Separated by commas, colons, semicolons or hyphens:
48:65:6cor48-65-6C - Upper or lower case, or a mix.
If the number of digits is odd, a leading 0 is assumed. Any other character that is not a hex digit produces a clear error rather than garbage.
Example
Input:
48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 20 e2 9c 93Output:
Hello, World! ✓
When the output looks wrong
- Replacement characters (�). The bytes are not valid UTF-8. They might be binary data (an image, a compressed stream, a hash) or text in another encoding such as Latin-1 or UTF-16. For example
c3 28is an invalid UTF-8 sequence and decodes to�(. - Every other character is empty. Lots of
00bytes between letters usually means UTF-16 text, which is not supported here. - Numbers instead of letters. If the hex represents a number (such as
0x1F4= 500) rather than text, use the Hex to Decimal converter.
Hex strings copied from network tools often include offsets and an ASCII column, as in a classic hex dump. Remove those columns before pasting, or the offset digits will be decoded as extra bytes.
Reading multi-byte UTF-8 by hand
The first hex digit of each byte tells you how UTF-8 groups them. Bytes from 00 to 7F are single ASCII characters. A byte starting with C or D opens a two-byte character, E a three-byte one and F0–F4 a four-byte one, and every following byte must be between 80 and BF.
e2 9c 93
11100010 10011100 10010011 lead byte + 2 continuation bytes
0010 011100 010011 payload bits
= 0x2713 = ✓ (CHECK MARK)
That rule explains the c3 28 case above: c3 promises one more byte, but 28 is an ordinary "(" rather than a continuation byte, so the decoder emits a replacement character and carries on.
Hex to string in code
Python: bytes.fromhex('48656c6c6f').decode('utf-8'). Node.js: Buffer.from('48656c6c6f', 'hex').toString('utf8'). Shell: echo 48656c6c6f | xxd -r -p. MySQL: SELECT CONVERT(UNHEX('48656c6c6f') USING utf8mb4). Note that most of these expect plain hex without separators or prefixes, which is exactly what this tool removes for you.
Hex is not a cipher
Hex-encoded strings sometimes appear in configuration files or URLs and look secret. They are not: hex is a direct, reversible representation of bytes. If a “hex password” decodes to readable text, treat it as plain text.
The decoding is done locally in your browser.
Hex to string in Python
bytes.fromhex() converts hex digits to bytes and .decode("utf-8") turns them into text. fromhex() skips whitespace between bytes, but anything else raises ValueError: non-hexadecimal number found: 0x prefixes, \x escapes, colons and commas all have to be stripped first, and the digit count must be even. The helper below removes those decorations with one regular expression and uses errors="replace" so that invalid UTF-8 shows as � instead of raising.
import re
hex_text = "48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 20 e2 9c 93"
print(bytes.fromhex(hex_text).decode("utf-8"))
def hex_to_string(value):
digits = re.sub(r"0x|\\x|[\s,:;-]", "", value)
return bytes.fromhex(digits).decode("utf-8", errors="replace")
print(hex_to_string("0x48 0x65 0x6C 0x6C 0x6F"))
print(hex_to_string("48:65:6c:6c:6f"))
print(hex_to_string(r"\x48\x65\x6c\x6c\x6f"))
All four calls print the same text as the decoder above, which accepts the same input styles; to go the other way, use String to Hex.