Avatar ·

Как преобразовать массив байтов UTF-8 (byte[]) в строку (string) в C#?

Всем привет! Я новичок в C# и столкнулся с проблемой при работе с сетевыми данными. У меня есть метод, который получает массив байтов (byte[]) из TCP-сокета. Я знаю, что эти байты закодированы в UTF-8, и мне нужно преобразовать их в обычную строку (string), чтобы вывести в консоль или обработать дальше. Вот пример кода, который у меня есть: ```csharp byte[] data = new byte[] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100 }; // "Hello World" в UTF-8 string result = data.ToString(); // Получаю "System.Byte[]" Console.WriteLine(result); ``` Очевидно, что `data.ToString()` не работает — он возвращает имя типа, а не содержимое. Я пробовал использовать `Convert.ToBase64String()`, но это даёт строку в base64, а не в читаемом виде. Пробовал также просто привести к строке через (string)data — компилятор ругается. Я знаю, что в .NET есть класс `Encoding`, но не понимаю, как его правильно применить. Подскажите, пожалуйста, как корректно и безопасно преобразовать byte[] UTF-8 в string? Важно, чтобы учитывались все символы, включая кириллицу и спецсимволы. Заранее спасибо! <<>>

Использование класса Encoding.UTF8

Для преобразования массива байтов в строку с учётом кодировки UTF-8 в C# используется статический класс System.Text.Encoding. Основной метод — GetString(), который принимает массив байтов и опционально начальный индекс и длину.

Вот правильное решение вашей задачи:

using System;
using System.Text;

public class Program
{
    public static void Main()
    {
        byte[] data = new byte[] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100 };
        
        // Преобразование всего массива
        string result = Encoding.UTF8.GetString(data);
        Console.WriteLine(result); // Выведет: Hello World
        
        // Работа с частью массива (индекс 0, длина 5)
        string part = Encoding.UTF8.GetString(data, 0, 5);
        Console.WriteLine(part); // Выведет: Hello
    }
}

Важные нюансы и альтернативы

  • Кириллица и Unicode: UTF-8 корректно обрабатывает любые символы, включая кириллицу, иероглифы и эмодзи. Пример:
    byte[] cyrillic = Encoding.UTF8.GetBytes("Привет, мир!");
    string text = Encoding.UTF8.GetString(cyrillic);
    Console.WriteLine(text); // Привет, мир!
  • Обработка неполных данных: Если вы получаете данные частями (например, из сети), используйте Decoder для буферизации:
    Decoder decoder = Encoding.UTF8.GetDecoder();
    byte[] partialData = new byte[] { 208, 159, 209 }; // Начало слова "Привет"
    char[] chars = new char[decoder.GetCharCount(partialData, 0, partialData.Length, false)];
    int charsDecoded = decoder.GetChars(partialData, 0, partialData.Length, chars, 0, false);
    string partialString = new string(chars, 0, charsDecoded);
    Console.WriteLine(partialString); // Может вывести "П" или "Пр" в зависимости от границы
  • Производительность: Для больших массивов (сотни мегабайт) используйте перегрузку GetString(ReadOnlySpan<byte>) в .NET Core 3.0+:
    ReadOnlySpan<byte> span = data.AsSpan();
    string fastResult = Encoding.UTF8.GetString(span);
  • Ошибки декодирования: По умолчанию Encoding.UTF8 выбрасывает DecoderFallbackException при невалидных байтах. Чтобы избежать этого, задайте fallback:
    Encoding safeEncoding = Encoding.GetEncoding("UTF-8", 
        EncoderFallback.ReplacementFallback, 
        DecoderFallback.ReplacementFallback);
    string safeResult = safeEncoding.GetString(data);

Почему не работает data.ToString()?

Метод ToString() для массивов в C# не переопределён для вывода содержимого — он возвращает имя типа (System.Byte[]). Для преобразования байтов в строку всегда используйте Encoding.

Заключение

Основной способ — Encoding.UTF8.GetString(byteArray). Он прост, надёжен и поддерживает все символы Unicode. Для потоковой обработки используйте Decoder, а для повышения производительности — Span<T>. Всегда помните про возможные ошибки декодирования и задавайте fallback при работе с ненадёжными данными.

<<>>

Использование System.Text.Encoding.UTF8.GetString() с учётом длины

Когда вы работаете с буферами, которые могут быть больше, чем фактические данные (например, при чтении из сокета), обязательно указывайте реальную длину данных, а не длину массива:

byte[] buffer = new byte[1024];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
string message = Encoding.UTF8.GetString(buffer, 0, bytesRead); // Только прочитанные байты

Если вы передадите весь массив (buffer.Length), то в строку попадут нулевые байты (\0), что может привести к неожиданным результатам или ошибкам.

Дополнительный совет: использование MemoryStream и StreamReader

Если вы предпочитаете более высокоуровневый подход, можно использовать StreamReader:

using (var ms = new MemoryStream(byteArray))
using (var reader = new StreamReader(ms, Encoding.UTF8))
{
    string result = reader.ReadToEnd();
}

Этот способ удобен, когда байты уже представлены в виде потока, но для простого массива Encoding.UTF8.GetString() быстрее и лаконичнее.