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

1632voto

Tomalak Punkte 320467

Sie können verwenden Convert.ToHexString beginnend mit .NET 5.
Es gibt auch eine Methode für den umgekehrten Vorgang: Convert.FromHexString .


Für ältere Versionen von .NET können Sie entweder verwenden:

public static string ByteArrayToString(byte[] ba)
{
  StringBuilder hex = new StringBuilder(ba.Length * 2);
  foreach (byte b in ba)
    hex.AppendFormat("{0:x2}", b);
  return hex.ToString();
}

oder:

public static string ByteArrayToString(byte[] ba)
{
  return BitConverter.ToString(ba).Replace("-","");
}

Es gibt noch weitere Varianten, zum Beispiel aquí .

Die umgekehrte Umrechnung würde folgendermaßen aussehen:

public static byte[] StringToByteArray(String hex)
{
  int NumberChars = hex.Length;
  byte[] bytes = new byte[NumberChars / 2];
  for (int i = 0; i < NumberChars; i += 2)
    bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
  return bytes;
}

Verwendung von Substring ist die beste Option in Kombination mit Convert.ToByte . Siehe diese Antwort für weitere Informationen. Wenn Sie eine bessere Leistung benötigen, müssen Sie Folgendes vermeiden Convert.ToByte bevor Sie die SubString .

0 Stimmen

In der Zeile hex.AppendFormat("{0:x2}", b) fehlt ein Semikolon

31 Stimmen

Sie verwenden SubString. Weist diese Schleife nicht eine schreckliche Menge an String-Objekten zu?

33 Stimmen

Ehrlich gesagt - solange es die Leistung nicht dramatisch einschränkt, würde ich dazu neigen, dies zu ignorieren und darauf vertrauen, dass die Runtime und der GC sich darum kümmern.

537voto

patridge Punkte 25936

Leistungsanalyse

Anmerkung: neuer Leiter ab 2015-08-20.

Ich habe jede der verschiedenen Umwandlungsmethoden durch einige grobe Stopwatch Leistungstests, ein Lauf mit einem zufälligen Satz (n=61, 1000 Iterationen) und ein Lauf mit einem Projekt Gutenburg-Text (n=1.238.957, 150 Iterationen). Hier sind die Ergebnisse, grob geordnet vom schnellsten zum langsamsten. Alle Messungen sind in Ticks ( 10.000 Ticks = 1 ms ) und alle relativen Noten werden mit der [langsamsten] StringBuilder Umsetzung. Für den verwendeten Code siehe unten oder die Test-Framework-Repositorium wo ich jetzt den Code für die Ausführung dieses Programms verwalte.

Haftungsausschluss

WARNUNG: Verlassen Sie sich nicht auf diese Statistiken, sie sind lediglich ein Beispiel für eine Reihe von Beispieldaten. Wenn Sie wirklich eine erstklassige Leistung benötigen, sollten Sie diese Methoden in einer Umgebung testen, die für Ihre Produktionsanforderungen repräsentativ ist, und zwar mit Daten, die für das, was Sie verwenden werden, repräsentativ sind.

Résultats

Nachschlagetabellen haben der Byte-Manipulation den Rang abgelaufen. Im Grunde gibt es eine Form der Vorausberechnung, was ein bestimmtes Nibble oder Byte in Hexadezimalzeichen sein wird. Wenn Sie dann die Daten durchgehen, sehen Sie einfach im nächsten Abschnitt nach, welche Hex-Zeichenkette das sein könnte. Dieser Wert wird dann auf irgendeine Weise zu der resultierenden Zeichenkettenausgabe hinzugefügt. Lange Zeit war die Byte-Manipulation, die von einigen Entwicklern möglicherweise schwerer zu lesen ist, die leistungsfähigste Methode.

Am besten ist es immer noch, einige repräsentative Daten zu finden und sie in einer produktionsähnlichen Umgebung auszuprobieren. Wenn Sie unterschiedliche Speicherbeschränkungen haben, ziehen Sie vielleicht eine Methode mit weniger Zuweisungen einer Methode vor, die zwar schneller ist, aber mehr Speicher verbraucht.

Prüfung Code

