Как преобразовать байт [] UTF-8 в строку?

У меня есть массив byte[], загруженный из файла, который, как мне известно, содержит UTF-8.

В некотором отладочном коде мне нужно преобразовать его в строку. Есть ли один лайнер, который сделает это?

По сути, это должно быть просто выделение памяти и memcopy, поэтому даже если это не реализовано, это должно быть возможно.


person BCS    schedule 16.06.2009    source источник
comment
должно быть просто выделением, а memcopy: неверно, потому что строка .NET закодирована в UTF-16. Символом Unicode может быть одна кодовая единица UTF-8 или одна кодовая единица UTF-16. другой может быть двумя кодовыми единицами UTF-8 или одной кодовой единицей UTF-16, другой может быть тремя кодовыми единицами UTF-8 или одной кодовой единицей UTF-16, другой может быть четырьмя кодовыми единицами UTF-8 или двумя кодовыми единицами UTF-16 . Memcopy может расширяться, но не сможет обрабатывать преобразование UTF-8 в UTF-16.   -  person Tom Blodget    schedule 19.11.2016


Ответы (15)


Есть как минимум четыре различных способа сделать это преобразование.

  1. GetString кодировки
    , но вы не сможете вернуть исходные байты, если эти байты содержат символы, отличные от ASCII.

  2. BitConverter.ToString
    Вывод представляет собой строку с разделителями «-», но нет встроенного метода .NET для преобразования строки обратно в массив байтов.

  3. Convert.ToBase64String
    Вы можете легко преобразовать выходную строку обратно в байтовый массив, используя Convert.FromBase64String.
    Примечание. Выходная строка может содержать '+', '/' и '= '. Если вы хотите использовать строку в URL-адресе, вам необходимо явно ее закодировать.

  4. HttpServerUtility.UrlTokenEncode
    Вы можете легко преобразовать выходную строку обратно в байтовый массив с помощью HttpServerUtility.UrlTokenDecode. Строка вывода уже совместима с URL! Обратной стороной является System.Web сборка, если ваш проект не является веб-проектом.

Полный пример:

byte[] bytes = { 130, 200, 234, 23 }; // A byte array contains non-ASCII (or non-readable) characters

string s1 = Encoding.UTF8.GetString(bytes); // ���
byte[] decBytes1 = Encoding.UTF8.GetBytes(s1);  // decBytes1.Length == 10 !!
// decBytes1 not same as bytes
// Using UTF-8 or other Encoding object will get similar results

string s2 = BitConverter.ToString(bytes);   // 82-C8-EA-17
String[] tempAry = s2.Split('-');
byte[] decBytes2 = new byte[tempAry.Length];
for (int i = 0; i < tempAry.Length; i++)
    decBytes2[i] = Convert.ToByte(tempAry[i], 16);
// decBytes2 same as bytes

string s3 = Convert.ToBase64String(bytes);  // gsjqFw==
byte[] decByte3 = Convert.FromBase64String(s3);
// decByte3 same as bytes

string s4 = HttpServerUtility.UrlTokenEncode(bytes);    // gsjqFw2
byte[] decBytes4 = HttpServerUtility.UrlTokenDecode(s4);
// decBytes4 same as bytes
person detale    schedule 28.02.2014
comment
LINQ it: var decBytes2 = str.Split('-').Select(ch => Convert.ToByte(ch, 16)).ToArray(); - person drtf; 13.07.2014
comment
Это должен быть принятый ответ. Он прекрасно иллюстрирует вывод нескольких методов. Текущий принятый ответ показывает только один, что может быть проблематичным для некоторых разработчиков, которые не прокручивают так далеко вниз. - если, конечно, не сортировать по голосам. - person dimitar.bogdanov; 11.04.2021

Общее решение для преобразования из массива байтов в строку, когда вы не знаете кодировку:

static string BytesToStringConverted(byte[] bytes)
{
    using (var stream = new MemoryStream(bytes))
    {
        using (var streamReader = new StreamReader(stream))
        {
            return streamReader.ReadToEnd();
        }
    }
}
person Nir    schedule 20.09.2015
comment
Но это предполагает, что либо в байтовом потоке есть спецификация кодирования, либо она находится в UTF-8. Но вы все равно можете сделать то же самое с кодированием. Это не решает проблему волшебным образом, если вы не знаете кодировку. - person Sebastian Zander; 26.09.2017

