1576 Stimmen

Wie konvertiert man ein Byte-Array in eine hexadezimale Zeichenfolge und umgekehrt?

Wie kann man ein Byte-Array in eine hexadezimale Zeichenkette umwandeln und umgekehrt?

10 Stimmen

Die akzeptierte Antwort unten scheinen eine schreckliche Menge von Zeichenfolgen in der Zeichenfolge in Bytes-Konvertierung zuzuweisen. Ich frage mich, wie sich dies auf die Leistung auswirkt

84voto

CodesInChaos Punkte 103089

Ein weiterer auf Nachschlagetabellen basierender Ansatz. Hier wird nur eine Nachschlagetabelle für jedes Byte verwendet, statt einer Nachschlagetabelle pro Nibble.

private static readonly uint[] _lookup32 = CreateLookup32();

private static uint[] CreateLookup32()
{
    var result = new uint[256];
    for (int i = 0; i < 256; i++)
    {
        string s=i.ToString("X2");
        result[i] = ((uint)s[0]) + ((uint)s[1] << 16);
    }
    return result;
}

private static string ByteArrayToHexViaLookup32(byte[] bytes)
{
    var lookup32 = _lookup32;
    var result = new char[bytes.Length * 2];
    for (int i = 0; i < bytes.Length; i++)
    {
        var val = lookup32[bytes[i]];
        result[2*i] = (char)val;
        result[2*i + 1] = (char) (val >> 16);
    }
    return new string(result);
}

Ich habe auch Varianten davon getestet, indem ich ushort , struct{char X1, X2} , struct{byte X1, X2} in der Nachschlagetabelle.

Je nach Kompilierungsziel (x86, X64) waren diese entweder ungefähr gleich leistungsfähig oder etwas langsamer als diese Variante.


Und für noch mehr Leistung sorgt die unsafe Geschwister:

private static readonly uint[] _lookup32Unsafe = CreateLookup32Unsafe();
private static readonly uint* _lookup32UnsafeP = (uint*)GCHandle.Alloc(_lookup32Unsafe,GCHandleType.Pinned).AddrOfPinnedObject();

private static uint[] CreateLookup32Unsafe()
{
    var result = new uint[256];
    for (int i = 0; i < 256; i++)
    {
        string s=i.ToString("X2");
        if(BitConverter.IsLittleEndian)
            result[i] = ((uint)s[0]) + ((uint)s[1] << 16);
        else
            result[i] = ((uint)s[1]) + ((uint)s[0] << 16);
    }
    return result;
}

public static string ByteArrayToHexViaLookup32Unsafe(byte[] bytes)
{
    var lookupP = _lookup32UnsafeP;
    var result = new char[bytes.Length * 2];
    fixed(byte* bytesP = bytes)
    fixed (char* resultP = result)
    {
        uint* resultP2 = (uint*)resultP;
        for (int i = 0; i < bytes.Length; i++)
        {
            resultP2[i] = lookupP[bytesP[i]];
        }
    }
    return new string(result);
}

Oder ob Sie es für akzeptabel halten, direkt in die Zeichenkette zu schreiben:

public static string ByteArrayToHexViaLookup32UnsafeDirect(byte[] bytes)
{
    var lookupP = _lookup32UnsafeP;
    var result = new string((char)0, bytes.Length * 2);
    fixed (byte* bytesP = bytes)
    fixed (char* resultP = result)
    {
        uint* resultP2 = (uint*)resultP;
        for (int i = 0; i < bytes.Length; i++)
        {
            resultP2[i] = lookupP[bytesP[i]];
        }
    }
    return result;
}

1 Stimmen

Warum werden bei der Erstellung der Nachschlagetabelle in der unsicheren Version die Nibbles des vorberechneten Bytes vertauscht? Ich dachte, die Endianness ändert nur die Reihenfolge von Entitäten, die aus mehreren Bytes bestehen.

1 Stimmen

@RaifAtef Was hier zählt, ist nicht die Reihenfolge der Knabbereien. Sondern die Reihenfolge der 16-Bit-Wörter in einer 32-Bit-Ganzzahl. Aber ich denke darüber nach, es so umzuschreiben, dass derselbe Code unabhängig von der Endianness ausgeführt werden kann.

1 Stimmen