Sie können gerne mit dem von mir verwendeten Testcode spielen. Eine Version ist hier enthalten, aber Sie können den Code auch klonen. Repo und fügen Sie Ihre eigenen Methoden hinzu. Bitte reichen Sie einen Pull-Request ein, wenn Sie etwas Interessantes finden oder helfen wollen, das verwendete Test-Framework zu verbessern.

  1. Fügen Sie die neue statische Methode ( Func<byte[], string> ) nach /Tests/ConvertByteArrayToHexString/Test.cs.
  2. Fügen Sie den Namen dieser Methode in die TestCandidates Rückgabewert in derselben Klasse.
  3. Vergewissern Sie sich, dass Sie die gewünschte Eingabeversion, Satz oder Text, ausführen, indem Sie die Kommentare in GenerateTestInput in derselben Klasse.
  4. Hit F5 und warten Sie auf die Ausgabe (es wird auch ein HTML-Dump im Ordner /bin erzeugt).

    static string ByteArrayToHexStringViaStringJoinArrayConvertAll(byte[] bytes) { return string.Join(string.Empty, Array.ConvertAll(bytes, b => b.ToString("X2"))); } static string ByteArrayToHexStringViaStringConcatArrayConvertAll(byte[] bytes) { return string.Concat(Array.ConvertAll(bytes, b => b.ToString("X2"))); } static string ByteArrayToHexStringViaBitConverter(byte[] bytes) { string hex = BitConverter.ToString(bytes); return hex.Replace("-", ""); } static string ByteArrayToHexStringViaStringBuilderAggregateByteToString(byte[] bytes) { return bytes.Aggregate(new StringBuilder(bytes.Length 2), (sb, b) => sb.Append(b.ToString("X2"))).ToString(); } static string ByteArrayToHexStringViaStringBuilderForEachByteToString(byte[] bytes) { StringBuilder hex = new StringBuilder(bytes.Length 2); foreach (byte b in bytes) hex.Append(b.ToString("X2")); return hex.ToString(); } static string ByteArrayToHexStringViaStringBuilderAggregateAppendFormat(byte[] bytes) { return bytes.Aggregate(new StringBuilder(bytes.Length 2), (sb, b) => sb.AppendFormat("{0:X2}", b)).ToString(); } static string ByteArrayToHexStringViaStringBuilderForEachAppendFormat(byte[] bytes) { StringBuilder hex = new StringBuilder(bytes.Length 2); foreach (byte b in bytes) hex.AppendFormat("{0:X2}", b); return hex.ToString(); } static string ByteArrayToHexViaByteManipulation(byte[] bytes) { char[] c = new char[bytes.Length 2]; byte b; for (int i = 0; i < bytes.Length; i++) { b = ((byte)(bytes[i] >> 4)); c[i 2] = (char)(b > 9 ? b + 0x37 : b + 0x30); b = ((byte)(bytes[i] & 0xF)); c[i 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30); } return new string(c); } static string ByteArrayToHexViaByteManipulation2(byte[] bytes) { char[] c = new char[bytes.Length 2]; int b; for (int i = 0; i < bytes.Length; i++) { b = bytes[i] >> 4; c[i 2] = (char)(55 + b + (((b - 10) >> 31) & -7)); b = bytes[i] & 0xF; c[i 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7)); } return new string(c); } static string ByteArrayToHexViaSoapHexBinary(byte[] bytes) { SoapHexBinary soapHexBinary = new SoapHexBinary(bytes); return soapHexBinary.ToString(); } static string ByteArrayToHexViaLookupAndShift(byte[] bytes) { StringBuilder result = new StringBuilder(bytes.Length 2); string hexAlphabet = "0123456789ABCDEF"; foreach (byte b in bytes) { result.Append(hexAlphabet[(int)(b >> 4)]); result.Append(hexAlphabet[(int)(b & 0xF)]); } return result.ToString(); } static readonly uint _lookup32UnsafeP = (uint)GCHandle.Alloc(_Lookup32, GCHandleType.Pinned).AddrOfPinnedObject(); 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; } static uint[] _Lookup32 = Enumerable.Range(0, 255).Select(i => { string s = i.ToString("X2"); return ((uint)s[0]) + ((uint)s[1] << 16); }).ToArray(); static string ByteArrayToHexViaLookupPerByte(byte[] bytes) { var result = new char[bytes.Length 2]; for (int i = 0; i < bytes.Length; i++) { var val = _Lookup32[bytes[i]]; result[2i] = (char)val; result[2i + 1] = (char) (val >> 16); } return new string(result); } static string ByteArrayToHexViaLookup(byte[] bytes) { string[] hexStringTable = new string[] { "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF", "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF", "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF", }; StringBuilder result = new StringBuilder(bytes.Length 2); foreach (byte b in bytes) { result.Append(hexStringTable[b]); } return result.ToString(); }

