Why the "UTF-8 byte[] to string" question remains relevant?
Imagine: you received a byte array from a network request, read a binary file, or deserialized data from an API. Inside is plain text in Russian, English, or emojis. But C# only sees byte[]. Your task is to turn these zeros and ones into a readable string.
It seems simple, right? One method call and you're done. But on Stack Overflow, the question "How to convert UTF-8 byte[] to string" gathers thousands of views and dozens of answers. Why? Because the devil is in the details: encoding, performance, safety, and unexpected exceptions.
In this article, we won't just give you ready-made code. We will compare approaches, measure speed, discuss pitfalls, and choose the best tool for each task. If you write in C# and work with networks, files, or serialization — this material is for you.
The Problem: byte[] is not a string
In C#, the string type is stored in UTF-16 encoding. But the bytes you received are most likely encoded in UTF-8 (the standard for the internet, JSON, most text protocols). Direct type casting is impossible — the compiler will throw an error.
You need a decoder that converts a sequence of UTF-8 bytes into UTF-16 characters. And here's where it gets interesting: a developer has at least three fundamentally different paths.
Method 1: The Classic — Encoding.UTF8.GetString
The most obvious and Microsoft-recommended way is to use the static class System.Text.Encoding:
byte[] utf8Bytes = { 72, 101, 108, 108, 111, 32, 208, 191, 209, 128, 208, 176, 208, 178, 208, 176 }; // "Hello права"string result = Encoding.UTF8.GetString(utf8Bytes);Console.WriteLine(result); // Output: Hello праваPros:
- Simplicity — one line of code.
- Support for all encodings, including UTF-8, UTF-16, ASCII, Latin-1.
- Built-in BOM (Byte Order Mark) handling — if there is a UTF-8 marker at the beginning of the array, it will be ignored.
- Safety — no
unsafeblocks.
Cons:
- Each call creates a new string and an internal buffer.
- If the bytes are corrupted or contain an invalid UTF-8 sequence, the method will throw a
DecoderFallbackExceptionby default (if fallback is not configured). - Not optimal for very large arrays (hundreds of megabytes) — double data copying.
When to use?
In 95% of cases. This is the industry standard. If you are not writing a high-load parser or working with gigabytes of logs — use Encoding.UTF8.GetString.
Method 2: Fast and Dangerous — unsafe with pointers
For those chasing speed and not afraid of unsafe. The idea: we know that UTF-8 and UTF-16 are different encodings, but if the array contains only ASCII characters (codes up to 127), each byte can be "stretched" into a 2-byte character without decoding.
unsafe string UnsafeAsciiBytesToString(byte[] bytes){ fixed (byte* pBytes = bytes) { return new string((sbyte*)pBytes, 0, bytes.Length); }}This code works only for ASCII! For Russian text (characters > 127), the result will be garbage. But for pure English, it works 2-3 times faster than Encoding.UTF8.GetString.
Pros:
- Minimal overhead — no copying, no decoding.
- Ideal for parsing HTTP headers, JSON keys (if they are ASCII).
Cons:
- Dangerous for multi-byte characters — data corruption.
- Requires enabling
unsafein the project (not always allowed). - Does not handle