Re-Lesen des Codes, ich denke, dass Sie dies taten, weil, wenn Sie die char * später zu einem uint * casten und zuweisen (bei der Generierung der Hex-Char), die Laufzeit/CPU wird die Bytes (da uint nicht das gleiche wie 2 separate 16-Bit-Zeichen behandelt wird) flippen, so dass Sie Pre-Flipping sie zu kompensieren sind. Liege ich richtig? Endianness ist verwirrend :-).

74voto

Baget Punkte 3273

Sie können die Methode BitConverter.ToString verwenden:

byte[] bytes = {0, 1, 2, 4, 8, 16, 32, 64, 128, 256}
Console.WriteLine( BitConverter.ToString(bytes));

Ausgabe:

00-01-02-04-08-10-20-40-80-FF

Weitere Informationen: BitConverter.ToString Methode (Byte[])

17 Stimmen

Das beantwortet nur die Hälfte der Frage.

4 Stimmen

Wo ist der zweite Teil der Antwort?

2 Stimmen

Ich hoffe, die Tatsache, dass 256 in "FF" umgewandelt wird, ist nur ein Tippfehler...

61voto

Waleed Eissa Punkte 9825

Ich bin gerade heute auf das gleiche Problem gestoßen und habe diesen Code gefunden:

private static string ByteArrayToHex(byte[] barray)
{
    char[] c = new char[barray.Length * 2];
    byte b;
    for (int i = 0; i < barray.Length; ++i)
    {
        b = ((byte)(barray[i] >> 4));
        c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
        b = ((byte)(barray[i] & 0xF));
        c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
    }
    return new string(c);
}

Quelle: Beitrag im Forum byte[] Array zu Hex-String (siehe den Beitrag von PZahra). Ich habe den Code ein wenig geändert, um das Präfix 0x zu entfernen.

Ich habe den Code einigen Leistungstests unterzogen, und er war fast achtmal schneller als die Verwendung von BitConverter.ToString() (der schnellsten Methode laut Patridges Beitrag).

0 Stimmen

Ganz zu schweigen davon, dass dies am wenigsten Speicherplatz benötigt. Es werden keinerlei Zwischenstrings erzeugt.

9 Stimmen

Das beantwortet nur die Hälfte der Frage.

0 Stimmen

Das ist großartig, weil es im Grunde mit jeder Version von NET funktioniert, einschließlich NETMF. Ein Gewinner!

31voto

balrob Punkte 485

Ab .NET 5 RC2 können Sie verwenden:

Es sind Überladungen verfügbar, die Spannenparameter annehmen.

2 Stimmen

In .NET 6, Convert.ToHexString verwendet den SSSE3-Befehlssatz auf der CPU, so dass er nicht nur bequemer zu verwenden ist als in .NET 5, sondern auch mehr leistungsfähig für Eingaben von mehr als 3 Byte. Der Leistungsunterschied wird mit zunehmender Eingabegröße deutlicher.

21voto

CoperNick Punkte 2243

Ergänzung zur Antwort von @CodesInChaos (umgekehrte Methode)

public static byte[] HexToByteUsingByteManipulation(string s)
{
    byte[] bytes = new byte[s.Length / 2];
    for (int i = 0; i < bytes.Length; i++)
    {
        int hi = s[i*2] - 65;
        hi = hi + 10 + ((hi >> 31) & 7);

        int lo = s[i*2 + 1] - 65;
        lo = lo + 10 + ((lo >> 31) & 7) & 0x0f;

        bytes[i] = (byte) (lo | hi << 4);
    }
    return bytes;
}

Erläuterung:

& 0x0f soll auch Kleinbuchstaben unterstützen

hi = hi + 10 + ((hi >> 31) & 7); ist dasselbe wie:

hi = ch-65 + 10 + (((ch-65) >> 31) & 7);

Für '0' 9' ist es dasselbe wie hi = ch - 65 + 10 + 7; das ist hi = ch - 48 (Dies liegt an den 0xffffffff & 7 ).

Für 'A' 'F' ist es hi = ch - 65 + 10; (Dies liegt an den 0x00000000 & 7 ).

Für 'a' f' haben wir zu große Zahlen, also müssen wir 32 von der Standardversion subtrahieren, indem wir einige Bits machen 0 durch die Verwendung von & 0x0f .

65 ist der Code für 'A'

48 ist der Code für '0'

7 ist die Anzahl der Buchstaben zwischen '9' y 'A' in der ASCII-Tabelle ( ...456789:;<=>?@ABCD... ).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X