Aktualisierung (2010-01-13)

Antwort von Waleed zur Analyse hinzugefügt. Ziemlich schnell.

Aktualisierung (2011-10-05)

Hinzugefügt string.Concat Array.ConvertAll Variante der Vollständigkeit halber (erfordert .NET 4.0). Gleichauf mit string.Join Version.

Update (2012-02-05)

Das Testrepositorium enthält weitere Varianten wie StringBuilder.Append(b.ToString("X2")) . Keiner hat die Ergebnisse verfälscht. foreach ist schneller als {IEnumerable}.Aggregate zum Beispiel, aber BitConverter gewinnt trotzdem.

Aktualisierung (2012-04-03)

Mykroft's hinzugefügt SoapHexBinary Antwort auf die Analyse, die den dritten Platz belegte.

Update (2013-01-15)

Die Antwort von CodesInChaos auf die Byte-Manipulation wurde hinzugefügt, die den ersten Platz belegt hat (mit großem Abstand bei großen Textblöcken).

Update (2013-05-23)

Nathan Moinvaziris Nachschlagewerk und die Variante aus Brian Lamberts Blog wurden hinzugefügt. Beide ziemlich schnell, aber auf dem von mir verwendeten Testrechner (AMD Phenom 9750) nicht führend.

Update (2014-07-31)

Die neue bytebasierte Nachschlageantwort von @CodesInChaos wurde hinzugefügt. Sie scheint sowohl bei den Satztests als auch bei den Volltexttests die Führung übernommen zu haben.

Update (2015-08-20)

Hinzugefügt Luftatmer Optimierungen und unsafe Variante dazu Repo der Antwort . Wenn Sie im unsicheren Spiel mitspielen wollen, können Sie sowohl bei kurzen Zeichenfolgen als auch bei großen Texten enorme Leistungssteigerungen gegenüber den bisherigen Spitzenreitern erzielen.

0 Stimmen

Möchten Sie den Code aus der Antwort von Waleed testen? Er scheint sehr schnell zu sein. stackoverflow.com/questions/311165/

7 Stimmen

Obwohl ich Ihnen den Code zur Verfügung gestellt habe, um genau das zu tun, was Sie verlangt haben, habe ich den Testcode aktualisiert, um die Antwort von Waleed einzubeziehen. Abgesehen von aller Mürrischkeit ist es viel schneller.

0 Stimmen

Ich erhielt ein anderes Ergebnis, als ich "ByteArrayToHexStringViaBitConverter" und "ByteArrayToHexStringViaStringBuilder" verwendete. Der letztere erwies sich als "richtig". Gibt es einen Grund, warum das Ergebnis der beiden Funktionen unterschiedlich sein sollte?

261voto

Mykroft Punkte 12677

Es gibt eine Klasse namens SoapHexBinary die genau das tut, was Sie wollen.

using System.Runtime.Remoting.Metadata.W3cXsd2001;

public static byte[] GetStringToBytes(string value)
{
    SoapHexBinary shb = SoapHexBinary.Parse(value);
    return shb.Value;
}

public static string GetBytesToString(byte[] value)
{
    SoapHexBinary shb = new SoapHexBinary(value);
    return shb.ToString();
}

38 Stimmen

SoapHexBinary ist seit .NET 1.0 verfügbar und befindet sich in mscorlib. Trotz des komischen Namensraums tut es genau das, was die Frage verlangt.

4 Stimmen

Toller Fund! Beachten Sie, dass Sie ungerade Zeichenfolgen mit einer führenden 0 für GetStringToBytes auffüllen müssen, wie die andere Lösung.

0 Stimmen

Haben Sie den Umsetzungsgedanken gesehen? Die akzeptierte Antwort hat eine bessere IMHO.

