Як перетворити байт UTF-8 [] у рядок?


932

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

У якомусь коді налагодження мені потрібно перетворити його в рядок. Чи є один лайнер, який буде це робити?

Під обкладинками має бути лише розподіл та записка , тому навіть якщо вона не буде реалізована, це має бути можливим.


5
"має бути лише розподілом та запискою": невірно, оскільки .NET рядок закодовано UTF-16. Символ Unicode може бути одним блоком коду UTF-8 або одним кодовим блоком UTF-16. інший може бути двома кодовими блоками UTF-8 або одним кодовим блоком UTF-16, інший може бути трьома кодовими кодами UTF-8 або одним кодовим блоком UTF-16, інший може бути чотирма кодовими кодами UTF-8 або двома кодовими кодами UTF-16 . Пам'ятка може бути розширена, але вона не зможе обробити перетворення UTF-8 в UTF-16.
Том Блоджет

Відповіді:


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

13
як він обробляє нульові рядки?
maazza

14
@maazza з невідомих причин це зовсім не відбувається. Я це називаю так System.Text.Encoding.UTF8.GetString(buf).TrimEnd('\0');.
Привіт-Ангел

15
@ Привіт-Ангел Невідома причина? Єдиною причиною, коли нульові рядки коли-небудь стали популярними, - це мова С - і навіть це було лише через історичну диваку (інструкції процесора, які стосувалися рядків з нульовим завершенням). .NET використовує лише рядки з нульовим завершенням при взаємодії з кодом, який використовує рядки з нульовим завершенням (які остаточно зникають). Це цілком справедливо, якщо рядок містить символи NUL. І звичайно, хоча в ASCII рядки з нульовим завершенням просто мертві (просто будуйте, поки не отримаєте перший нульовий байт), інші кодування, включаючи UTF-8, не такі прості.
Луань

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

10
Добре, вдало розпакувавши його, якщо у нього є не-ассії. Просто використовуйте 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 це: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
Але це передбачає, що в байтовому потоці є або кодуюча BOM, або що вона знаходиться в UTF-8. Але ви можете зробити те ж саме з Encoding у будь-якому випадку. Це не вирішує магічну проблему, коли ви не знаєте кодування.
Себастьян Зандер

12

Визначення:

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

Використання:

string result = input.ConvertByteToString();

9

Перетворення a byte[]на 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 це буде потрібно навіть для обчислення, якщо у вас є великий байтовий масив, як той, який використовується із зображень.
користувач3841581

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- найгірше ім’я класу, що коли-небудь; unicode зовсім не кодування. Цей клас насправді є UTF-16. Думаю, малоіндійська версія.
Nyerguds


2

Одне вкладиш Linq для перетворення байтового масиву, byteArrFilenameпрочитаного з файлу, до чистого рядка з нульовим завершенням у стилі ascii:

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)це швидкий і простий спосіб вирішити нульову проблему припинення.
Nyerguds

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 з BOM, а інший API читається без BOM. Мені потрібно вивчити посилання на цю концепцію, прочитавши " Яка різниця між UTF-8 та UTF-8 без BOM? ", Обговорення стека переповнення та посилання Вікіпедії " Позначення порядку байтів ", щоб побудувати мій підхід.

Нарешті, моє програмування на 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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.