Определение:

public static string ConvertByteToString(this byte[] source)
{
    return source != null ? System.Text.Encoding.UTF8.GetString(source) : null;
}

Использование:

string result = input.ConvertByteToString();
person Erçin Dedeoğlu    schedule 16.10.2014

Преобразование byte[] в string кажется простым, но любая кодировка может испортить выходную строку. Эта маленькая функция просто работает без каких-либо неожиданных результатов:

private string ToString(byte[] bytes)
{
    string response = string.Empty;

    foreach (byte b in bytes)
        response += (Char)b;

    return response;
}
person AndrewJE    schedule 22.04.2015
comment
Я получил System.FormatException с помощью вашего метода, когда распаковал его с помощью Convert.FromBase64String. - person Erik Bergstedt; 12.12.2015
comment
@ AndrewJE это займет даже вычисление, если у вас есть большой массив байтов, подобный тому, который используется на рисунках. - person user3841581; 04.11.2017

Используя (byte)b.ToString("x2"), выдает b4b5dfe475e58b67

public static class Ext {

    public static string ToHexString(this byte[] hex)
    {
        if (hex == null) return null;
        if (hex.Length == 0) return string.Empty;

        var s = new StringBuilder();
        foreach (byte b in hex) {
            s.Append(b.ToString("x2"));
        }
        return s.ToString();
    }

    public static byte[] ToHexBytes(this string hex)
    {
        if (hex == null) return null;
        if (hex.Length == 0) return new byte[0];

        int l = hex.Length / 2;
        var b = new byte[l];
        for (int i = 0; i < l; ++i) {
            b[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
        }
        return b;
    }

    public static bool EqualsTo(this byte[] bytes, byte[] bytesToCompare)
    {
        if (bytes == null && bytesToCompare == null) return true; // ?
        if (bytes == null || bytesToCompare == null) return false;
        if (object.ReferenceEquals(bytes, bytesToCompare)) return true;

        if (bytes.Length != bytesToCompare.Length) return false;

        for (int i = 0; i < bytes.Length; ++i) {
            if (bytes[i] != bytesToCompare[i]) return false;
        }
        return true;
    }

}
person metadings    schedule 22.11.2014

Также существует довольно простой в использовании класс UnicodeEncoding:

ByteConverter = new UnicodeEncoding();
string stringDataForEncoding = "My Secret Data!";
byte[] dataEncoded = ByteConverter.GetBytes(stringDataForEncoding);

Console.WriteLine("Data after decoding: {0}", ByteConverter.GetString(dataEncoded));
person P.K.    schedule 18.05.2015
comment
Но не UTF-8? - person david.pfx; 14.07.2015
comment
UnicodeEncoding - худшее имя класса на свете; Юникод - это вообще не кодировка. Этот класс на самом деле UTF-16. Думаю, версия с прямым порядком байтов. - person Nyerguds; 17.11.2016

Класс BitConverter можно использовать для преобразования byte[] в string.

var convertedString = BitConverter.ToString(byteAttay);

Документацию по классу BitConverter можно найти на MSDN

person Sagar    schedule 05.01.2017
comment
Это преобразует массив байтов в шестнадцатеричную строку, представляющую каждый байт, что обычно не то, что вам нужно при преобразовании байтов в строку. Если да, то это другой вопрос, см., Например, Как преобразовать массив байтов в шестнадцатеричную строку и наоборот?. - person CodeCaster; 05.01.2017
comment
Не то, что спросил OP - person Winter; 19.07.2017

Альтернативно:

 var byteStr = Convert.ToBase64String(bytes);
person Fehr    schedule 15.09.2016

Однострочным Linq для преобразования байтового массива byteArrFilename, прочитанного из файла, в чистую строку ascii C-стиля с нулевым завершением будет следующее: Удобно для чтения таких вещей, как таблицы индексов файлов в старых форматах архивов.

String filename = new String(byteArrFilename.TakeWhile(x => x != 0)
                              .Select(x => x < 128 ? (Char)x : '?').ToArray());

Я использую '?' в качестве символа по умолчанию для всего, что не является чистым ascii, но это, конечно, можно изменить. Если вы хотите быть уверены, что можете его обнаружить, просто используйте вместо этого '\0', поскольку TakeWhile в начале гарантирует, что построенная таким образом строка не может содержать '\0' значений из источника ввода.

person Nyerguds    schedule 17.11.2016

Насколько мне известно, ни один из приведенных ответов не гарантирует правильного поведения с нулевым завершением. Пока кто-то не покажет мне другое, я написал свой собственный статический класс для обработки этого с помощью следующих методов:

// Mimics the functionality of strlen() in c/c++
// Needed because niether StringBuilder or Encoding.*.GetString() handle \0 well
static int StringLength(byte[] buffer, int startIndex = 0)
{
    int strlen = 0;
    while
    (
        (startIndex + strlen + 1) < buffer.Length // Make sure incrementing won't break any bounds
        && buffer[startIndex + strlen] != 0       // The typical null terimation check
    )
    {
        ++strlen;
    }
    return strlen;
}

// This is messy, but I haven't found a built-in way in c# that guarentees null termination
public static string ParseBytes(byte[] buffer, out int strlen, int startIndex = 0)
{
    strlen = StringLength(buffer, startIndex);
    byte[] c_str = new byte[strlen];
    Array.Copy(buffer, startIndex, c_str, 0, strlen);
    return Encoding.UTF8.GetString(c_str);
}

Причина startIndex была в примере, над которым я работал, в частности, мне нужно было проанализировать byte[] как массив строк с завершающим нулем. Его можно смело игнорировать в простом случае.

person Assimilater    schedule 29.06.2017
comment
На самом деле, моя. byteArr.TakeWhile(x => x != 0) - это быстрый и простой способ решить проблему нулевого завершения. - person Nyerguds; 21.09.2017

В дополнение к выбранному ответу, если вы используете .NET35 или .NET35 CE, вам необходимо указать индекс первого байта для декодирования и количество байтов для декодирования:

string result = System.Text.Encoding.UTF8.GetString(byteArray,0,byteArray.Length);
person The One    schedule 01.02.2019

Я видел несколько ответов в этом посте, и их можно считать завершенными базовыми знаниями, потому что в программировании на C # есть несколько подходов для решения той же проблемы. Единственное, что необходимо учитывать, - это разница между Pure UTF-8 и UTF-8 с B.O.M..

На прошлой неделе на моей работе мне нужно было разработать одну функцию, которая выводит файлы CSV с помощью B.O.M. и другие CSV-файлы с чистым UTF-8 (без B.O.M.), каждый тип кодировки CSV-файла будет использоваться разными нестандартизированными API-интерфейсами, один из которых считывает UTF-8 с помощью B.O.M. и другой API, читаемый без спецификации. Мне нужно изучить ссылки на эту концепцию, прочитав "В чем разница между UTF-8 и UTF-8 без спецификации?" Обсуждение переполнения стека и эта ссылка в Википедии "Отметка порядка байтов" для построения моего подхода.

Наконец, мое программирование на C # для обоих типов кодировки UTF-8 (с B.O.M. и чистым) должно быть похоже на этот пример ниже:

//for UTF-8 with B.O.M., equals shared by Zanoni (at top)
string result = System.Text.Encoding.UTF8.GetString(byteArray);

//for Pure UTF-8 (without B.O.M.)
string result = (new UTF8Encoding(false)).GetString(byteArray);
person Antonio Leonardo    schedule 21.05.2020
comment
Разве вам не нужно специально снимать спецификацию с самого начала? Насколько мне известно, даже если вы используете UTF8Encoding с BOM, он не отключит это автоматически. - person Nyerguds; 14.01.2021
comment
@Nyerguds, объект UTF8Encoding с ложным значением параметра не имеет спецификации. - person Antonio Leonardo; 12.02.2021
comment
Нет, я имею в виду, что если у текста есть спецификация, даже System.Text.Encoding.UTF8 не автоматически удаляет это. Попробуйте сами. - person Nyerguds; 14.02.2021

Попробуйте это консольное приложение:

static void Main(string[] args)
{
    //Encoding _UTF8 = Encoding.UTF8;
    string[] _mainString = { "Héllo World" };
    Console.WriteLine("Main String: " + _mainString);

    //Convert a string to utf-8 bytes.
    byte[] _utf8Bytes = Encoding.UTF8.GetBytes(_mainString[0]);

    //Convert utf-8 bytes to a string.
    string _stringuUnicode = Encoding.UTF8.GetString(_utf8Bytes);
    Console.WriteLine("String Unicode: " + _stringuUnicode);
}
person R M Shahidul Islam Shahed    schedule 29.09.2019

hier - это результат, в котором вам не нужно было беспокоиться о кодировании. Я использовал его в своем классе сети и отправлял двоичные объекты в виде строки с ним.

