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


933

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

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

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


5
«должно быть просто выделением и записью»: неверно, потому что строка .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.
Том Блоджет

Ответы:


1471
string result = System.Text.Encoding.UTF8.GetString(byteArray);

13
как он обрабатывает строки с нулевым окончанием?
Маацца

14
@maazza по неизвестной причине это не так. Я называю это как System.Text.Encoding.UTF8.GetString(buf).TrimEnd('\0');.
Привет-ангел

15
@ Привет-Ангел Неизвестная причина? Единственной причиной, по которой строки с нулевым символом в конце стали когда-либо популярными, был язык C - и даже это было только из-за исторической странности (инструкции CPU, которые имели дело со строками с нулевым символом в конце). .NET использует только строки с нулевым символом в конце при взаимодействии с кодом, в котором используются строки с нулевым символом в конце (которые в конечном итоге исчезают). Вполне допустимо, чтобы строка содержала NUL-символы. И, конечно, в то время как строки с нулевым символом в ASCII очень просты (просто собирайте, пока не получите первый нулевой байт), другие кодировки, включая UTF-8, не так просты.
Луаан

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

10
Что ж, удачи в распаковке, если у него нет ascii. Просто используйте Convert.ToBase64String.
Эрик Бергштедт

323

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

  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

7
LINQ it:var decBytes2 = str.Split('-').Select(ch => Convert.ToByte(ch, 16)).ToArray();
drtf

25

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

static string BytesToStringConverted(byte[] bytes)
{
    using (var stream = new MemoryStream(bytes))
    {
        using (var streamReader = new StreamReader(stream))
        {
            return streamReader.ReadToEnd();
        }
    }
}

3
Но это предполагает, что в байтовом потоке есть либо кодирующая спецификация, либо она находится в UTF-8. Но вы можете сделать то же самое с кодировкой в ​​любом случае. Это волшебным образом не решает проблему, когда вы не знаете кодировку.
Себастьян Зандер

12

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

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

С помощью:

string result = input.ConvertByteToString();

9

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

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

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

    return response;
}

Я получил System.FormatException, используя ваш метод, когда распаковал его с Convert.FromBase64String.
Эрик Бергштедт

@ AndrewJE это займет даже для вычисления, если у вас есть большой массив байтов, как тот, который используется на рисунках.
user3841581

7

Использование (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;
    }

}

4

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

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

Console.WriteLine("Data after decoding: {0}", ByteConverter.GetString(dataEncoded));

Но не UTF-8 метинкс?
david.pfx

1
UnicodeEncodingсамое плохое имя класса когда-либо; Юникод вообще не является кодировкой. Этот класс на самом деле UTF-16. Версия с прямым порядком байтов, я думаю.
Nyerguds

3

В качестве альтернативы:

 var byteStr = Convert.ToBase64String(bytes);

2

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

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

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


2

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

var convertedString = BitConverter.ToString(byteAttay);

Документация BitConverterкласса может быть найдена на MSDN


1
Это преобразует массив байтов в шестнадцатеричную строку, представляющую каждый байт, что обычно не то, что вы хотите при преобразовании байтов в строку. Если да, то это другой вопрос, см., Например, Как преобразовать массив байтов в шестнадцатеричную строку и наоборот? ,
CodeCaster

Не то, что спросил ОП
зима

2

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

// 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[]массив как массив строк с нулевым символом в конце. Это может быть безопасно проигнорировано в простом случае


Мой, на самом деле. byteArr.TakeWhile(x => x != 0)это быстрый и простой способ решить проблему нулевого завершения.
Нергудс

1

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);
        }

не было одного. Но эта функция используется для двоичной передачи в нашей корпоративной сети, и до сих пор 20 ТБ были повторно и правильно закодированы. Так что для меня эта функция работает :)
Марко Пардо

1

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

string result = System.Text.Encoding.UTF8.GetString(byteArray,0,byteArray.Length);

0

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

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);
}

0

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

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

Наконец, мое программирование на C # для обоих типов кодирования UTF-8 (с BOM и pure) должно быть примерно таким, как в следующем примере:

//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);

0
static string BytesToStringConverted(byte[] bytes)
{
    using (var stream = new MemoryStream(bytes))
    {
        using (var streamReader = new StreamReader(stream))
        {
            return streamReader.ReadToEnd();
        }
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.