Jak przekonwertować bajt [] UTF-8 na ciąg?


931

Mam byte[]tablicę, która jest ładowana z pliku, o którym wiadomo, że zawiera UTF-8 .

W kodzie do debugowania muszę przekonwertować go na ciąg. Czy istnieje jeden liniowiec, który to zrobi?

Pod przykrywkami powinien to być tylko przydział i memcopy , więc nawet jeśli nie zostanie wdrożony, powinno być możliwe.


5
„powinien być tylko przydziałem i memcopy”: jest niepoprawny, ponieważ ciąg .NET jest zakodowany w UTF-16. Znak Unicode może być jedną jednostką kodu UTF-8 lub jedną jednostką kodu UTF-16. innym mogą być dwie jednostki kodu UTF-8 lub jedna jednostka kodu UTF-16, innym mogą być trzy jednostki kodu UTF-8 lub jedna jednostka kodu UTF-16, innym mogą być cztery jednostki kodu UTF-8 lub dwie jednostki kodu UTF-16 . Memcopy może być w stanie się rozszerzyć, ale nie byłby w stanie obsłużyć konwersji UTF-8 na UTF-16.
Tom Blodget,

Odpowiedzi:


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

13
w jaki sposób obsługuje ciągi zerowe?
maazza

14
@maazza z nieznanego powodu wcale tak nie jest. Nazywam to tak System.Text.Encoding.UTF8.GetString(buf).TrimEnd('\0');.
Cześć Angel

15
@ Hi-Angel Nieznany powód? Jedynym powodem, dla którego ciągi zakończone znakiem zerowym stały się popularne, był język C - i to tylko z powodu historycznej osobliwości (instrukcje procesora, które zajmowały się ciągami zakończonymi znakiem zerowym). .NET używa łańcuchów zakończonych znakiem zerowym tylko podczas interakcji z kodem, który używa łańcuchów zakończonych znakiem zerowym (które w końcu znikają). Jest całkowicie poprawne, aby ciąg znaków zawierał znaki NUL. Oczywiście, podczas gdy ciągi zakończone znakiem zerowym są w ASCII bardzo proste (po prostu buduj, dopóki nie otrzymasz pierwszego bajtu zerowego), inne kodowania, w tym UTF-8, nie są tak proste.
Luaan,

4
Jedną z pięknych cech UTF-8 jest to, że krótsza sekwencja nigdy nie jest podsekwencją dłuższej sekwencji. Zatem łańcuch UTF-8 zakończony zerem jest prosty.
płyn do płukania

10
Cóż, powodzenia rozpakowywanie go, jeśli nie ma ascii. Wystarczy użyć Convert.ToBase64String.
Erik Bergstedt,

323

Istnieją co najmniej cztery różne sposoby wykonania tej konwersji.

  1. Kodowanie GetString
    , ale nie będziesz w stanie odzyskać oryginalnych bajtów, jeśli te bajty zawierają znaki spoza ASCII.

  2. BitConverter.ToString
    Dane wyjściowe są ciągiem rozdzielanym znakami „-”, ale nie ma wbudowanej metody .NET umożliwiającej konwersję ciągu z powrotem na tablicę bajtów.

  3. Convert.ToBase64String
    Za pomocą można łatwo przekonwertować ciąg wyjściowy z powrotem na tablicę bajtów Convert.FromBase64String.
    Uwaga: Łańcuch wyjściowy może zawierać „+”, „/” i „=”. Jeśli chcesz użyć ciągu w adresie URL, musisz go jawnie zakodować.

  4. HttpServerUtility.UrlTokenEncode
    Za pomocą można łatwo przekonwertować ciąg wyjściowy z powrotem na tablicę bajtów HttpServerUtility.UrlTokenDecode. Łańcuch wyjściowy jest już przyjazny dla adresu URL! Minusem jest to, że wymaga System.Webmontażu, jeśli Twój projekt nie jest projektem internetowym.

Pełny przykład:

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

Ogólne rozwiązanie do konwersji z tablicy bajtów na ciąg znaków, gdy nie znasz kodowania:

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

3
Zakłada się jednak, że w strumieniu bajtów istnieje kodowanie BOM lub że jest on w UTF-8. Ale i tak możesz zrobić to samo z kodowaniem. Nie rozwiązuje to magicznie problemu, gdy nie znasz kodowania.
Sebastian Zander

12

Definicja:

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

Za pomocą:

string result = input.ConvertByteToString();

9

Konwersja a byte[]na stringwydaje się prosta, ale jakiekolwiek kodowanie może zepsuć łańcuch wyjściowy. Ta niewielka funkcja działa bez żadnych nieoczekiwanych rezultatów:

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

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

    return response;
}

