Wie kann man ein Byte-Array in eine hexadezimale Zeichenkette umwandeln und umgekehrt?
In der Zeile hex.AppendFormat("{0:x2}", b) fehlt ein Semikolon
Wie kann man ein Byte-Array in eine hexadezimale Zeichenkette umwandeln und umgekehrt?
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
.
Sie verwenden SubString. Weist diese Schleife nicht eine schreckliche Menge an String-Objekten zu?
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.
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.
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.
unsafe
(über CodesInChaos) (dem Testrepositorium hinzugefügt von Airbreather )
BitConverter
(über Tomalak)
{SoapHexBinary}.ToString
(über Mykroft)
{byte}.ToString("X2")
(mit foreach
) (abgeleitet von der Antwort von Will Dean)
{byte}.ToString("X2")
(mit {IEnumerable}.Aggregate
, erfordert System.Linq) (via Mark)
Array.ConvertAll
(mit string.Join
) (über Will Dean)
Array.ConvertAll
(mit string.Concat
(erfordert .NET 4.0) (über Will Dean)
{StringBuilder}.AppendFormat
(mit foreach
) (über Tomalak)
{StringBuilder}.AppendFormat
(mit {IEnumerable}.Aggregate
, erfordert System.Linq) (abgeleitet von Tomalaks Antwort)
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.
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.
Func<byte[], string>
) nach /Tests/ConvertByteArrayToHexString/Test.cs.TestCandidates
Rückgabewert in derselben Klasse.GenerateTestInput
in derselben Klasse.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(); }
Antwort von Waleed zur Analyse hinzugefügt. Ziemlich schnell.
Hinzugefügt string.Concat
Array.ConvertAll
Variante der Vollständigkeit halber (erfordert .NET 4.0). Gleichauf mit string.Join
Version.
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.
Mykroft's hinzugefügt SoapHexBinary
Antwort auf die Analyse, die den dritten Platz belegte.
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).
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.
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.
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.
Möchten Sie den Code aus der Antwort von Waleed testen? Er scheint sehr schnell zu sein. stackoverflow.com/questions/311165/
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.
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?
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();
}
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.
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.
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:
bytes[i] >> 4
extrahiert das hohe Nibble eines Bytesbytes[i] & 0xF
extrahiert das untere Nibble eines Bytesb - 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
.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
.(b-10)>>31
wird sein 0
für Briefe und -1
für Ziffern.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
).b
aus dem Bereich 0 bis 9 in den Bereich 0
(48) bis 9
(57). Das bedeutet, dass sie -7 werden muss ( '0' - 55
).& -7
seit (0 & -7) == 0
y (-1 & -7) == -7
.Einige weitere Überlegungen:
c
da die Messung zeigt, dass die Berechnung aus i
ist billiger.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.b
ein int ermöglicht unnötige Konvertierungen von und nach Byte.
+1 für die korrekte Angabe der Quelle, nachdem Sie dieses Stück schwarzer Magie beschworen haben. Lang lebe Cthulhu.
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.)
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.
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