Avatar ·

Как преобразовать массив байт UTF-8 в строку в C#?

Всем привет! Я начинающий C# разработчик и столкнулся с проблемой. Я получаю массив байт из сетевого потока (TCP сокет), который представляет собой текст в кодировке UTF-8. Мне нужно преобразовать этот `byte[]` обратно в обычную строку `string`, чтобы вывести её в консоль или обработать. Проблема в том, что я не понимаю, как правильно это сделать. Прямое преобразование через `(string)buffer` не работает, компилятор ругается. Я пробовал использовать `Encoding.Default.GetString(buffer)`, но на выходе получаю кракозябры вместо русского текста. Вот пример кода, который у меня есть: ```csharp byte[] utf8Bytes = new byte[] { 208, 161, 208, 176, 208, 189, 208, 186, 209, 130 }; // Это "Санкт" в UTF-8 string result = Encoding.Default.GetString(utf8Bytes); Console.WriteLine(result); // Выводит: Санкт (неправильно) ``` Подскажите, как правильно декодировать именно UTF-8? И в чём разница между разными кодировками в .NET? Я слышал про `Encoding.UTF8`, но не уверен, как его использовать. Заранее спасибо за помощь! <<>>

Правильное преобразование UTF-8 byte[] в string

Ваша проблема в том, что вы используете Encoding.Default, который применяет системную кодировку (обычно Windows-1251 на русской версии Windows), а не UTF-8. Для корректного декодирования нужно использовать Encoding.UTF8. Вот правильное решение:

byte[] utf8Bytes = new byte[] { 208, 161, 208, 176, 208, 189, 208, 186, 209, 130 };
string result = Encoding.UTF8.GetString(utf8Bytes);
Console.WriteLine(result); // Выводит: Санкт

Теперь разберёмся подробнее. В .NET есть несколько способов работы с кодировками:

Основные методы класса Encoding

  • Encoding.UTF8.GetString(byte[]) — декодирует массив байт в строку, предполагая, что байты закодированы в UTF-8.
  • Encoding.UTF8.GetBytes(string) — обратная операция: преобразует строку в массив байт UTF-8.
  • Encoding.Default — системная кодировка (опасно использовать, так как результат зависит от настроек ОС).

Дополнительные нюансы

Если вы работаете с частичными данными (например, читаете поток по частям), важно учитывать, что UTF-8 использует переменную длину символов (от 1 до 4 байт). В таких случаях используйте Decoder для корректной обработки границ:

byte[] buffer = new byte[1024];
int bytesRead = await networkStream.ReadAsync(buffer, 0, buffer.Length);
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytesRead)];
int charsDecoded = decoder.GetChars(buffer, 0, bytesRead, chars, 0);
string result = new string(chars, 0, charsDecoded);

Проверка на null и пустые массивы

Всегда проверяйте входные данные:

public static string ConvertUtf8BytesToString(byte[] bytes)
{
    if (bytes == null)
        throw new ArgumentNullException(nameof(bytes));
    if (bytes.Length == 0)
        return string.Empty;
    return Encoding.UTF8.GetString(bytes);
}

Работа с BOM (Byte Order Mark)

Иногда UTF-8 данные содержат BOM (первые три байта: 0xEF, 0xBB, 0xBF). Encoding.UTF8.GetString() корректно обрабатывает BOM, но если вы хотите его удалить, используйте new UTF8Encoding(false):

Encoding utf8WithoutBom = new UTF8Encoding(false);
string result = utf8WithoutBom.GetString(bytes);

Производительность

Для больших массивов (мегабайты+) используйте Encoding.UTF8.GetString(byte[], int, int), чтобы избежать копирования подмассива:

string result = Encoding.UTF8.GetString(byteArray, startIndex, length);

Теперь ваш код будет работать корректно с любым UTF-8 текстом, включая русский, китайский или эмодзи.

<<>>

Альтернативный подход через MemoryStream и StreamReader

Если вы работаете с потоками данных (например, из файла или сети), более удобно использовать StreamReader с указанием кодировки UTF-8:

byte[] utf8Bytes = new byte[] { 208, 161, 208, 176, 208, 189, 208, 186, 209, 130 };
using (MemoryStream ms = new MemoryStream(utf8Bytes))
using (StreamReader reader = new StreamReader(ms, Encoding.UTF8))
{
    string result = reader.ReadToEnd();
    Console.WriteLine(result); // Санкт
}

Когда это полезно

  • При чтении данных из файла: using (StreamReader reader = new StreamReader("file.txt", Encoding.UTF8)).
  • При работе с сетевыми потоками, где данные приходят частями.
  • Если нужно обработать большие объёмы данных без загрузки всего массива в память.

Важное замечание

Не забывайте закрывать StreamReader (или использовать using), чтобы освободить ресурсы. Этот способ чуть медленнее прямого вызова Encoding.UTF8.GetString(), но даёт больше гибкости при работе с потоками.