157voto

CodesInChaos Punkte 103089

Beim Schreiben von Kryptocode ist es üblich, datenabhängige Verzweigungen und Tabellensuchen zu vermeiden, um sicherzustellen, dass die Laufzeit nicht von den Daten abhängt, da datenabhängiges Timing zu Seitenkanalangriffen führen kann.

Es ist auch ziemlich schnell.

static string ByteToHexBitFiddle(byte[] bytes)
{
    char[] c = new char[bytes.Length * 2];
    int b;
    for (int i = 0; i < bytes.Length; i++) {
        b = bytes[i] >> 4;
        c[i * 2] = (char)(55 + b + (((b-10)>>31)&-7));
        b = bytes[i] & 0xF;
        c[i * 2 + 1] = (char)(55 + b + (((b-10)>>31)&-7));
    }
    return new string(c);
}

Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn


Gebt alle Hoffnung auf, die ihr hier eintretet

Eine Erklärung für die seltsame Bit-Tüftelei:

  1. bytes[i] >> 4 extrahiert das hohe Nibble eines Bytes
    bytes[i] & 0xF extrahiert das untere Nibble eines Bytes
  2. b - 10
    でございます < 0 für Werte b < 10 , die zu einer Dezimalstelle wird
    でございます >= 0 für Werte b > 10 die zu einem Brief von A a F .
  3. Verwendung von i >> 31 auf eine vorzeichenbehaftete 32-Bit-Ganzzahl extrahiert das Vorzeichen, dank der Vorzeichenerweiterung. Es wird -1 para i < 0 y 0 para i >= 0 .
  4. Die Kombination von 2) und 3) zeigt, dass (b-10)>>31 wird sein 0 für Briefe und -1 für Ziffern.
  5. Betrachtet man den Fall der Buchstaben, so wird der letzte Summand 0 y b liegt im Bereich von 10 bis 15. Wir wollen ihn abbilden auf A (65) bis F (70), was die Addition von 55 ( 'A'-10 ).
  6. Im Fall der Ziffern wollen wir den letzten Summanden so anpassen, dass er b aus dem Bereich 0 bis 9 in den Bereich 0 (48) bis 9 (57). Das bedeutet, dass sie -7 werden muss ( '0' - 55 ).
    Jetzt könnten wir einfach mit 7 multiplizieren. Da aber -1 dadurch dargestellt wird, dass alle Bits 1 sind, können wir stattdessen verwenden & -7 seit (0 & -7) == 0 y (-1 & -7) == -7 .

Einige weitere Überlegungen:

  • Ich habe keine zweite Schleifenvariable verwendet, um den Index in c da die Messung zeigt, dass die Berechnung aus i ist billiger.
  • Mit genau i < bytes.Length als obere Schranke der Schleife ermöglicht es dem JITter, die Prüfung der Schranken auf bytes[i] Deshalb habe ich diese Variante gewählt.
  • Herstellung von b ein int ermöglicht unnötige Konvertierungen von und nach Byte.

11 Stimmen

Und hex string a byte[] array ?

17 Stimmen

+1 für die korrekte Angabe der Quelle, nachdem Sie dieses Stück schwarzer Magie beschworen haben. Lang lebe Cthulhu.

0 Stimmen

Beste Antwort (für den Hexadezimalcode-Teil der Frage)!

116voto

Will Dean Punkte 38365

Wenn Sie mehr Flexibilität wünschen als BitConverter , aber nicht diese klobigen expliziten Schleifen im Stil der 1990er Jahre wollen, dann können Sie das tun:

String.Join(String.Empty, Array.ConvertAll(bytes, x => x.ToString("X2")));

Oder, wenn Sie .NET 4.0 verwenden:

String.Concat(Array.ConvertAll(bytes, x => x.ToString("X2")));

(Letzteres aus einem Kommentar zum ursprünglichen Beitrag.)

22 Stimmen

Noch kürzer: String.Concat(Array.ConvertAll(bytes, x => x.ToString("X2"))

0 Stimmen

Nur ein Hinweis, dass die nette Technik von maxc .net 4.0 benötigt

16 Stimmen

Noch kürzer: String.Concat(bytes.Select(b => b.ToString("X2"))) [.NET4]

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