Otrzymałem System.FormatException przy użyciu twojej metody, kiedy rozpakowałem ją z Convert.FromBase64String.
Erik Bergstedt,

@ AndrewJE to potrwa nawet do obliczenia, jeśli masz dużą tablicę bajtów, taką jak ta ze zdjęć.
user3841581,

7

Korzystanie (byte)b.ToString("x2"), dane wyjścioweb4b5dfe475e58b67

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

Istnieje również klasa UnicodeEncoding, dość prosta w użyciu:

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

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

Ale nie myśli UTF-8?
david.pfx

1
UnicodeEncodingjest najgorszą nazwą klasy w historii; Unicode w ogóle nie jest kodowaniem. Ta klasa to tak naprawdę UTF-16. Myślę, że wersja Little-Endian.
Nyerguds,

3

Alternatywnie:

 var byteStr = Convert.ToBase64String(bytes);

2

Jednowierszowy Linq do konwertowania tablicy bajtów byteArrFilenameodczytanej z pliku na ciąg znaków zerowych zakończony zerowym stylem czystego ascii w stylu C byłby następujący: Przydatny do odczytu takich rzeczy jak tabele indeksów plików w starych formatach archiwów.

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

Używam '?'jako domyślnego znaku dla wszystkiego, co nie jest czystym ascii, ale można to oczywiście zmienić. Jeśli chcesz mieć pewność, że możesz to wykryć, po prostu użyj '\0'zamiast tego, ponieważ TakeWhilena początku zapewnia, że ​​zbudowany w ten sposób łańcuch nie może zawierać '\0'wartości ze źródła wejściowego.


2

BitConverterklasa może być użyta do konwersji byte[]na string.

var convertedString = BitConverter.ToString(byteAttay);

Dokumentację BitConverterklasy można znaleźć w MSDN


1
Konwertuje tablicę bajtów na ciąg szesnastkowy reprezentujący każdy bajt, co na ogół nie jest tym, czego chcesz, przekształcając bajty w ciąg. Jeśli tak, to jest to kolejne pytanie, zobacz na przykład: Jak przekonwertować tablicę bajtów na ciąg szesnastkowy i odwrotnie? .
CodeCaster,

Nie to, o co poprosił OP
Winter

2

Według mojej wiedzy żadna z podanych odpowiedzi nie gwarantuje poprawnego zachowania z zerowym zakończeniem. Dopóki ktoś nie pokaże mi inaczej, napisałem własną klasę statyczną do obsługi tego za pomocą następujących metod:

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

Powodem tego startIndexbył przykład, nad którym konkretnie pracowałem, musiałem przeanalizować byte[]jako tablicę ciągów zakończonych zerem. W prostym przypadku można go bezpiecznie zignorować


Właściwie to moje. byteArr.TakeWhile(x => x != 0)to szybki i łatwy sposób rozwiązania problemu zerowego zakończenia.
Nyerguds,

1

hier to wynik, w którym nie trzeba się martwić kodowaniem. Użyłem go w mojej klasie sieciowej i wysyłałem z nim obiekty binarne jako ciąg znaków.

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

nie miałem jednego. Ale ta funkcja jest używana do transmisji binarnej w naszej sieci firmowej i do tej pory 20 TB zostało poprawnie i ponownie zakodowanych. Więc dla mnie ta funkcja działa :)
Marco Pardo

1

Oprócz wybranej odpowiedzi, jeśli używasz .NET35 lub .NET35 CE, musisz określić indeks pierwszego bajtu do zdekodowania oraz liczbę bajtów do zdekodowania:

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

0

Wypróbuj tę aplikację konsoli:

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

Widziałem kilka odpowiedzi w tym poście i można uznać, że jest to podstawowa wiedza, ponieważ istnieje kilka podejść do programowania w języku C #, aby rozwiązać ten sam problem. Jedyną rzeczą, którą należy wziąć pod uwagę, jest różnica między Pure UTF-8 i UTF-8 z BOM .

W zeszłym tygodniu, w mojej pracy, muszę opracować jedną funkcję, która wyprowadza pliki CSV z BOM i inne CSV z czystym UTF-8 (bez BOM), każdy typ kodowania pliku CSV będzie wykorzystywany przez różne niestandardowe interfejsy API, ten jeden API odczytuje UTF-8 z BOM, a drugi API odczytuje bez BOM. Muszę zbadać odniesienia do tej koncepcji, czytając „ Jaka jest różnica między UTF-8 a UTF-8 bez BOM? ” Dyskusja o przepełnieniu stosu i link do Wikipedii „ Znak kolejności bajtów ”, aby zbudować moje podejście.

Wreszcie, moje programowanie w C # dla obu typów kodowania UTF-8 (z BOM i czystym) musiało być podobne, jak w poniższym przykładzie:

//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);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.