        public static byte[] String2ByteArray(string str)
        {
            char[] chars = str.ToArray();
            byte[] bytes = new byte[chars.Length * 2];

            for (int i = 0; i < chars.Length; i++)
                Array.Copy(BitConverter.GetBytes(chars[i]), 0, bytes, i * 2, 2);

            return bytes;
        }

        public static string ByteArray2String(byte[] bytes)
        {
            char[] chars = new char[bytes.Length / 2];

            for (int i = 0; i < chars.Length; i++)
                chars[i] = BitConverter.ToChar(bytes, i * 2);

            return new string(chars);
        }
person Marco Pardo    schedule 06.07.2018
comment
не было ни одного. Но эта функция используется для двоичной передачи в нашей корпоративной сети, и до сих пор 20 ТБ были перекодированы правильно. Так что у меня эта функция работает :) - person Marco Pardo; 17.09.2018

person    schedule
comment
как он обрабатывает строки с нулевым окончанием? - person maazza; 12.05.2015
comment
@maazza по неизвестной причине совсем не так. Я называю это как System.Text.Encoding.UTF8.GetString(buf).TrimEnd('\0');. - person Hi-Angel; 27.07.2015
comment
@ Hi-Angel Неизвестная причина? Единственной причиной, по которой строки с завершающим нулевым символом в конце стали популярными, был язык C - и даже это было только из-за исторической странности (инструкции ЦП, которые имели дело со строками с нулевым символом в конце). .NET использует строки с завершающим нулем только при взаимодействии с кодом, который использует строки с завершающим нулем (которые наконец исчезают). Совершенно допустимо, чтобы строка содержала символы NUL. И, конечно же, хотя строки с завершающим нулем в ASCII очень просты (просто создавайте, пока не получите первый нулевой байт), другие кодировки, включая UTF-8, не так просты. - person Luaan; 23.11.2015
comment
Одна из прекрасных особенностей UTF-8 заключается в том, что более короткая последовательность никогда не является подпоследовательностью более длинной последовательности. Таким образом, строка UTF-8 с завершающим нулем проста. - person plugwash; 24.11.2015
comment
Что ж, удачи с распаковкой, если там не ascii. Просто используйте Convert.ToBase64String. - person Erik Bergstedt; 12.12.2015
comment
Пример, демонстрирующий, что это не заканчивается нулевыми символами. Encoding.Ascii дает такие же результаты - person Assimilater; 29.06.2017
comment
Я очень рад возможности использовать ваши знания, дорогие друзья. Удачи и спасибо за ваши индивидуальные объяснения и ответы. @ Hi-Angel Могу я спросить, почему вы использовали TrimEnd ?? - person elnaz jangi; 12.06.2020
comment
@elnazjangi Я давно не использовал C #, но AFAIR в C # нулевой байт является допустимым элементом строки. Однако это бесполезно, поэтому вызов .TrimEnd('\0') просто удаляет их, если они обнаруживаются в конце. Относительно того, почему он должен быть там: в языках C и C ++ нулевой байт имеет особое значение, он отмечает конец строки. Поэтому, если вы знаете, что находитесь в обстоятельствах, когда строка, которую вы получаете из буфера, может иметь нулевое завершение, вы должны использовать этот вызов функции. - person Hi-Angel; 13.06.2020