What is 48 65 6C 6C 6F in text?
Hello 48 is H, 65 is e, 6C is l and 6F is o in ASCII and UTF-8.
Convert hex to text and text to hex. Paste hex in any shape — spaced, 0x, \x, xxd or hexdump output, Java or C byte arrays — and ASCII, UTF-8, GBK or UTF-16 is detected for you. Runs in your browser.
Read as plain hex · 13 bytes
Hello, 世界
Auto-detected UTF-8: every multi-byte sequence is well-formed.
The result is itself hex — it was probably converted twice. One more round gives:
| Encoding | Reads as |
|---|---|
| UTF-8 Decodes cleanly | Hello, 世界 |
| GBK / GB18030 Decodes cleanly | Hello, 涓栫晫 |
| UTF-16LE Has undecodable bytes | 效汬Ɐ隸闧� |
| UTF-16BE Has undecodable bytes | 䡥汬漬⃤뢖� |
| ISO-8859-1 Decodes cleanly | Hello, ä¸<96>ç<95><8C> |
48 65 6C 6C 6F 2C 20 E4 B8 96 E7 95 8C
Written and reviewed by developers who build the Go Tools encoding utilities. Every hex value and output quoted on this page is produced by the page's own engine and checked by automated tests.
Hello 48 is H, 65 is e, 6C is l and 6F is o in ASCII and UTF-8.
Usually 3 bytes in UTF-8, 2 in GBK 你 is E4 BD A0 in UTF-8 and C4 E3 in GBK.
CR LF (\r\n) Carriage return followed by line feed — the line ending used by Windows, HTTP and most serial command sets.
41 Lowercase a is 61; the two cases always differ by 20.
Hexadecimal is a way of writing bytes: each byte, from 0 to 255, is written as two digits from 00 to FF. Hex to string conversion turns those bytes back into readable text, and it always involves a choice of character encoding — the table that says which byte, or sequence of bytes, stands for which character.
For English text the choice rarely shows, because ASCII, UTF-8, GBK and most other encodings agree on bytes 00 to 7F. It shows immediately with anything else. The bytes C4 E3 BA C3 are 你好 in GBK and invalid in UTF-8, while 你好 in UTF-8 is E4 BD A0 E5 A5 BD. The hex is only half the information; the encoding is the other half.
String to hex is the reverse: encode the text into bytes, then write each byte as two hex digits. Developers use it to see exactly what goes over a serial line or into a database column, to put binary data into source code, and to compare what two systems actually sent.
$ echo 48656c6c6f | xxd -r -p
Hello
>>> bytes.fromhex('c4e3bac3').decode('gbk')
'你好'
>>> bytes.fromhex('c4e3bac3').decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc4 in position 0: invalid continuation byte Spaced, compact, colon- or dash-separated, 0x values, \x escapes, % encoding, C, Go and Java arrays, Java's signed Arrays.toString output, Python bytes literals, Node.js Buffers, and full xxd, hexdump -C and od screens. The page tells you what it recognised and what it removed.
Auto-detect checks for a byte-order mark, well-formed UTF-8, UTF-16, pure ASCII and then GBK, and says which rule decided. GBK is only chosen when the bytes read as common Chinese characters, so short binary frames are reported as probably not text instead of as nonsense Chinese.
The same bytes are shown as UTF-8, GBK, UTF-16LE, UTF-16BE and ISO-8859-1, each marked as decoding cleanly or not. When text comes out garbled, the correct reading is usually one row down.
NUL, CR, LF, ESC and other control bytes are shown as ␀ ␍ ␊ ␛, so a trailing 00 or a missing 0D in protocol data stands out. Copying still gives the real characters.
Text → Hex writes spaced or compact hex, 0x lists, \x escapes, a C array in xxd -i style, a Java byte[] with signed values, a Python bytes literal matching repr(), a Go []byte or an xxd dump — and the page can read every one of them back.
Parsing and decoding run locally in JavaScript. Nothing is uploaded, stored or put in the URL, so packet captures and production logs are safe to paste.
bytes.fromhex(h).decode() bytes.fromhex('48 65 6c 6c 6f').decode('utf-8') returns 'Hello'; spaces between bytes are allowed, a 0x prefix is not. The reverse is s.encode('utf-8').hex(), or .hex(' ') for spaced output. Pass 'gbk' to decode or encode Chinese text in GBK.
Buffer.from(h, 'hex') Buffer.from(h, 'hex').toString('utf8') decodes, Buffer.from(s, 'utf8').toString('hex') encodes. Invalid input is not an error: decoding stops at the first bad pair and a trailing odd digit is dropped, so validate first.
TextDecoder / TextEncoder Parse pairs with parseInt(pair, 16) into a Uint8Array, then new TextDecoder('utf-8').decode(bytes). TextDecoder also reads 'gbk', 'big5' and 'shift_jis', but TextEncoder only produces UTF-8.
HexFormat.of() new String(HexFormat.of().parseHex(h), StandardCharsets.UTF_8) decodes and HexFormat.of().formatHex(s.getBytes(StandardCharsets.UTF_8)) encodes. On older versions format each byte with String.format("%02x", b); avoid Integer.toHexString(b), which prints ffffffe4 for negative bytes.
encoding/hex hex.DecodeString(h) returns the bytes and string(b) makes them a string; hex.EncodeToString([]byte(s)) goes back. It is strict: spaces return invalid byte: U+0020 ' ' and an odd length returns odd length hex string.
sscanf with %2hhx Loop over the string two digits at a time into an unsigned char buffer and add a terminating '\0'. To print hex, cast to unsigned char and use %02X, otherwise bytes above 0x7F can print as FFFFFFE4 where char is signed.
hex2bin() / bin2hex() hex2bin('48656c6c6f') returns Hello and bin2hex('Hello') returns 48656c6c6f. The manual documents that hex2bin() returns false with an E_WARNING for odd-length or invalid input.
xxd -r -p / xxd -p echo 48656c6c6f | xxd -r -p prints Hello, and printf 'Hello' | xxd -p prints 48656c6c6f. Use printf rather than echo when encoding, because echo adds a trailing newline, 0a.
Convert.FromHexString() Encoding.UTF8.GetString(Convert.FromHexString(h)) decodes and Convert.ToHexString(Encoding.UTF8.GetBytes(s)) encodes (uppercase, no separators). FromHexString rejects spaces and a 0x prefix. .NET Core and .NET 5+ ship without GBK: call Encoding.RegisterProvider(CodePagesEncodingProvider.Instance) first, then Encoding.GetEncoding(936).
48 65 6C 6C 6F 2C 20 E4 B8 96 E7 95 8C
Hello, 世界
The first seven bytes are plain ASCII: 48 65 6C 6C 6F is Hello, 2C is a comma and 20 a space. E4 B8 96 and E7 95 8C are three-byte UTF-8 sequences for 世 and 界 — most Chinese characters take three bytes in UTF-8. Because every sequence is well-formed, auto-detect reads it as UTF-8.
CE C2 B6 C8 3A 32 35 2E 33 A1 E6
温度:25.3℃
Many serial devices send Chinese text in GBK. Here CE C2 is 温 and B6 C8 is 度, 3A 32 35 2E 33 is the ASCII :25.3, and A1 E6 is the full-width ℃ sign — two bytes per Chinese character or symbol. Read as UTF-8 the bytes are invalid (CE starts a two-byte sequence, but C2 is not a continuation byte), and read as GBK they are common characters, so auto-detect picks GBK. A converter that only knows UTF-8 shows replacement characters here.
00000000: 4869 20e4 bda0 e5a5 bd0d 0a Hi ........
Hi 你好␍␊
This is exactly what printf 'Hi 你好\r\n' | xxd prints. The offset 00000000: and the character column on the right are removed before decoding; if they were read as data, the eight zeros alone would become four NUL bytes at the start. The last two bytes, 0d 0a, are a Windows line break, shown as ␍␊.
[-28, -72, -83, -26, -106, -121]
中文
Arrays.toString(bytes) prints Java's signed bytes in decimal. Negative values are bytes of 0x80 and above: -28 is 256 − 28 = 228 = 0xE4. The six bytes E4 B8 AD E6 96 87 are the UTF-8 encoding of 中文.
AT+CSQ\r\n
41 54 2B 43 53 51 0D 0A
Modems and most UART command sets expect every command to end in carriage return plus line feed. A text box only produces 0A for Enter, so tick Line breaks as CR LF and the line ending becomes 0D 0A.
653462646130653561356264
e4bda0e5a5bd → 你好
Every byte here is an ASCII hex digit (65 is e, 34 is 4, 62 is b), so the first decode produces another hex string. This happens when a hex string is treated as text and converted again. The page offers the second round, which gives 你好. To avoid false alarms it only offers it when the first result is at least 12 hex digits and the second decodes to real text — dates, timestamps, CRC32 values and MD5 hashes do not trigger it.
Paste it into the box on the Hex → Text tab in whatever form you have: spaced, compact, 0x values, \x escapes, a byte array or a whole xxd screen. The line under the box says what it was read as.
The text appears on the right, with the auto-detected encoding and the reason for it. If the guess is wrong, the table below shows every reading; pick the right one from the Encoding menu.
Control bytes such as NUL, CR and LF are shown as ␀ ␍ ␊. Untick Show invisible characters for the plain text, then copy the result.
On the Text → Hex tab, choose the encoding and an output format — spaced hex, 0x values, a C, Java, Python or Go array, or an xxd dump. Tick CR LF for serial and network protocols.
Chinese text from older Windows software, many serial devices and legacy databases is GBK. Decoding it as UTF-8 either fails or fills the output with replacement characters. Look at the encoding table and use the reading that makes sense.
>>> bytes.fromhex('c4e3bac3').decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc4 in position 0: invalid continuation byte >>> bytes.fromhex('c4e3bac3').decode('gbk')
'你好' charCodeAt() returns a UTF-16 code unit. For ASCII it happens to match the byte, so the bug only appears with other characters, where the result is not what any UTF-8 system sends.
'你'.charCodeAt(0).toString(16) // '4f60' — a UTF-16 code unit
Buffer.from('你', 'utf8').toString('hex') // 'e4bda0' — the UTF-8 bytes Java's byte is signed, so bytes from 0x80 up are negative. Integer.toHexString() works on an int, prints negative values as 32-bit unsigned hex, and drops leading zeros.
Integer.toHexString(b) // "ffffffe4" for 0xE4, "a" for 0x0A
String.format("%02x", b) // "e4", "0a" Buffer.from(hex, 'hex') never throws. It stops at the first pair that is not valid hex and ignores a trailing odd digit, so a typo gives you a shorter buffer instead of an error.
Buffer.from('486', 'hex') // <Buffer 48> — the 6 is silently dropped
Buffer.from('48zz65', 'hex') // <Buffer 48> — stops at zz if (!/^([0-9a-f]{2})*$/i.test(hex)) throw new Error('invalid hex');
Buffer.from(hex, 'hex'); A converter that only removes spaces reads the offset 00000000: as data and the character column as more hex where it can. The result starts with NUL bytes and drifts from there. Use xxd -p for plain hex, or paste the dump here, where the columns are recognised.
00000000: 4869 20e4 bda0 e5a5 bd0d 0a Hi ........ → read naively: 00 00 00 00 48 69 20 e4 …
$ printf 'Hi 你好\r\n' | xxd -p 486920e4bda0e5a5bd0d0a
AT modems and many line-based protocols end each command with carriage return plus line feed. A command that ends in 0A alone is often ignored with no error at all.
41 54 2B 43 53 51 0A AT+CSQ followed by LF only
41 54 2B 43 53 51 0D 0A AT+CSQ followed by CR LF
0D 0A or 00 at the end — and go the other way to build a command, with CR LF line endings, that a device will accept.b'...' literals and Node.js as <Buffer ...>. Paste the log fragment as it is to see the text, including whether it was UTF-8 or GBK.00 to FF, and the number of bytes is always half the number of digits. Upper and lower case mean the same thing. Separators, prefixes and array syntax are only notation: 4865, 48 65, 0x48, 0x65 and \x48\x65 are the same two bytes.00 to 7F. Printable characters run from 20 (space) to 7E (~); the rest are control characters, of which 00 (NUL), 09 (tab), 0A (line feed), 0D (carriage return) and 1B (escape) appear most in real data. UTF-8, GBK and ISO-8859-1 all keep these values, which is why plain English survives almost any wrong encoding.C2–DF for two bytes, E0–EF for three, F0–F4 for four — and every following byte must be in 80–BF. That strict structure is why a GBK sentence is almost never valid UTF-8 — in our test on 33,910 Chinese sentences, 55 were — and why auto-detect trusts a clean UTF-8 decode. A single Chinese character is different: about 18% of GBK characters happen to form a valid two-byte UTF-8 sequence.81 to FE followed by a trail byte from 40 to FE, excluding 7F. Because the trail byte range is so wide, short UTF-8 text read as GBK often decodes without any error into unrelated characters — E4 BD A0 E5 A5 BD (你好) becomes 浣犲ソ — which is why this page tries UTF-8 first. GB18030 extends GBK with four-byte sequences for rarer characters; decoding here accepts them.41 00 — and UTF-16BE the high byte first — 00 41. Windows APIs and many files use little-endian and may start with a byte-order mark, FF FE; UTF-8 files sometimes start with EF BB BF. Auto-detect removes a byte-order mark and reports it.getBytes(), encode(), a serial terminal setting, a database connection charset — set the encoding explicitly and write it down next to the hex, so the other side does not have to guess.charCodeAt() or Java's char values, give UTF-16 code units rather than encoded bytes. Encode the string first with TextEncoder, Buffer.from() or getBytes(StandardCharsets.UTF_8), then format the bytes.%02x or an equivalent, never a bare number-to-hex call. a and 0a look alike in a log, but concatenating unpadded values produces a string with an odd length, or worse, one that decodes into different bytes.00 to FF, so 48656c6c6f is the five bytes 48, 65, 6C, 6C, 6F. Second, decode those bytes with a character encoding. In ASCII and UTF-8, 48 is H, 65 is e, 6C is l and 6F is o, giving Hello. The second step is where results differ: bytes above 7F mean different characters in UTF-8, GBK and other encodings, which is why this page detects the encoding and shows every reading side by side. C4 E3 BA C3 is 你好 in GBK but invalid UTF-8, and E4 BD A0 E5 A5 BD is 你好 in UTF-8 but turns into 浣犲ソ when read as GBK. Look at the encoding table under the result — the row that reads as sensible text is the encoding the data was written in. With only one or two characters, automatic detection cannot always tell: D2 BB is valid UTF-8 (һ) and also GBK (一), so check the GBK line the page shows under the result. Two other causes are worth ruling out: an extra or missing hex digit, which shifts every following byte by half a byte, and pasting a hex dump with its offset column still attached. If what you have is already garbled text (such as 浣犲ソ) rather than hex, paste the text into the encoding converter instead. E4 BD A0 E5 A5 BD for 你好 show up as 浣犲ソ. Or the text contains control bytes such as 00 or 0D 0A that appear as boxes or line breaks. Paste the hex here: the encoding table shows the UTF-8 and GBK readings side by side, and control bytes appear as ␀ ␍ ␊. 00 to 7F: letters, digits, punctuation and control characters. UTF-8 is built so that those same bytes mean exactly the same characters, and it uses sequences of bytes from 80 upward for everything else — 2 bytes for accented Latin letters, 3 for most Chinese, Japanese and Korean characters, 4 for emoji. So for plain English text, hex to ASCII and hex to UTF-8 give identical results. They differ as soon as a byte is 80 or higher: an ASCII-only converter cannot show those bytes as characters, while UTF-8 decodes them into the full range of Unicode. For what each byte from 00 to 7F stands for, see the ASCII table. E4 BD A0. In GBK and GB2312 they take 2 bytes: 你 is C4 E3. In UTF-16 characters in the Basic Multilingual Plane take 2 bytes, and the byte order matters: 你 is 60 4F in UTF-16LE and 4F 60 in UTF-16BE. Rare characters outside that plane take 4 bytes in UTF-8, 4 in UTF-16 (a surrogate pair) and 4 in GB18030. Switch the encoding on the Text → Hex tab to see the byte count for your own text. bytes.fromhex() and then decode: bytes.fromhex('48656c6c6f').decode('utf-8') returns 'Hello'. fromhex accepts spaces between bytes, so bytes.fromhex('48 65 6c 6c 6f') works too, but it rejects a 0x prefix with a ValueError. For GBK data decode with 'gbk': bytes.fromhex('c4e3bac3').decode('gbk') returns '你好', while decoding the same bytes as UTF-8 raises UnicodeDecodeError. The reverse is '你好'.encode('utf-8').hex(), which returns 'e4bda0e5a5bd'; pass a separator such as .hex(' ') to get space-separated output. Buffer.from('48656c6c6f', 'hex').toString('utf8') returns 'Hello'. Be careful with bad input: Node does not throw — it stops at the first invalid pair and silently drops a trailing odd digit, so Buffer.from('486', 'hex') is a one-byte buffer. In the browser, build the bytes yourself and use TextDecoder, which also reads GBK: new TextDecoder('gbk').decode(Uint8Array.from('c4e3bac3'.match(/../g), h => parseInt(h, 16))) returns '你好'. Do not use charCodeAt() to get bytes: '你'.charCodeAt(0).toString(16) is '4f60', a UTF-16 code unit, not the UTF-8 bytes e4bda0. unsigned char buffer, then terminate it: for (size_t i = 0; i < n; i++) sscanf(hex + 2 * i, "%2hhx", &buf[i]); buf[n] = '\0'; where n is strlen(hex) / 2. With hex set to 48656c6c6f2c20e4b896e7958c, printing buf gives Hello, 世界 on a UTF-8 terminal. For the other direction print each byte with printf("%02X ", (unsigned char)s[i]). The cast matters: on platforms where char is signed, a byte such as 0xE4 would otherwise be sign-extended and printed as FFFFFFE4. In C++, append each pair with s.push_back(static_cast<char>(std::stoi(hex.substr(i, 2), nullptr, 16))); the same hex gives Hello, 世界. new String(HexFormat.of().parseHex(hex), StandardCharsets.UTF_8); for GBK data use Charset.forName("GBK") instead. Going the other way, the classic surprise is ffffffe4. Because Java's byte is signed and Integer.toHexString() takes an int. The byte 0xE4 is stored as -28; widening it to an int keeps the value -28, and toHexString prints negative numbers as the unsigned 32-bit value, ffffffe4. The same method also drops leading zeros, so 0x0A comes out as a. Use String.format("%02x", b), which formats a negative byte as its unsigned 8-bit value, or Integer.toHexString(b & 0xff) with padding. On Java 17 and later, HexFormat.of().formatHex(bytes) converts a whole array, and HexFormat.of().parseHex(hex) goes back. hex2bin() decodes a hex string into a binary string, and bin2hex() goes the other way: bin2hex('Hello') returns 48656c6c6f. According to the PHP manual, hex2bin() returns false and raises an E_WARNING when the input has an odd length or is not valid hexadecimal, so strip spaces and 0x prefixes before calling it. PHP strings are bytes, so the result has whatever encoding the original text used — convert GBK output with mb_convert_encoding() if your page is UTF-8. xxd (including -u, -c and -g), hexdump -C, Go's hex.Dump, od -A x -t x1 and GNU od -t x1z, and it expands the * line that hexdump and od print in place of repeated rows. It also handles plain hexdump and od -x, which print 16-bit words rather than bytes: on a little-endian machine the bytes 48 69 are printed as 6948, so the page swaps each pair back and uses the last offset to drop the padding byte added to odd-length data. Each xxd, hexdump and od variant was tested against 600 real dumps of random data. a instead of 0a), a character cut off when copying, or a stray letter such as O in place of 0. A hex string produced by converting all the bytes as one big number, such as Python's hex(int.from_bytes(data, 'big')), loses the zero at the start: \r\n comes out as 0xd0a. The page does not guess which digit is missing, because guessing wrong shifts every byte after it by half a byte and produces convincing nonsense. Instead it offers two one-click fixes — add a 0 at the start, or remove the last digit — so you can compare the results. Values written individually with a prefix, such as 0x0 0xa, are fine: each one is read as a whole byte. Encoding & Formatting
The full ASCII table: 128 characters in decimal, hex, octal and binary, plus a two-way text-to-ASCII converter. Control characters come with escape sequences, caret notation and where you actually meet them.
Encoding & Formatting
Decode and encode Base64 online for free. Real-time conversion with full UTF-8 and emoji support. 100% private — runs in your browser. No signup needed.
Encoding & Formatting
Decode a Base64 string or data URI back into an image in your browser. Preview, read dimensions & MIME, then download as PNG, JPG, GIF, SVG. No upload.
Encoding & Formatting
Convert CSV to JSON in your browser. RFC 4180, type inference, header row, big-int safe. 100% private, no upload.
Encoding & Formatting
Paste garbled text and get the original back. Every plausible encoding chain — UTF-8, GBK, Big5, Shift_JIS, EUC-KR, Windows-1252 — is tried and ranked, with the exact chain shown. Free, private, runs in your browser.
Encoding & Formatting
Paste a .env file, get JSON instantly. Your database passwords, API keys and tokens never leave your browser — 100% private, no upload, free dotenv parser.