Die unten akzeptierte Antwort scheint eine schreckliche Anzahl von Zeichenfolgen in der Zeichenfolge für die Bytekonvertierung zuzuweisen. Ich frage mich, wie sich dies auf die Leistung auswirkt
Wim Coenen
9
Die SoapHexBinary-Klasse macht genau das, was Sie wollen, denke ich.
Mykroft
Es scheint mir, dass das Stellen von 2 Fragen in einem Beitrag nicht ganz Standard ist.
SandRock
Antworten:
1353
Entweder:
publicstaticstringByteArrayToString(byte[] ba){StringBuilder hex =newStringBuilder(ba.Length*2);foreach(byte b in ba)
hex.AppendFormat("{0:x2}", b);return hex.ToString();}
Die umgekehrte Konvertierung würde folgendermaßen aussehen:
publicstaticbyte[]StringToByteArray(String hex){intNumberChars= hex.Length;byte[] bytes =newbyte[NumberChars/2];for(int i =0; i <NumberChars; i +=2)
bytes[i /2]=Convert.ToByte(hex.Substring(i,2),16);return bytes;}
Die Verwendung Substringist die beste Option in Kombination mit Convert.ToByte. Weitere Informationen finden Sie in dieser Antwort . Wenn Sie eine bessere Leistung benötigen, müssen Sie diese vermeiden, Convert.ToBytebevor Sie sie fallen lassen können SubString.
Sie verwenden SubString. Weist diese Schleife nicht eine schreckliche Menge von Zeichenfolgenobjekten zu?
Wim Coenen
30
Ehrlich gesagt - bis die Leistung dramatisch beeinträchtigt wird, würde ich dies eher ignorieren und darauf vertrauen, dass Runtime und GC sich darum kümmern.
Tomalak
87
Da ein Byte aus zwei Halbbytes besteht, muss jede Hex-Zeichenfolge, die ein Byte-Array gültig darstellt, eine gerade Zeichenanzahl haben. Eine 0 sollte nirgendwo hinzugefügt werden. Wenn Sie eine 0 hinzufügen, wird davon ausgegangen, dass ungültige Daten möglicherweise gefährlich sind. Wenn überhaupt, sollte die StringToByteArray-Methode eine FormatException auslösen, wenn die Hex-Zeichenfolge eine ungerade Anzahl von Zeichen enthält.
David Boike
7
@ 00jt Sie müssen davon ausgehen, dass F == 0F ist. Entweder ist es dasselbe wie 0F, oder die Eingabe wurde abgeschnitten, und F ist tatsächlich der Anfang von etwas, das Sie nicht erhalten haben. Es liegt an Ihrem Kontext, diese Annahmen zu treffen, aber ich glaube, eine Allzweckfunktion sollte ungerade Zeichen als ungültig ablehnen, anstatt diese Annahme für den aufrufenden Code zu treffen.
David Boike
11
@DavidBoike Die Frage hatte NICHTS mit "wie man mit möglicherweise abgeschnittenen Stream-Werten umgeht" zu tun. Es geht um einen String. String myValue = 10.ToString ("X"); myValue ist "A", nicht "0A". Lesen Sie diesen String jetzt wieder in Bytes. Hoppla, Sie haben ihn gebrochen.
30.
488
Performance-Analyse
Hinweis: Neuer Leiter ab dem 20.08.2015.
Ich habe jede der verschiedenen Konvertierungsmethoden durch einige grobe StopwatchLeistungstests, einen Lauf mit einem zufälligen Satz (n = 61, 1000 Iterationen) und einen Lauf mit einem Projekt-Gutenburg-Text (n = 1.238.957, 150 Iterationen) geführt. Hier sind die Ergebnisse, ungefähr vom schnellsten zum langsamsten. Alle Messungen erfolgen in Ticks ( 10.000 Ticks = 1 ms ) und alle relativen Noten werden mit der [langsamsten] StringBuilderImplementierung verglichen . Informationen zum verwendeten Code finden Sie unten oder im Testframework-Repo, in dem ich jetzt den Code zum Ausführen dieses Codes verwalte.
Haftungsausschluss
WARNUNG: Verlassen Sie sich bei nichts Konkretem auf diese Statistiken. Sie sind einfach ein Beispiellauf von Beispieldaten. Wenn Sie wirklich erstklassige Leistung benötigen, testen Sie diese Methoden in einer Umgebung, die für Ihre Produktionsanforderungen repräsentativ ist, mit Daten, die für Ihre Verwendung repräsentativ sind.
Nachschlagetabellen haben die Führung bei der Bytemanipulation übernommen. Grundsätzlich gibt es eine Form der Vorberechnung, was ein bestimmtes Halbbyte oder Byte in hexadezimaler Form sein wird. Wenn Sie dann die Daten durchgehen, schauen Sie einfach im nächsten Abschnitt nach, um zu sehen, um welche Hex-Zeichenfolge es sich handelt. Dieser Wert wird dann auf irgendeine Weise zur resultierenden Zeichenfolgenausgabe hinzugefügt. Lange Zeit war die Byte-Manipulation, die von einigen Entwicklern möglicherweise schwerer zu lesen war, der Ansatz mit der besten Leistung.
Am besten finden Sie immer noch einige repräsentative Daten und probieren sie in einer produktionsähnlichen Umgebung aus. Wenn Sie unterschiedliche Speicherbeschränkungen haben, bevorzugen Sie möglicherweise eine Methode mit weniger Zuordnungen gegenüber einer Methode, die schneller ist, aber mehr Speicher verbraucht.
Code testen
Fühlen Sie sich frei, mit dem Testcode zu spielen, den ich verwendet habe. Eine Version ist hier enthalten, aber Sie können das Repo klonen und Ihre eigenen Methoden hinzufügen. Bitte senden Sie eine Pull-Anfrage, wenn Sie etwas Interessantes finden oder zur Verbesserung des verwendeten Test-Frameworks beitragen möchten.
Fügen Sie die neue statische Methode ( Func<byte[], string>) zu /Tests/ConvertByteArrayToHexString/Test.cs hinzu.
Fügen Sie den Namen dieser Methode zum TestCandidatesRückgabewert in derselben Klasse hinzu.
Stellen Sie sicher, dass Sie die gewünschte Eingabeversion (Satz oder Text) ausführen, indem Sie die Kommentare in GenerateTestInputderselben Klasse umschalten .
Drücken Sie F5und warten Sie auf die Ausgabe (ein HTML-Speicherauszug wird auch im Ordner / bin generiert).
staticstringByteArrayToHexStringViaStringJoinArrayConvertAll(byte[] bytes){returnstring.Join(string.Empty,Array.ConvertAll(bytes, b => b.ToString("X2")));}staticstringByteArrayToHexStringViaStringConcatArrayConvertAll(byte[] bytes){returnstring.Concat(Array.ConvertAll(bytes, b => b.ToString("X2")));}staticstringByteArrayToHexStringViaBitConverter(byte[] bytes){string hex =BitConverter.ToString(bytes);return hex.Replace("-","");}staticstringByteArrayToHexStringViaStringBuilderAggregateByteToString(byte[] bytes){return bytes.Aggregate(newStringBuilder(bytes.Length*2),(sb, b)=> sb.Append(b.ToString("X2"))).ToString();}staticstringByteArrayToHexStringViaStringBuilderForEachByteToString(byte[] bytes){StringBuilder hex =newStringBuilder(bytes.Length*2);foreach(byte b in bytes)
hex.Append(b.ToString("X2"));return hex.ToString();}staticstringByteArrayToHexStringViaStringBuilderAggregateAppendFormat(byte[] bytes){return bytes.Aggregate(newStringBuilder(bytes.Length*2),(sb, b)=> sb.AppendFormat("{0:X2}", b)).ToString();}staticstringByteArrayToHexStringViaStringBuilderForEachAppendFormat(byte[] bytes){StringBuilder hex =newStringBuilder(bytes.Length*2);foreach(byte b in bytes)
hex.AppendFormat("{0:X2}", b);return hex.ToString();}staticstringByteArrayToHexViaByteManipulation(byte[] bytes){char[] c =newchar[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);}returnnewstring(c);}staticstringByteArrayToHexViaByteManipulation2(byte[] bytes){char[] c =newchar[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));}returnnewstring(c);}staticstringByteArrayToHexViaSoapHexBinary(byte[] bytes){SoapHexBinary soapHexBinary =newSoapHexBinary(bytes);return soapHexBinary.ToString();}staticstringByteArrayToHexViaLookupAndShift(byte[] bytes){StringBuilder result =newStringBuilder(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();}staticreadonlyuint* _lookup32UnsafeP =(uint*)GCHandle.Alloc(_Lookup32,GCHandleType.Pinned).AddrOfPinnedObject();staticstringByteArrayToHexViaLookup32UnsafeDirect(byte[] bytes){var lookupP = _lookup32UnsafeP;var result =newstring((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;}staticuint[]_Lookup32=Enumerable.Range(0,255).Select(i =>{string s = i.ToString("X2");return((uint)s[0])+((uint)s[1]<<16);}).ToArray();staticstringByteArrayToHexViaLookupPerByte(byte[] bytes){var result =newchar[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);}returnnewstring(result);}staticstringByteArrayToHexViaLookup(byte[] bytes){string[] hexStringTable =newstring[]{"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 =newStringBuilder(bytes.Length*2);foreach(byte b in bytes){
result.Append(hexStringTable[b]);}return result.ToString();}
Update (13.01.2010)
Waleeds Antwort zur Analyse hinzugefügt. Ziemlich schnell.
Update (05.10.2011)
Der string.ConcatArray.ConvertAllVollständigkeit halber wurde eine Variante hinzugefügt (erfordert .NET 4.0). Auf Augenhöhe mit der string.JoinVersion.
Update (05.02.2012)
Test Repo enthält weitere Varianten wie StringBuilder.Append(b.ToString("X2")). Keiner hat die Ergebnisse gestört. foreachist schneller als {IEnumerable}.Aggregatezum Beispiel, BitConvertergewinnt aber trotzdem.
Update (03.04.2012)
Mykrofts SoapHexBinaryAntwort zur Analyse hinzugefügt , die den dritten Platz einnahm.
Update (15.01.2013)
Die Byte-Manipulationsantwort von CodesInChaos wurde hinzugefügt, die den ersten Platz einnahm (mit großem Abstand bei großen Textblöcken).
Update (23.05.2013)
Nathan Moinvaziris Lookup-Antwort und die Variante aus Brian Lamberts Blog wurden hinzugefügt. Beides ziemlich schnell, aber nicht die Führung auf der von mir verwendeten Testmaschine (AMD Phenom 9750).
Update (31.07.2014)
Die neue bytebasierte Suchantwort von @ CodesInChaos wurde hinzugefügt. Es scheint sowohl bei den Satztests als auch bei den Volltexttests die Führung übernommen zu haben.
Update (20.08.2015)
Hinzugefügt airbreather die Optimierungen und unsafeVariante dieser Repo - Antwort . Wenn Sie in dem unsicheren Spiel spielen möchten, können Sie sowohl bei kurzen Saiten als auch bei großen Texten enorme Leistungssteigerungen gegenüber den vorherigen Top-Gewinnern erzielen.
Obwohl Sie den Code zur Verfügung gestellt haben, um genau das zu tun, was Sie selbst angefordert haben, habe ich den Testcode so aktualisiert, dass er die Antwort von Waleed enthält. Abgesehen von allem Grummeln ist es viel schneller.
Patridge
2
@CodesInChaos Fertig. Und es hat in meinen Tests auch einiges gewonnen. Ich gebe noch nicht vor, eine der Top-Methoden vollständig zu verstehen, aber sie sind leicht vor direkter Interaktion zu verbergen.
Patridge
6
Diese Antwort hat nicht die Absicht, die Frage zu beantworten, was "natürlich" oder alltäglich ist. Das Ziel ist es, den Menschen einige grundlegende Leistungsmaßstäbe zu geben, da Sie diese Konvertierung häufig durchführen, wenn Sie sie durchführen müssen. Wenn jemand rohe Geschwindigkeit benötigt, führt er die Benchmarks einfach mit geeigneten Testdaten in der gewünschten Computerumgebung aus. Stecken Sie diese Methode dann in eine Erweiterungsmethode, in der Sie die Implementierung nie wieder sehen (z bytes.ToHexStringAtLudicrousSpeed(). B. ).
Patridge
2
Ich habe gerade eine auf Hochleistungstabellen basierende Implementierung erstellt. Die sichere Variante ist ungefähr 30% schneller als der aktuelle Marktführer auf meiner CPU. Die unsicheren Varianten sind noch schneller. stackoverflow.com/a/24343727/445517
CodesInChaos
244
Es gibt eine Klasse namens SoapHexBinary , die genau das tut, was Sie wollen.
using System.Runtime.Remoting.Metadata.W3cXsd2001;publicstaticbyte[]GetStringToBytes(stringvalue){SoapHexBinary shb =SoapHexBinary.Parse(value);return shb.Value;}publicstaticstringGetBytesToString(byte[]value){SoapHexBinary shb =newSoapHexBinary(value);return shb.ToString();}
SoapHexBinary wird in .NET Core / .NET Standard nicht unterstützt ...
11.
141
Beim Schreiben von Kryptocode werden häufig datenabhängige Verzweigungen und Tabellensuchen vermieden, 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.
staticstringByteToHexBitFiddle(byte[] bytes){char[] c =newchar[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));}returnnewstring(c);}
Gib alle Hoffnung auf, ihr, die ihr hier eintretet
Eine Erklärung für das seltsame Geigen:
bytes[i] >> 4extrahiert das hohe Halbbyte eines Bytes bytes[i] & 0xFextrahiert das niedrige Halbbyte eines Bytes
b - 10
ist < 0für Werte b < 10, die zu einer Dezimalstelle werden,
ist >= 0für Werte b > 10, die zu einem Buchstaben von Abis werden F.
Durch die Verwendung i >> 31einer vorzeichenbehafteten 32-Bit-Ganzzahl wird das Vorzeichen dank der Vorzeichenerweiterung extrahiert. Es wird -1für i < 0und 0für sein i >= 0.
Die Kombination von 2) und 3) zeigt, dass (b-10)>>31dies 0für Buchstaben und -1Ziffern gilt.
Wenn man den Fall für Buchstaben betrachtet, wird der letzte Summand 0und bliegt im Bereich von 10 bis 15. Wir möchten ihn A(65) bis F(70) zuordnen, was das Hinzufügen von 55 ( 'A'-10) impliziert .
Wenn wir uns den Fall für Ziffern ansehen, möchten wir den letzten Summanden so anpassen, dass er bvom Bereich 0 bis 9 dem Bereich 0(48) bis 9(57) zugeordnet wird. Dies bedeutet, dass es -7 ( '0' - 55) werden muss.
Jetzt könnten wir einfach mit 7 multiplizieren. Da -1 jedoch durch alle Bits 1 dargestellt wird, können wir stattdessen & -7da (0 & -7) == 0und verwenden (-1 & -7) == -7.
Einige weitere Überlegungen:
Ich habe keine zweite Schleifenvariable zum Indizieren verwendet c, da die Messung zeigt, dass die Berechnung ibilliger ist.
Die Verwendung genau i < bytes.Lengthder oberen Grenze der Schleife ermöglicht es dem JITter, Grenzüberprüfungen zu eliminieren bytes[i], daher habe ich diese Variante gewählt.
Das Erstellen beines Int ermöglicht unnötige Konvertierungen von und zu Byte.
+1 für das korrekte Zitieren Ihrer Quelle nach dem Aufrufen dieses Stücks schwarzer Magie. Alle begrüßen Cthulhu.
Edward
4
Was ist mit String zu Byte []?
Syaiful Nizam Yahya
9
Nett! Für diejenigen, die eine Ausgabe in Kleinbuchstaben benötigen, ändert sich der Ausdruck offensichtlich in87 + b + (((b-10)>>31)&-39)
eXavier
2
@AaA Sie sagten " byte[] array", was wörtlich ein Array von Byte-Arrays bedeutet, oder byte[][]. Ich habe mich nur lustig gemacht.
CoolOppo
97
Wenn Sie mehr Flexibilität als möchten BitConverter, aber nicht diese klobigen expliziten Schleifen im Stil der 90er Jahre möchten, können Sie Folgendes 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 Originalbeitrag.)
Noch kürzer: String.Concat (Array.ConvertAll (Bytes, x => x.ToString ("X2"))
Nestor
14
Noch kürzer: String.Concat (bytes.Select (b => b.ToString ("X2"))) [.NET4]
Allon Guralnek
14
Beantwortet nur die halbe Frage.
Sly Gryphon
1
Warum braucht der zweite .Net 4? String.Concat befindet sich in .Net 2.0.
Polyfun
2
Diese "90er Jahre" -Schleifen sind im Allgemeinen schneller, aber so vernachlässigbar, dass sie in den meisten Kontexten keine Rolle spielen. Immer noch erwähnenswert
Austin_Anderson
69
Ein weiterer Ansatz, der auf Nachschlagetabellen basiert. Dieser verwendet nur eine Nachschlagetabelle für jedes Byte anstelle einer Nachschlagetabelle pro Nibble.
privatestaticreadonlyuint[] _lookup32 =CreateLookup32();privatestaticuint[]CreateLookup32(){var result =newuint[256];for(int i =0; i <256; i++){string s=i.ToString("X2");
result[i]=((uint)s[0])+((uint)s[1]<<16);}return result;}privatestaticstringByteArrayToHexViaLookup32(byte[] bytes){var lookup32 = _lookup32;var result =newchar[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);}returnnewstring(result);}
Ich habe auch Varianten davon mit getestet ushort von , struct{char X1, X2}, struct{byte X1, X2}in der Lookup - Tabelle.
Abhängig vom Kompilierungsziel (x86, X64) hatten diese entweder ungefähr die gleiche Leistung oder waren etwas langsamer als diese Variante.
Und für noch höhere Leistung ist es unsafe Geschwister:
privatestaticreadonlyuint[] _lookup32Unsafe =CreateLookup32Unsafe();privatestaticreadonlyuint* _lookup32UnsafeP =(uint*)GCHandle.Alloc(_lookup32Unsafe,GCHandleType.Pinned).AddrOfPinnedObject();privatestaticuint[]CreateLookup32Unsafe(){var result =newuint[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;}publicstaticstringByteArrayToHexViaLookup32Unsafe(byte[] bytes){var lookupP = _lookup32UnsafeP;var result =newchar[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]];}}returnnewstring(result);}
Oder wenn Sie es für akzeptabel halten, direkt in die Zeichenfolge zu schreiben:
publicstaticstringByteArrayToHexViaLookup32UnsafeDirect(byte[] bytes){var lookupP = _lookup32UnsafeP;var result =newstring((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;}
Warum tauscht das Erstellen der Nachschlagetabelle in der unsicheren Version die Halbbytes des vorberechneten Bytes aus? Ich dachte, Endianness änderte nur die Reihenfolge von Entitäten, die aus mehreren Bytes bestehen.
Raif Atef
@ RaifAtef Was hier zählt, ist nicht die Reihenfolge der Knabbereien. Aber die Reihenfolge von 16-Bit-Wörtern in einer 32-Bit-Ganzzahl. Aber ich denke darüber nach, es neu zu schreiben, damit derselbe Code unabhängig von der Endianness ausgeführt werden kann.
CodesInChaos
Beim erneuten Lesen des Codes denke ich, dass Sie dies getan haben, da die Laufzeit / CPU die Bytes umdreht, wenn Sie das Zeichen * später einem Uint * zuweisen und es zuweisen (beim Generieren des Hex-Zeichens) (da Uint nicht behandelt wird) Entspricht 2 separaten 16-Bit-Zeichen, sodass Sie sie zur Kompensation vorab umdrehen. Habe ich recht ? Endianness ist verwirrend :-).
Raif Atef
4
Dies beantwortet nur die Hälfte der Frage ... Wie wäre es mit einer Hex-Zeichenfolge zu Bytes?
Narvalex
3
@CodesInChaos Ich frage mich, ob Spanjetzt anstelle von verwendet werden kann unsafe?
Konrad
64
Sie können die BitConverter.ToString-Methode verwenden:
Ich bin heute auf das gleiche Problem gestoßen und bin auf diesen Code gestoßen:
privatestaticstringByteArrayToHex(byte[] barray){char[] c =newchar[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);}returnnewstring(c);}
Quelle: Forum post byte [] Array zu Hex String (siehe den Beitrag von PZahra). Ich habe den Code ein wenig geändert, um das 0x-Präfix zu entfernen.
Ich habe einige Leistungstests für den Code durchgeführt und er war fast achtmal schneller als die Verwendung von BitConverter.ToString () (laut Patridges Beitrag der schnellste).
ganz zu schweigen davon, dass dies den geringsten Speicher benötigt. Es wurden keinerlei Zwischenzeichenfolgen erstellt.
Chochos
8
Beantwortet nur die halbe Frage.
Sly Gryphon
Dies ist großartig, da es grundsätzlich auf jeder Version von NET funktioniert, einschließlich NETMF. Ein Gewinner!
Jonesome Reinstate Monica
1
Die akzeptierte Antwort enthält zwei hervorragende HexToByteArray-Methoden, die die andere Hälfte der Frage darstellen. Die Lösung von Waleed beantwortet die laufende Frage, wie dies zu tun ist, ohne dabei eine große Anzahl von Zeichenfolgen zu erstellen.
Brendten Eickstaedt
Kopiert eine neue Zeichenfolge (c) und weist sie neu zu, oder ist sie intelligent genug, um zu wissen, wann sie das Zeichen [] einfach umbrechen kann?
Ich mache den Fall, dass diese Bearbeitung falsch ist, und erkläre, warum sie zurückgesetzt werden könnte. Unterwegs lernen Sie vielleicht ein oder zwei Dinge über einige Interna und sehen ein weiteres Beispiel dafür, was vorzeitige Optimierung wirklich ist und wie sie Sie beißen kann.
tl; dr: Verwenden Sie einfach Convert.ToByteund String.Substringwenn Sie es eilig haben ("Originalcode" unten), ist es die beste Kombination, wenn Sie nicht erneut implementieren möchten Convert.ToByte. Verwenden Sie etwas Fortgeschritteneres (siehe andere Antworten), das nicht verwendet wird, Convert.ToBytewenn Sie Leistung benötigen . Sie nicht verwenden , etwas anderes als String.Substringin Kombination mitConvert.ToByte , es sei denn , jemand etwas interessantes dieses in den Kommentaren dieser Antwort zu sagen hat.
Warnung: Diese Antwort kann veraltet sein, wenn aConvert.ToByte(char[], Int32) Überlastung im Framework implementiert ist. Dies wird wahrscheinlich nicht bald passieren.
In der Regel sage ich nicht gerne "Nicht vorzeitig optimieren", weil niemand weiß, wann "vorzeitig" ist. Das einzige, was Sie berücksichtigen müssen, wenn Sie entscheiden, ob Sie optimieren möchten oder nicht, ist: "Habe ich die Zeit und die Ressourcen, um Optimierungsansätze richtig zu untersuchen?". Wenn Sie das nicht tun, dann ist es zu früh, warten Sie, bis Ihr Projekt wird reifen , oder bis Sie die Leistung benötigen (wenn es einen echten Bedarf ist, dann werden Sie machen die Zeit). Machen Sie in der Zwischenzeit das Einfachste, was stattdessen funktionieren könnte.
Originalcode:
publicstaticbyte[]HexadecimalStringToByteArray_Original(string input){var outputLength = input.Length/2;var output =newbyte[outputLength];for(var i =0; i < outputLength; i++)
output[i]=Convert.ToByte(input.Substring(i *2,2),16);return output;}
Revision 4:
publicstaticbyte[]HexadecimalStringToByteArray_Rev4(string input){var outputLength = input.Length/2;var output =newbyte[outputLength];
using (var sr =newStringReader(input)){for(var i =0; i < outputLength; i++)
output[i]=Convert.ToByte(newstring(newchar[2]{(char)sr.Read(),(char)sr.Read()}),16);}return output;}
Die Revision vermeidet String.Substringund verwendet aStringReader stattdessen ein. Der angegebene Grund ist:
Bearbeiten: Sie können die Leistung für lange Zeichenfolgen verbessern, indem Sie einen Single-Pass-Parser verwenden, wie folgt:
Schauen Sie sich den Referenzcode für anString.Substring , ist es eindeutig schon "Single-Pass". und warum sollte es nicht sein? Es arbeitet auf Byte-Ebene, nicht auf Ersatzpaaren.
Es wird jedoch eine neue Zeichenfolge zugewiesen, aber dann müssen Sie eine Zeichenfolge zuweisen, an die Sie Convert.ToBytetrotzdem übergeben möchten. Darüber hinaus weist die in der Revision bereitgestellte Lösung jeder Iteration ein weiteres Objekt zu (das Zwei-Zeichen-Array). Sie können diese Zuordnung sicher außerhalb der Schleife platzieren und das Array wiederverwenden, um dies zu vermeiden.
publicstaticbyte[]HexadecimalStringToByteArray(string input){var outputLength = input.Length/2;var output =newbyte[outputLength];var numeral =newchar[2];
using (var sr =newStringReader(input)){for(var i =0; i < outputLength; i++){
numeral[0]=(char)sr.Read();
numeral[1]=(char)sr.Read();
output[i]=Convert.ToByte(newstring(numeral),16);}}return output;}
Jede Hexadezimalzahl numeralrepräsentiert ein einzelnes Oktett mit zwei Ziffern (Symbolen).
Aber warum dann StringReader.Readzweimal anrufen ? Rufen Sie einfach die zweite Überladung auf und bitten Sie sie, zwei Zeichen im Array mit zwei Zeichen gleichzeitig zu lesen. und reduzieren Sie die Anzahl der Anrufe um zwei.
publicstaticbyte[]HexadecimalStringToByteArray(string input){var outputLength = input.Length/2;var output =newbyte[outputLength];var numeral =newchar[2];
using (var sr =newStringReader(input)){for(var i =0; i < outputLength; i++){var read = sr.Read(numeral,0,2);Debug.Assert(read ==2);
output[i]=Convert.ToByte(newstring(numeral),16);}}return output;}
Was Ihnen bleibt, ist ein String-Reader, dessen einziger zusätzlicher "Wert" ein paralleler Index (intern _pos) ist, den Sie selbst hätten deklarieren können (wie jzum Beispiel), eine redundante Längenvariable (intern _length) und eine redundante Referenz auf die Eingabe Zeichenfolge (intern_s ). Mit anderen Worten, es ist nutzlos.
Wenn Sie sich fragen, wie Read"liest", schauen Sie sich einfach den Code an . Alles, was er tut, ist ein AufrufString.CopyTo der Eingabezeichenfolge. Der Rest ist nur Buchhaltungsaufwand, um Werte zu erhalten, die wir nicht benötigen.
Entfernen Sie also den String-Reader bereits und rufen Sie sich CopyToselbst an. Es ist einfacher, klarer und effizienter.
Benötigen Sie wirklich einen jIndex, der in Schritten von zwei parallel zu erhöht wird i? Natürlich nicht, multiplizieren Sie einfach imit zwei (was der Compiler in der Lage sein sollte, eine Addition zu optimieren).
publicstaticbyte[]HexadecimalStringToByteArray_BestEffort(string input){var outputLength = input.Length/2;var output =newbyte[outputLength];var numeral =newchar[2];for(int i =0; i < outputLength; i++){
input.CopyTo(i *2, numeral,0,2);
output[i]=Convert.ToByte(newstring(numeral),16);}return output;}
Wie sieht die Lösung jetzt aus? Genau wie es am Anfang war, nur anstelle der Verwendung String.Substringder Zeichenfolge und kopieren Sie die Daten , um es zu verteilen, sind Sie ein Zwischen Array , zu dem Sie die hexadezimalen Ziffern zu kopieren, dann verteilen Sie die Zeichenfolge selbst und kopieren Sie die Daten wieder aus das Array und in die Zeichenfolge (wenn Sie es im Zeichenfolgenkonstruktor übergeben). Die zweite Kopie wird möglicherweise optimiert, wenn sich die Zeichenfolge bereits im internen Pool befindet, kann sie dann String.Substringaber auch in diesen Fällen vermeiden.
Wenn Sie sich das noch String.Substringeinmal ansehen , sehen Sie, dass es einige interne Kenntnisse auf niedriger Ebene darüber verwendet, wie Zeichenfolgen erstellt werden, um die Zeichenfolge schneller zuzuweisen, als Sie es normalerweise tun könnten, und dass es denselben Code enthält, der CopyTodirekt dort verwendet wird, um dies zu vermeiden der Anruf Overhead.
String.Substring
Worst-Case: Eine schnelle Zuordnung, eine schnelle Kopie.
Bester Fall: Keine Zuordnung, keine Kopie.
Manuelle Methode
Worst-Case: Zwei normale Zuordnungen, eine normale Kopie, eine schnelle Kopie.
Bester Fall: Eine normale Zuordnung, eine normale Kopie.
Fazit? Wenn Sie verwenden möchtenConvert.ToByte(String, Int32) (weil Sie diese Funktionalität nicht selbst erneut implementieren möchten), scheint es keinen Weg zu geben, sie zu schlagenString.Substring . Alles, was Sie tun, ist im Kreis zu laufen und das Rad neu zu erfinden (nur mit nicht optimalen Materialien).
Beachten Sie, dass mit Convert.ToByteundString.Substring eine absolut gültige Wahl ist, wenn Sie keine extreme Leistung benötigen. Denken Sie daran: Entscheiden Sie sich nur für eine Alternative, wenn Sie Zeit und Ressourcen haben, um zu untersuchen, wie sie ordnungsgemäß funktioniert.
Wenn es eine Convert.ToByte(char[], Int32)gäbe, wären die Dinge natürlich anders (es wäre möglich, das zu tun, was ich oben beschrieben habe, und es vollständig zu vermeiden String).
Ich vermute, dass Leute, die eine bessere Leistung durch "Vermeiden String.Substring" melden, auch vermeiden Convert.ToByte(String, Int32), was Sie wirklich tun sollten, wenn Sie die Leistung trotzdem brauchen. Schauen Sie sich die unzähligen anderen Antworten an, um die verschiedenen Ansätze dafür zu entdecken.
Haftungsausschluss: Ich habe die neueste Version des Frameworks nicht dekompiliert, um zu überprüfen, ob die Referenzquelle auf dem neuesten Stand ist.
Jetzt klingt alles gut und logisch, hoffentlich sogar offensichtlich, wenn Sie es geschafft haben, so weit zu kommen. Aber ist es wahr?
Intel(R)Core(TM) i7-3720QM CPU @2.60GHzCores:8CurrentClockSpeed:2600MaxClockSpeed:2600--------------------Parsing hexadecimal stringinto an array of bytes
--------------------HexadecimalStringToByteArray_Original:7,777.09 average ticks (over 10000 runs),1.2XHexadecimalStringToByteArray_BestEffort:8,550.82 average ticks (over 10000 runs),1.1XHexadecimalStringToByteArray_Rev4:9,218.03 average ticks (over 10000 runs),1.0X
Ja!
Requisiten an Partridge für das Bench Framework, es ist einfach zu hacken. Die verwendete Eingabe ist der folgende SHA-1-Hash, der 5000 Mal wiederholt wird, um eine 100.000 Byte lange Zeichenfolge zu erstellen.
Fehler: {"Konnte keine erkennbaren Ziffern finden."}
Priya Jagtap
17
Ergänzung zur Antwort durch @CodesInChaos (umgekehrte Methode)
publicstaticbyte[]HexToByteUsingByteManipulation(string s){byte[] bytes =newbyte[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 das gleiche 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(das liegt an 0xffffffff & 7).
Für 'A' .. 'F' ist es hi = ch - 65 + 10;(das liegt an0x00000000 & 7 ).
Für 'a' .. 'f' müssen wir große Zahlen haben, also müssen wir 32 von der Standardversion subtrahieren, indem wir einige Bits 0mit verwenden & 0x0f.
65 ist Code für 'A'
48 ist Code für '0'
7 ist die Anzahl der Buchstaben zwischen '9'und 'A'in der ASCII-Tabelle ( ...456789:;<=>?@ABCD...).
Dieses Problem könnte auch mithilfe einer Nachschlagetabelle gelöst werden. Dies würde eine kleine Menge statischen Speichers sowohl für den Codierer als auch für den Decodierer erfordern. Diese Methode wird jedoch schnell sein:
Encodertabelle 512 Bytes oder 1024 Bytes (doppelt so groß, wenn sowohl Groß- als auch Kleinbuchstaben benötigt werden)
Decodertabelle 256 Bytes oder 64 KiB (entweder Single-Char-Lookup oder Dual-Char-Lookup)
Meine Lösung verwendet 1024 Bytes für die Codierungstabelle und 256 Bytes für die Decodierung.
Dekodierung
privatestaticreadonlybyte[]LookupTable=newbyte[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};privatestaticbyteLookup(char c){var b =LookupTable[c];if(b ==255)thrownewIOException("Expected a hex character, got "+ c);return b;}publicstaticbyteToByte(char[] chars,int offset){return(byte)(Lookup(chars[offset])<<4|Lookup(chars[offset +1]));}
Codierung
privatestaticreadonlychar[][]LookupTableUpper;privatestaticreadonlychar[][]LookupTableLower;staticHex(){LookupTableLower=newchar[256][];LookupTableUpper=newchar[256][];for(var i =0; i <256; i++){LookupTableLower[i]= i.ToString("x2").ToCharArray();LookupTableUpper[i]= i.ToString("X2").ToCharArray();}}publicstaticchar[]ToCharLower(byte[] b,int bOffset){returnLookupTableLower[b[bOffset]];}publicstaticchar[]ToCharUpper(byte[] b,int bOffset){returnLookupTableUpper[b[bOffset]];}
Während der Dekodierung können IOException und IndexOutOfRangeException auftreten (wenn ein Zeichen einen zu hohen Wert> 256 hat). Methoden zum De- / Codieren von Streams oder Arrays sollten implementiert werden, dies ist nur ein Proof of Concept.
Die Speichernutzung von 256 Bytes ist vernachlässigbar, wenn Sie Code auf der CLR ausführen.
Dolmen
9
Dies ist ein großartiger Beitrag. Ich mag Waleeds Lösung. Ich habe Patridges Test nicht durchlaufen, aber es scheint ziemlich schnell zu sein. Ich brauchte auch den umgekehrten Prozess, bei dem eine Hex-Zeichenfolge in ein Byte-Array konvertiert wurde, also schrieb ich sie als Umkehrung von Waleeds Lösung. Ich bin mir nicht sicher, ob es schneller ist als die ursprüngliche Lösung von Tomalak. Auch hier habe ich den umgekehrten Prozess nicht durch Patridges Test ausgeführt.
privatebyte[]HexStringToByteArray(string hexString){int hexStringLength = hexString.Length;byte[] b =newbyte[hexStringLength /2];for(int i =0; i < hexStringLength; i +=2){int topChar =(hexString[i]>0x40? hexString[i]-0x37: hexString[i]-0x30)<<4;int bottomChar = hexString[i +1]>0x40? hexString[i +1]-0x37: hexString[i +1]-0x30;
b[i /2]=Convert.ToByte(topChar + bottomChar);}return b;}
Dieser Code geht davon aus, dass die Hex-Zeichenfolge Alpha-Zeichen in Großbuchstaben verwendet, und explodiert, wenn die Hex-Zeichenfolge Alpha in Kleinbuchstaben verwendet. Möglicherweise möchten Sie aus Sicherheitsgründen eine Konvertierung in Großbuchstaben für die Eingabezeichenfolge durchführen.
Marc Novakowski
Das ist eine kluge Beobachtung, Marc. Der Code wurde geschrieben, um Waleeds Lösung umzukehren. Der ToUpper-Aufruf würde den Algorithmus etwas verlangsamen, ihm jedoch erlauben, Alpha-Zeichen in Kleinbuchstaben zu verarbeiten.
Chris F
3
Convert.ToByte (topChar + bottomChar) kann geschrieben werden als (Byte) (topChar + bottomChar)
Amir Rezaei
Um beide Fälle ohne große Leistungseinbußen zu behandeln,hexString[i] &= ~0x20;
Ben Voigt
9
Warum es komplex machen? Dies ist in Visual Studio 2008 einfach:
Der Grund ist die Leistung, wenn Sie eine Hochleistungslösung benötigen. :)
Ricky
7
Um nicht auf die vielen Antworten hier einzugehen, fand ich eine ziemlich optimale (~ 4,5-mal besser als akzeptiert), unkomplizierte Implementierung des Hex-String-Parsers. Zuerst Ausgabe meiner Tests (der erste Stapel ist meine Implementierung):
Give me that string:04c63f7842740c77e545bb0b2ade90b384f119f6ab57b680b7aa575a2f40939fTime to parse 100,000 times:50.4192 ms
Resultas base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=BitConverter'd:04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-57-B6-80-B7-AA-57-5A-2F-40-93-9FAccepted answer:(StringToByteArray)Time to parse 100000 times:233.1264msResultas base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=BitConverter'd:04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-57-B6-80-B7-AA-57-5A-2F-40-93-9FWithMono's implementation:Time to parse 100000 times:777.2544msResultas base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=BitConverter'd:04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-57-B6-80-B7-AA-57-5A-2F-40-93-9FWithSoapHexBinary:Time to parse 100000 times:845.1456msResultas base64: BMY/eEJ0DHflRbsLKt6Qs4TxGfarV7aAt6pXWi9Ak58=BitConverter'd:04-C6-3F-78-42-74-0C-77-E5-45-BB-0B-2A-DE-90-B3-84-F1-19-F6-AB-57-B6-80-B7-AA-57-5A-2F-40-93-9F
Die Zeilen base64 und 'BitConverter'd' dienen zum Testen der Richtigkeit. Beachten Sie, dass sie gleich sind.
Die Umsetzung:
publicstaticbyte[]ToByteArrayFromHex(string hexString){if(hexString.Length%2!=0)thrownewArgumentException("String must have an even length");vararray=newbyte[hexString.Length/2];for(int i =0; i < hexString.Length; i +=2){array[i/2]=ByteFromTwoChars(hexString[i], hexString[i +1]);}returnarray;}privatestaticbyteByteFromTwoChars(char p,char p_2){byte ret;if(p <='9'&& p >='0'){
ret =(byte)((p -'0')<<4);}elseif(p <='f'&& p >='a'){
ret =(byte)((p -'a'+10)<<4);}elseif(p <='F'&& p >='A'){
ret =(byte)((p -'A'+10)<<4);}elsethrownewArgumentException("Char is not a hex digit: "+ p,"p");if(p_2 <='9'&& p_2 >='0'){
ret |=(byte)((p_2 -'0'));}elseif(p_2 <='f'&& p_2 >='a'){
ret |=(byte)((p_2 -'a'+10));}elseif(p_2 <='F'&& p_2 >='A'){
ret |=(byte)((p_2 -'A'+10));}elsethrownewArgumentException("Char is not a hex digit: "+ p_2,"p_2");return ret;}
Ich habe einige Dinge ausprobiert unsafeund die (eindeutig redundante) Zeichen-zu-Knabber- ifSequenz auf eine andere Methode verschoben , aber dies war die schnellste, die es gab.
(Ich gebe zu, dass dies die Hälfte der Frage beantwortet. Ich hatte das Gefühl, dass die Konvertierung von Zeichenfolge-> Byte [] unterrepräsentiert war, während der Winkel von Byte [] -> Zeichenfolge gut abgedeckt zu sein scheint. Daher diese Antwort.)
Für die Anhänger von Knuth: Ich habe dies getan, weil ich alle paar Minuten ein paar tausend Hex-Strings analysieren muss. Deshalb ist es wichtig, dass es so schnell wie möglich ist (sozusagen in der inneren Schleife). Tomalaks Lösung ist nicht wesentlich langsamer, wenn viele solcher Parsen nicht auftreten.
Ben Mosher
5
Sichere Versionen:
publicstaticclassHexHelper{[System.Diagnostics.Contracts.Pure]publicstaticstringToHex(thisbyte[]value){if(value==null)thrownewArgumentNullException("value");conststring hexAlphabet =@"0123456789ABCDEF";var chars =newchar[checked(value.Length*2)];unchecked{for(int i =0; i <value.Length; i++){
chars[i *2]= hexAlphabet[value[i]>>4];
chars[i *2+1]= hexAlphabet[value[i]&0xF];}}returnnewstring(chars);}[System.Diagnostics.Contracts.Pure]publicstaticbyte[]FromHex(thisstringvalue){if(value==null)thrownewArgumentNullException("value");if(value.Length%2!=0)thrownewArgumentException("Hexadecimal value length must be even.","value");unchecked{byte[] result =newbyte[value.Length/2];for(int i =0; i < result.Length; i++){// 0(48) - 9(57) -> 0 - 9// A(65) - F(70) -> 10 - 15int b =value[i *2];// High 4 bits.int val =((b -'0')+((('9'- b)>>31)&-7))<<4;
b =value[i *2+1];// Low 4 bits.
val +=(b -'0')+((('9'- b)>>31)&-7);
result[i]=checked((byte)val);}return result;}}}
Unsichere Versionen Für diejenigen, die Leistung bevorzugen und keine Angst vor Unsicherheit haben. Etwa 35% schneller ToHex und 10% schneller FromHex.
publicstaticclassHexUnsafeHelper{[System.Diagnostics.Contracts.Pure]publicstaticunsafestringToHex(thisbyte[]value){if(value==null)thrownewArgumentNullException("value");conststring alphabet =@"0123456789ABCDEF";string result =newstring(' ',checked(value.Length*2));fixed(char* alphabetPtr = alphabet)fixed(char* resultPtr = result){char* ptr = resultPtr;unchecked{for(int i =0; i <value.Length; i++){*ptr++=*(alphabetPtr +(value[i]>>4));*ptr++=*(alphabetPtr +(value[i]&0xF));}}}return result;}[System.Diagnostics.Contracts.Pure]publicstaticunsafebyte[]FromHex(thisstringvalue){if(value==null)thrownewArgumentNullException("value");if(value.Length%2!=0)thrownewArgumentException("Hexadecimal value length must be even.","value");unchecked{byte[] result =newbyte[value.Length/2];fixed(char* valuePtr =value){char* valPtr = valuePtr;for(int i =0; i < result.Length; i++){// 0(48) - 9(57) -> 0 - 9// A(65) - F(70) -> 10 - 15int b =*valPtr++;// High 4 bits.int val =((b -'0')+((('9'- b)>>31)&-7))<<4;
b =*valPtr++;// Low 4 bits.
val +=(b -'0')+((('9'- b)>>31)&-7);
result[i]=checked((byte)val);}}return result;}}}
Übrigens
Für Benchmark-Tests, bei denen das Alphabet jedes Mal initialisiert wird, wenn die aufgerufene Konvertierungsfunktion falsch ist, muss das Alphabet const (für Zeichenfolge) oder statisch schreibgeschützt (für char []) sein. Dann wird die alphabetische Konvertierung von Byte [] in Zeichenfolge so schnell wie die Byte-Manipulationsversionen.
Und natürlich muss der Test in Release (mit Optimierung) kompiliert und die Debug-Option "JIT-Optimierung unterdrücken" deaktiviert sein (dasselbe gilt für "Nur meinen Code aktivieren", wenn Code debuggbar sein muss).
Inverse Funktion für Waleed Eissa-Code (Hex String To Byte Array):
publicstaticbyte[]HexToBytes(thisstring hexString){byte[] b =newbyte[hexString.Length/2];char c;for(int i =0; i < hexString.Length/2; i++){
c = hexString[i *2];
b[i]=(byte)((c <0x40? c -0x30:(c <0x47? c -0x37: c -0x57))<<4);
c = hexString[i *2+1];
b[i]+=(byte)(c <0x40? c -0x30:(c <0x47? c -0x37: c -0x57));}return b;}
Waleed Eissa-Funktion mit Unterstützung für Kleinbuchstaben:
publicstaticstringBytesToHex(thisbyte[] barray,bool toLowerCase =true){byte addByte =0x37;if(toLowerCase) addByte =0x57;char[] c =newchar[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 + addByte : b +0x30);
b =((byte)(barray[i]&0xF));
c[i *2+1]=(char)(b >9? b + addByte : b +0x30);}returnnewstring(c);}
publicstaticclassByteExtensions{publicstaticstringToHexString(thisbyte[] ba){StringBuilder hex =newStringBuilder(ba.Length*2);foreach(byte b in ba){
hex.AppendFormat("{0:x2}", b);}return hex.ToString();}}
Sie sollten den Code wahrscheinlich testen, bevor Sie ihn für eine solche Frage anbieten.
JWW
3
Von den Entwicklern von Microsoft eine nette, einfache Konvertierung:
publicstaticstringByteArrayToString(byte[] ba){// Concatenate the bytes into one long stringreturn ba.Aggregate(newStringBuilder(32),(sb, b)=> sb.Append(b.ToString("X2"))).ToString();}
Während das oben Genannte sauber und kompakt ist, werden Performance-Junkies mithilfe von Enumeratoren darüber schreien. Mit einer verbesserten Version von Tomalaks ursprünglicher Antwort können Sie Spitzenleistungen erzielen :
publicstaticstringByteArrayToString(byte[] ba){StringBuilder hex =newStringBuilder(ba.Length*2);for(int i=0; i < ba.Length; i++)// <-- Use for loop is faster than foreach
hex.Append(ba[i].ToString("X2"));// <-- ToString is faster than AppendFormat return hex.ToString();}
Dies ist die schnellste aller Routinen, die ich bisher hier gesehen habe. Nehmen Sie nicht einfach mein Wort dafür ... Testen Sie jede Routine auf Leistung und überprüfen Sie den CIL-Code selbst.
wenn Source == nulloder Source.Length == 0wir haben ein Problem, Sir!
Andrei Krasutski
2
In Bezug auf die Geschwindigkeit scheint dies hier besser zu sein als alles andere:
publicstaticstringToHexString(byte[] data){byte b;int i, j, k;int l = data.Length;char[] r =newchar[l *2];for(i =0, j =0; i < l;++i){
b = data[i];
k = b >>4;
r[j++]=(char)(k >9? k +0x37: k +0x30);
k = b &15;
r[j++]=(char)(k >9? k +0x37: k +0x30);}returnnewstring(r);}
Ich habe den von Ihnen vorgeschlagenen Code nicht erhalten, Olipro. hex[i] + hex[i+1]anscheinend zurückgegeben ein int.
Ich hatte jedoch einige Erfolge, indem ich einige Hinweise aus dem Waleeds-Code nahm und diese zusammenhämmerte. Es ist höllisch hässlich, aber es scheint zu funktionieren und funktioniert in 1/3 der Fälle im Vergleich zu den anderen gemäß meinen Tests (unter Verwendung des Patridges-Testmechanismus). Abhängig von der Eingabegröße. Das Umschalten der ?: S, um zuerst 0-9 zu trennen, würde wahrscheinlich ein etwas schnelleres Ergebnis liefern, da es mehr Zahlen als Buchstaben gibt.
publicstaticbyte[]StringToByteArray2(string hex){byte[] bytes =newbyte[hex.Length/2];int bl = bytes.Length;for(int i =0; i < bl;++i){
bytes[i]=(byte)((hex[2* i]>'F'? hex[2* i]-0x57: hex[2* i]>'9'? hex[2* i]-0x37: hex[2* i]-0x30)<<4);
bytes[i]|=(byte)(hex[2* i +1]>'F'? hex[2* i +1]-0x57: hex[2* i +1]>'9'? hex[2* i +1]-0x37: hex[2* i +1]-0x30);}return bytes;}
staticprivatereadonlychar[] hexAlphabet =newchar[]{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};staticstringByteArrayToHexViaByteManipulation3(byte[] bytes){char[] c =newchar[bytes.Length*2];byte b;for(int i =0; i < bytes.Length; i++){
b =((byte)(bytes[i]>>4));
c[i *2]= hexAlphabet[b];
b =((byte)(bytes[i]&0xF));
c[i *2+1]= hexAlphabet[b];}returnnewstring(c);}
Und ich denke, dies ist eine Optimierung:
staticprivatereadonlychar[] hexAlphabet =newchar[]{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};staticstringByteArrayToHexViaByteManipulation4(byte[] bytes){char[] c =newchar[bytes.Length*2];for(int i =0, ptr =0; i < bytes.Length; i++, ptr +=2){byte b = bytes[i];
c[ptr]= hexAlphabet[b >>4];
c[ptr +1]= hexAlphabet[b &0xF];}returnnewstring(c);}
Ich werde an diesem Bit-Fiddling-Wettbewerb teilnehmen, da ich eine Antwort habe, die auch Bit-Fiddling verwendet, um Hexadezimale zu dekodieren . Beachten Sie, dass die Verwendung von Zeichenarrays möglicherweise noch schneller ist, da das Aufrufen von StringBuilderMethoden ebenfalls einige Zeit in Anspruch nimmt.
publicstaticStringToHex(byte[] data){int dataLength = data.Length;// pre-create the stringbuilder using the length of the data * 2, precisely enoughStringBuilder sb =newStringBuilder(dataLength *2);for(int i =0; i < dataLength; i++){int b = data [i];// check using calculation over bits to see if first tuple is a letter// isLetter is zero if it is a digit, 1 if it is a letterint isLetter =(b >>7)&((b >>6)|(b >>5))&1;// calculate the code using a multiplication to make up the difference between// a digit character and an alphanumerical characterint code ='0'+((b >>4)&0xF)+ isLetter *('A'-'9'-1);// now append the result, after casting the code point to a character
sb.Append((Char)code);// do the same with the lower (less significant) tuple
isLetter =(b >>3)&((b >>2)|(b >>1))&1;
code ='0'+(b &0xF)+ isLetter *('A'-'9'-1);
sb.Append((Char)code);}return sb.ToString();}publicstaticbyte[]FromHex(String hex){// pre-create the arrayint resultLength = hex.Length/2;byte[] result =newbyte[resultLength];// set validity = 0 (0 = valid, anything else is not valid)int validity =0;int c, isLetter,value, validDigitStruct, validDigit, validLetterStruct, validLetter;for(int i =0, hexOffset =0; i < resultLength; i++, hexOffset +=2){
c = hex [hexOffset];// check using calculation over bits to see if first char is a letter// isLetter is zero if it is a digit, 1 if it is a letter (upper & lowercase)
isLetter =(c >>6)&1;// calculate the tuple value using a multiplication to make up the difference between// a digit character and an alphanumerical character// minus 1 for the fact that the letters are not zero basedvalue=((c &0xF)+ isLetter *(-1+10))<<4;// check validity of all the other bits
validity |= c >>7;// changed to >>, maybe not OK, use UInt?
validDigitStruct =(c &0x30)^0x30;
validDigit =((c &0x8)>>3)*(c &0x6);
validity |=(isLetter ^1)*(validDigitStruct | validDigit);
validLetterStruct = c &0x18;
validLetter =(((c -1)&0x4)>>2)*((c -1)&0x2);
validity |= isLetter *(validLetterStruct | validLetter);// do the same with the lower (less significant) tuple
c = hex [hexOffset +1];
isLetter =(c >>6)&1;value^=(c &0xF)+ isLetter *(-1+10);
result [i]=(byte)value;// check validity of all the other bits
validity |= c >>7;// changed to >>, maybe not OK, use UInt?
validDigitStruct =(c &0x30)^0x30;
validDigit =((c &0x8)>>3)*(c &0x6);
validity |=(isLetter ^1)*(validDigitStruct | validDigit);
validLetterStruct = c &0x18;
validLetter =(((c -1)&0x4)>>2)*((c -1)&0x2);
validity |= isLetter *(validLetterStruct | validLetter);}if(validity !=0){thrownewArgumentException("Hexadecimal encoding incorrect for input "+ hex);}return result;}
Hmm, ich sollte das wirklich optimieren Char[]und Charintern anstelle von Ints verwenden ...
Maarten Bodewes
Für C # wird es wahrscheinlich bevorzugt, die Variablen dort zu initialisieren, wo sie verwendet werden, anstatt außerhalb der Schleife, damit der Compiler optimiert wird. Ich bekomme in beiden Fällen die gleiche Leistung.
Peteter
2
Für die Leistung würde ich mit drphrozens Lösung gehen. Eine winzige Optimierung für den Decoder könnte darin bestehen, eine Tabelle für eines der beiden Zeichen zu verwenden, um "<< 4" zu entfernen.
Offensichtlich sind die beiden Methodenaufrufe teuer. Wenn eine Art von Überprüfung entweder auf Eingabe- oder Ausgabedaten durchgeführt wird (könnte CRC, Prüfsumme oder was auch immer sein), if (b == 255)...könnte dies übersprungen werden und dadurch auch die Methode insgesamt aufrufen.
Die Verwendung von offset++und offsetanstelle von offsetund offset + 1könnte einen theoretischen Vorteil bringen, aber ich vermute, dass der Compiler dies besser handhabt als ich.
privatestaticreadonlybyte[]LookupTableLow=newbyte[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};privatestaticreadonlybyte[]LookupTableHigh=newbyte[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xA0,0xB0,0xC0,0xD0,0xE0,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xA0,0xB0,0xC0,0xD0,0xE0,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};privatestaticbyteLookupLow(char c){var b =LookupTableLow[c];if(b ==255)thrownewIOException("Expected a hex character, got "+ c);return b;}privatestaticbyteLookupHigh(char c){var b =LookupTableHigh[c];if(b ==255)thrownewIOException("Expected a hex character, got "+ c);return b;}publicstaticbyteToByte(char[] chars,int offset){return(byte)(LookupHigh(chars[offset++])|LookupLow(chars[offset]));}
Dies ist mir ein Rätsel und wurde weder getestet noch bewertet.
publicstaticbyte[]FromHexString(string src){if(String.IsNullOrEmpty(src))returnnull;int index = src.Length;int sz = index /2;if(sz <=0)returnnull;byte[] rc =newbyte[sz];while(--sz >=0){char lo = src[--index];char hi = src[--index];
rc[sz]=(byte)(((hi >='0'&& hi <='9')? hi -'0':(hi >='a'&& hi <='f')? hi -'a'+10:(hi >='A'&& hi <='F')? hi -'A'+10:0)<<4|((lo >='0'&& lo <='9')? lo -'0':(lo >='a'&& lo <='f')? lo -'a'+10:(lo >='A'&& lo <='F')? lo -'A'+10:0));}return rc;}
Zwei Mashups, die die beiden Knabberoperationen zu einer zusammenfassen.
Wahrscheinlich ziemlich effiziente Version:
publicstaticstringByteArrayToString2(byte[] ba){char[] c =newchar[ba.Length*2];for(int i =0; i < ba.Length*2;++i){byte b =(byte)((ba[i>>1]>>4*((i&1)^1))&0xF);
c[i]=(char)(55+ b +(((b-10)>>31)&-7));}returnnewstring( c );}
Dekadente Linq-with-Bit-Hacking-Version:
publicstaticstringByteArrayToString(byte[] ba){returnstring.Concat( ba.SelectMany( b =>newint[]{ b >>4, b &0xF}).Select( b =>(char)(55+ b +(((b-10)>>31)&-7))));}
Und umgekehrt:
publicstaticbyte[]HexStringToByteArray(string s ){byte[] ab =newbyte[s.Length>>1];for(int i =0; i < s.Length; i++){int b = s[i];
b =(b -'0')+((('9'- b)>>31)&-7);
ab[i>>1]|=(byte)(b <<4*((i&1)^1));}return ab;}
HexStringToByteArray ("09") gibt 0x02 zurück, was schlecht ist
CoperNick
1
Eine andere Möglichkeit ist die stackallocReduzierung des GC-Speicherdrucks:
staticstringByteToHexBitFiddle(byte[] bytes){var c =stackallocchar[bytes.Length*2+1];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));}
c[bytes.Length*2]='\0';returnnewstring(c);}
Hier ist mein Schuss darauf. Ich habe ein Paar Erweiterungsklassen erstellt, um Zeichenfolge und Byte zu erweitern. Beim Test mit großen Dateien ist die Leistung mit der von Byte Manipulation 2 vergleichbar.
Der folgende Code für ToHexString ist eine optimierte Implementierung des Lookup- und Shift-Algorithmus. Es ist fast identisch mit dem von Behrooz, aber es stellt sich heraus, dass a foreachzum Iterieren verwendet wird und ein Zähler schneller ist als eine explizite Indizierungfor .
Es belegt den 2. Platz hinter Byte Manipulation 2 auf meinem Computer und ist ein gut lesbarer Code. Die folgenden Testergebnisse sind ebenfalls von Interesse:
Basierend auf den obigen Ergebnissen scheint es sicher zu sein, dass:
Die Nachteile für die Indizierung in eine Zeichenfolge zur Durchführung der Suche im Vergleich zu einem char-Array sind im Test für große Dateien erheblich.
Die Nachteile für die Verwendung eines StringBuilder mit bekannter Kapazität im Vergleich zu einem Zeichenarray mit bekannter Größe zum Erstellen des Strings sind noch bedeutender.
Hier ist der Code:
using System;
namespace ConversionExtensions{publicstaticclassByteArrayExtensions{privatereadonlystaticchar[] digits =newchar[]{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};publicstaticstringToHexString(thisbyte[] bytes){char[] hex =newchar[bytes.Length*2];int index =0;foreach(byte b in bytes){
hex[index++]= digits[b >>4];
hex[index++]= digits[b &0x0F];}returnnewstring(hex);}}}
using System;
using System.IO;
namespace ConversionExtensions{publicstaticclassStringExtensions{publicstaticbyte[]ToBytes(thisstring hexString){if(!string.IsNullOrEmpty(hexString)&& hexString.Length%2!=0){thrownewFormatException("Hexadecimal string must not be empty and must contain an even number of digits to be valid.");}
hexString = hexString.ToUpperInvariant();byte[] data =newbyte[hexString.Length/2];for(int index =0; index < hexString.Length; index +=2){int highDigitValue = hexString[index]<='9'? hexString[index]-'0': hexString[index]-'A'+10;int lowDigitValue = hexString[index +1]<='9'? hexString[index +1]-'0': hexString[index +1]-'A'+10;if(highDigitValue <0|| lowDigitValue <0|| highDigitValue >15|| lowDigitValue >15){thrownewFormatException("An invalid digit was encountered. Valid hexadecimal digits are 0-9 and A-F.");}else{bytevalue=(byte)((highDigitValue <<4)|(lowDigitValue &0x0F));
data[index /2]=value;}}return data;}}}
Nachfolgend sind die Testergebnisse aufgeführt, die ich erhalten habe, als ich meinen Code in das Testprojekt von @ patridge auf meinem Computer eingefügt habe. Ich habe auch einen Test zum Konvertieren in ein Byte-Array von hexadezimal hinzugefügt. Die Testläufe, die meinen Code ausgeführt haben, sind ByteArrayToHexViaOptimizedLookupAndShift und HexToByteArrayViaByteManipulation. Der HexToByteArrayViaConvertToByte wurde aus XXXX entnommen. Das HexToByteArrayViaSoapHexBinary stammt aus der Antwort von @ Mykroft.
Antworten:
Entweder:
oder:
Es gibt noch mehr Varianten, zum Beispiel hier .
Die umgekehrte Konvertierung würde folgendermaßen aussehen:
Die Verwendung
Substring
ist die beste Option in Kombination mitConvert.ToByte
. Weitere Informationen finden Sie in dieser Antwort . Wenn Sie eine bessere Leistung benötigen, müssen Sie diese vermeiden,Convert.ToByte
bevor Sie sie fallen lassen könnenSubString
.quelle
Performance-Analyse
Hinweis: Neuer Leiter ab dem 20.08.2015.
Ich habe jede der verschiedenen Konvertierungsmethoden durch einige grobe
Stopwatch
Leistungstests, einen Lauf mit einem zufälligen Satz (n = 61, 1000 Iterationen) und einen Lauf mit einem Projekt-Gutenburg-Text (n = 1.238.957, 150 Iterationen) geführt. Hier sind die Ergebnisse, ungefähr vom schnellsten zum langsamsten. Alle Messungen erfolgen in Ticks ( 10.000 Ticks = 1 ms ) und alle relativen Noten werden mit der [langsamsten]StringBuilder
Implementierung verglichen . Informationen zum verwendeten Code finden Sie unten oder im Testframework-Repo, in dem ich jetzt den Code zum Ausführen dieses Codes verwalte.Haftungsausschluss
WARNUNG: Verlassen Sie sich bei nichts Konkretem auf diese Statistiken. Sie sind einfach ein Beispiellauf von Beispieldaten. Wenn Sie wirklich erstklassige Leistung benötigen, testen Sie diese Methoden in einer Umgebung, die für Ihre Produktionsanforderungen repräsentativ ist, mit Daten, die für Ihre Verwendung repräsentativ sind.
Ergebnisse
unsafe
Suche (über CodesInChaos) (hinzugefügt, um das Repo per Luftatmer zu testen )BitConverter
(über Tomalak){SoapHexBinary}.ToString
(über Mykroft){byte}.ToString("X2")
(mitforeach
) (abgeleitet von Will Deans Antwort){byte}.ToString("X2")
(Verwenden von{IEnumerable}.Aggregate
, erfordert System.Linq) (über Mark)Array.ConvertAll
(mitstring.Join
) (über Will Dean)Array.ConvertAll
(Verwendungstring.Concat
von .NET 4.0 erforderlich) (über Will Dean){StringBuilder}.AppendFormat
(mitforeach
) (via Tomalak){StringBuilder}.AppendFormat
(Verwenden von{IEnumerable}.Aggregate
, erfordert System.Linq) (abgeleitet von Tomalaks Antwort)Nachschlagetabellen haben die Führung bei der Bytemanipulation übernommen. Grundsätzlich gibt es eine Form der Vorberechnung, was ein bestimmtes Halbbyte oder Byte in hexadezimaler Form sein wird. Wenn Sie dann die Daten durchgehen, schauen Sie einfach im nächsten Abschnitt nach, um zu sehen, um welche Hex-Zeichenfolge es sich handelt. Dieser Wert wird dann auf irgendeine Weise zur resultierenden Zeichenfolgenausgabe hinzugefügt. Lange Zeit war die Byte-Manipulation, die von einigen Entwicklern möglicherweise schwerer zu lesen war, der Ansatz mit der besten Leistung.
Am besten finden Sie immer noch einige repräsentative Daten und probieren sie in einer produktionsähnlichen Umgebung aus. Wenn Sie unterschiedliche Speicherbeschränkungen haben, bevorzugen Sie möglicherweise eine Methode mit weniger Zuordnungen gegenüber einer Methode, die schneller ist, aber mehr Speicher verbraucht.
Code testen
Fühlen Sie sich frei, mit dem Testcode zu spielen, den ich verwendet habe. Eine Version ist hier enthalten, aber Sie können das Repo klonen und Ihre eigenen Methoden hinzufügen. Bitte senden Sie eine Pull-Anfrage, wenn Sie etwas Interessantes finden oder zur Verbesserung des verwendeten Test-Frameworks beitragen möchten.
Func<byte[], string>
) zu /Tests/ConvertByteArrayToHexString/Test.cs hinzu.TestCandidates
Rückgabewert in derselben Klasse hinzu.GenerateTestInput
derselben Klasse umschalten .Update (13.01.2010)
Waleeds Antwort zur Analyse hinzugefügt. Ziemlich schnell.
Update (05.10.2011)
Der
string.Concat
Array.ConvertAll
Vollständigkeit halber wurde eine Variante hinzugefügt (erfordert .NET 4.0). Auf Augenhöhe mit derstring.Join
Version.Update (05.02.2012)
Test Repo enthält weitere Varianten wie
StringBuilder.Append(b.ToString("X2"))
. Keiner hat die Ergebnisse gestört.foreach
ist schneller als{IEnumerable}.Aggregate
zum Beispiel,BitConverter
gewinnt aber trotzdem.Update (03.04.2012)
Mykrofts
SoapHexBinary
Antwort zur Analyse hinzugefügt , die den dritten Platz einnahm.Update (15.01.2013)
Die Byte-Manipulationsantwort von CodesInChaos wurde hinzugefügt, die den ersten Platz einnahm (mit großem Abstand bei großen Textblöcken).
Update (23.05.2013)
Nathan Moinvaziris Lookup-Antwort und die Variante aus Brian Lamberts Blog wurden hinzugefügt. Beides ziemlich schnell, aber nicht die Führung auf der von mir verwendeten Testmaschine (AMD Phenom 9750).
Update (31.07.2014)
Die neue bytebasierte Suchantwort von @ CodesInChaos wurde hinzugefügt. Es scheint sowohl bei den Satztests als auch bei den Volltexttests die Führung übernommen zu haben.
Update (20.08.2015)
Hinzugefügt airbreather die Optimierungen und
unsafe
Variante dieser Repo - Antwort . Wenn Sie in dem unsicheren Spiel spielen möchten, können Sie sowohl bei kurzen Saiten als auch bei großen Texten enorme Leistungssteigerungen gegenüber den vorherigen Top-Gewinnern erzielen.quelle
bytes.ToHexStringAtLudicrousSpeed()
. B. ).Es gibt eine Klasse namens SoapHexBinary , die genau das tut, was Sie wollen.
quelle
Beim Schreiben von Kryptocode werden häufig datenabhängige Verzweigungen und Tabellensuchen vermieden, 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.
Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn
Eine Erklärung für das seltsame Geigen:
bytes[i] >> 4
extrahiert das hohe Halbbyte eines Bytesbytes[i] & 0xF
extrahiert das niedrige Halbbyte eines Bytesb - 10
ist
< 0
für Werteb < 10
, die zu einer Dezimalstelle werden,ist
>= 0
für Werteb > 10
, die zu einem Buchstaben vonA
bis werdenF
.i >> 31
einer vorzeichenbehafteten 32-Bit-Ganzzahl wird das Vorzeichen dank der Vorzeichenerweiterung extrahiert. Es wird-1
füri < 0
und0
für seini >= 0
.(b-10)>>31
dies0
für Buchstaben und-1
Ziffern gilt.0
undb
liegt im Bereich von 10 bis 15. Wir möchten ihnA
(65) bisF
(70) zuordnen, was das Hinzufügen von 55 ('A'-10
) impliziert .b
vom Bereich 0 bis 9 dem Bereich0
(48) bis9
(57) zugeordnet wird. Dies bedeutet, dass es -7 ('0' - 55
) werden muss.Jetzt könnten wir einfach mit 7 multiplizieren. Da -1 jedoch durch alle Bits 1 dargestellt wird, können wir stattdessen
& -7
da(0 & -7) == 0
und verwenden(-1 & -7) == -7
.Einige weitere Überlegungen:
c
, da die Messung zeigt, dass die Berechnungi
billiger ist.i < bytes.Length
der oberen Grenze der Schleife ermöglicht es dem JITter, Grenzüberprüfungen zu eliminierenbytes[i]
, daher habe ich diese Variante gewählt.b
eines Int ermöglicht unnötige Konvertierungen von und zu Byte.quelle
hex string
zubyte[] array
?87 + b + (((b-10)>>31)&-39)
byte[] array
", was wörtlich ein Array von Byte-Arrays bedeutet, oderbyte[][]
. Ich habe mich nur lustig gemacht.Wenn Sie mehr Flexibilität als möchten
BitConverter
, aber nicht diese klobigen expliziten Schleifen im Stil der 90er Jahre möchten, können Sie Folgendes tun:Oder wenn Sie .NET 4.0 verwenden:
(Letzteres aus einem Kommentar zum Originalbeitrag.)
quelle
Ein weiterer Ansatz, der auf Nachschlagetabellen basiert. Dieser verwendet nur eine Nachschlagetabelle für jedes Byte anstelle einer Nachschlagetabelle pro Nibble.
Ich habe auch Varianten davon mit getestet
ushort
von ,struct{char X1, X2}
,struct{byte X1, X2}
in der Lookup - Tabelle.Abhängig vom Kompilierungsziel (x86, X64) hatten diese entweder ungefähr die gleiche Leistung oder waren etwas langsamer als diese Variante.
Und für noch höhere Leistung ist es
unsafe
Geschwister:Oder wenn Sie es für akzeptabel halten, direkt in die Zeichenfolge zu schreiben:
quelle
Span
jetzt anstelle von verwendet werden kannunsafe
?Sie können die BitConverter.ToString-Methode verwenden:
Ausgabe:
Weitere Informationen: BitConverter.ToString-Methode (Byte [])
quelle
Ich bin heute auf das gleiche Problem gestoßen und bin auf diesen Code gestoßen:
Quelle: Forum post byte [] Array zu Hex String (siehe den Beitrag von PZahra). Ich habe den Code ein wenig geändert, um das 0x-Präfix zu entfernen.
Ich habe einige Leistungstests für den Code durchgeführt und er war fast achtmal schneller als die Verwendung von BitConverter.ToString () (laut Patridges Beitrag der schnellste).
quelle
Dies ist eine Antwort auf Revision 4 von Tomalaks sehr beliebter Antwort (und nachfolgenden Änderungen).
Ich mache den Fall, dass diese Bearbeitung falsch ist, und erkläre, warum sie zurückgesetzt werden könnte. Unterwegs lernen Sie vielleicht ein oder zwei Dinge über einige Interna und sehen ein weiteres Beispiel dafür, was vorzeitige Optimierung wirklich ist und wie sie Sie beißen kann.
tl; dr: Verwenden Sie einfach
Convert.ToByte
undString.Substring
wenn Sie es eilig haben ("Originalcode" unten), ist es die beste Kombination, wenn Sie nicht erneut implementieren möchtenConvert.ToByte
. Verwenden Sie etwas Fortgeschritteneres (siehe andere Antworten), das nicht verwendet wird,Convert.ToByte
wenn Sie Leistung benötigen . Sie nicht verwenden , etwas anderes alsString.Substring
in Kombination mitConvert.ToByte
, es sei denn , jemand etwas interessantes dieses in den Kommentaren dieser Antwort zu sagen hat.Warnung: Diese Antwort kann veraltet sein, wenn a
Convert.ToByte(char[], Int32)
Überlastung im Framework implementiert ist. Dies wird wahrscheinlich nicht bald passieren.In der Regel sage ich nicht gerne "Nicht vorzeitig optimieren", weil niemand weiß, wann "vorzeitig" ist. Das einzige, was Sie berücksichtigen müssen, wenn Sie entscheiden, ob Sie optimieren möchten oder nicht, ist: "Habe ich die Zeit und die Ressourcen, um Optimierungsansätze richtig zu untersuchen?". Wenn Sie das nicht tun, dann ist es zu früh, warten Sie, bis Ihr Projekt wird reifen , oder bis Sie die Leistung benötigen (wenn es einen echten Bedarf ist, dann werden Sie machen die Zeit). Machen Sie in der Zwischenzeit das Einfachste, was stattdessen funktionieren könnte.
Originalcode:
Revision 4:
Die Revision vermeidet
String.Substring
und verwendet aStringReader
stattdessen ein. Der angegebene Grund ist:Schauen Sie sich den Referenzcode für an
String.Substring
, ist es eindeutig schon "Single-Pass". und warum sollte es nicht sein? Es arbeitet auf Byte-Ebene, nicht auf Ersatzpaaren.Es wird jedoch eine neue Zeichenfolge zugewiesen, aber dann müssen Sie eine Zeichenfolge zuweisen, an die Sie
Convert.ToByte
trotzdem übergeben möchten. Darüber hinaus weist die in der Revision bereitgestellte Lösung jeder Iteration ein weiteres Objekt zu (das Zwei-Zeichen-Array). Sie können diese Zuordnung sicher außerhalb der Schleife platzieren und das Array wiederverwenden, um dies zu vermeiden.Jede Hexadezimalzahl
numeral
repräsentiert ein einzelnes Oktett mit zwei Ziffern (Symbolen).Aber warum dann
StringReader.Read
zweimal anrufen ? Rufen Sie einfach die zweite Überladung auf und bitten Sie sie, zwei Zeichen im Array mit zwei Zeichen gleichzeitig zu lesen. und reduzieren Sie die Anzahl der Anrufe um zwei.Was Ihnen bleibt, ist ein String-Reader, dessen einziger zusätzlicher "Wert" ein paralleler Index (intern
_pos
) ist, den Sie selbst hätten deklarieren können (wiej
zum Beispiel), eine redundante Längenvariable (intern_length
) und eine redundante Referenz auf die Eingabe Zeichenfolge (intern_s
). Mit anderen Worten, es ist nutzlos.Wenn Sie sich fragen, wie
Read
"liest", schauen Sie sich einfach den Code an . Alles, was er tut, ist ein AufrufString.CopyTo
der Eingabezeichenfolge. Der Rest ist nur Buchhaltungsaufwand, um Werte zu erhalten, die wir nicht benötigen.Entfernen Sie also den String-Reader bereits und rufen Sie sich
CopyTo
selbst an. Es ist einfacher, klarer und effizienter.Benötigen Sie wirklich einen
j
Index, der in Schritten von zwei parallel zu erhöht wirdi
? Natürlich nicht, multiplizieren Sie einfachi
mit zwei (was der Compiler in der Lage sein sollte, eine Addition zu optimieren).Wie sieht die Lösung jetzt aus? Genau wie es am Anfang war, nur anstelle der Verwendung
String.Substring
der Zeichenfolge und kopieren Sie die Daten , um es zu verteilen, sind Sie ein Zwischen Array , zu dem Sie die hexadezimalen Ziffern zu kopieren, dann verteilen Sie die Zeichenfolge selbst und kopieren Sie die Daten wieder aus das Array und in die Zeichenfolge (wenn Sie es im Zeichenfolgenkonstruktor übergeben). Die zweite Kopie wird möglicherweise optimiert, wenn sich die Zeichenfolge bereits im internen Pool befindet, kann sie dannString.Substring
aber auch in diesen Fällen vermeiden.Wenn Sie sich das noch
String.Substring
einmal ansehen , sehen Sie, dass es einige interne Kenntnisse auf niedriger Ebene darüber verwendet, wie Zeichenfolgen erstellt werden, um die Zeichenfolge schneller zuzuweisen, als Sie es normalerweise tun könnten, und dass es denselben Code enthält, derCopyTo
direkt dort verwendet wird, um dies zu vermeiden der Anruf Overhead.String.Substring
Manuelle Methode
Fazit? Wenn Sie verwenden möchten
Convert.ToByte(String, Int32)
(weil Sie diese Funktionalität nicht selbst erneut implementieren möchten), scheint es keinen Weg zu geben, sie zu schlagenString.Substring
. Alles, was Sie tun, ist im Kreis zu laufen und das Rad neu zu erfinden (nur mit nicht optimalen Materialien).Beachten Sie, dass mit
Convert.ToByte
undString.Substring
eine absolut gültige Wahl ist, wenn Sie keine extreme Leistung benötigen. Denken Sie daran: Entscheiden Sie sich nur für eine Alternative, wenn Sie Zeit und Ressourcen haben, um zu untersuchen, wie sie ordnungsgemäß funktioniert.Wenn es eine
Convert.ToByte(char[], Int32)
gäbe, wären die Dinge natürlich anders (es wäre möglich, das zu tun, was ich oben beschrieben habe, und es vollständig zu vermeidenString
).Ich vermute, dass Leute, die eine bessere Leistung durch "Vermeiden
String.Substring
" melden, auch vermeidenConvert.ToByte(String, Int32)
, was Sie wirklich tun sollten, wenn Sie die Leistung trotzdem brauchen. Schauen Sie sich die unzähligen anderen Antworten an, um die verschiedenen Ansätze dafür zu entdecken.Haftungsausschluss: Ich habe die neueste Version des Frameworks nicht dekompiliert, um zu überprüfen, ob die Referenzquelle auf dem neuesten Stand ist.
Jetzt klingt alles gut und logisch, hoffentlich sogar offensichtlich, wenn Sie es geschafft haben, so weit zu kommen. Aber ist es wahr?
Ja!
Requisiten an Partridge für das Bench Framework, es ist einfach zu hacken. Die verwendete Eingabe ist der folgende SHA-1-Hash, der 5000 Mal wiederholt wird, um eine 100.000 Byte lange Zeichenfolge zu erstellen.
Habe Spaß! (Aber mit Mäßigung optimieren.)
quelle
Ergänzung zur Antwort durch @CodesInChaos (umgekehrte Methode)
Erläuterung:
& 0x0f
soll auch Kleinbuchstaben unterstützenhi = hi + 10 + ((hi >> 31) & 7);
ist das gleiche wie:hi = ch-65 + 10 + (((ch-65) >> 31) & 7);
Für '0' .. '9' ist es dasselbe wie
hi = ch - 65 + 10 + 7;
das isthi = ch - 48
(das liegt an0xffffffff & 7
).Für 'A' .. 'F' ist es
hi = ch - 65 + 10;
(das liegt an0x00000000 & 7
).Für 'a' .. 'f' müssen wir große Zahlen haben, also müssen wir 32 von der Standardversion subtrahieren, indem wir einige Bits
0
mit verwenden& 0x0f
.65 ist Code für
'A'
48 ist Code für
'0'
7 ist die Anzahl der Buchstaben zwischen
'9'
und'A'
in der ASCII-Tabelle (...456789:;<=>?@ABCD...
).quelle
Dieses Problem könnte auch mithilfe einer Nachschlagetabelle gelöst werden. Dies würde eine kleine Menge statischen Speichers sowohl für den Codierer als auch für den Decodierer erfordern. Diese Methode wird jedoch schnell sein:
Meine Lösung verwendet 1024 Bytes für die Codierungstabelle und 256 Bytes für die Decodierung.
Dekodierung
Codierung
Vergleich
* diese Lösung
Hinweis
Während der Dekodierung können IOException und IndexOutOfRangeException auftreten (wenn ein Zeichen einen zu hohen Wert> 256 hat). Methoden zum De- / Codieren von Streams oder Arrays sollten implementiert werden, dies ist nur ein Proof of Concept.
quelle
Dies ist ein großartiger Beitrag. Ich mag Waleeds Lösung. Ich habe Patridges Test nicht durchlaufen, aber es scheint ziemlich schnell zu sein. Ich brauchte auch den umgekehrten Prozess, bei dem eine Hex-Zeichenfolge in ein Byte-Array konvertiert wurde, also schrieb ich sie als Umkehrung von Waleeds Lösung. Ich bin mir nicht sicher, ob es schneller ist als die ursprüngliche Lösung von Tomalak. Auch hier habe ich den umgekehrten Prozess nicht durch Patridges Test ausgeführt.
quelle
hexString[i] &= ~0x20;
Warum es komplex machen? Dies ist in Visual Studio 2008 einfach:
C #:
VB:
quelle
Um nicht auf die vielen Antworten hier einzugehen, fand ich eine ziemlich optimale (~ 4,5-mal besser als akzeptiert), unkomplizierte Implementierung des Hex-String-Parsers. Zuerst Ausgabe meiner Tests (der erste Stapel ist meine Implementierung):
Die Zeilen base64 und 'BitConverter'd' dienen zum Testen der Richtigkeit. Beachten Sie, dass sie gleich sind.
Die Umsetzung:
Ich habe einige Dinge ausprobiert
unsafe
und die (eindeutig redundante) Zeichen-zu-Knabber-if
Sequenz auf eine andere Methode verschoben , aber dies war die schnellste, die es gab.(Ich gebe zu, dass dies die Hälfte der Frage beantwortet. Ich hatte das Gefühl, dass die Konvertierung von Zeichenfolge-> Byte [] unterrepräsentiert war, während der Winkel von Byte [] -> Zeichenfolge gut abgedeckt zu sein scheint. Daher diese Antwort.)
quelle
Sichere Versionen:
Unsichere Versionen Für diejenigen, die Leistung bevorzugen und keine Angst vor Unsicherheit haben. Etwa 35% schneller ToHex und 10% schneller FromHex.
Übrigens Für Benchmark-Tests, bei denen das Alphabet jedes Mal initialisiert wird, wenn die aufgerufene Konvertierungsfunktion falsch ist, muss das Alphabet const (für Zeichenfolge) oder statisch schreibgeschützt (für char []) sein. Dann wird die alphabetische Konvertierung von Byte [] in Zeichenfolge so schnell wie die Byte-Manipulationsversionen.
Und natürlich muss der Test in Release (mit Optimierung) kompiliert und die Debug-Option "JIT-Optimierung unterdrücken" deaktiviert sein (dasselbe gilt für "Nur meinen Code aktivieren", wenn Code debuggbar sein muss).
quelle
Inverse Funktion für Waleed Eissa-Code (Hex String To Byte Array):
Waleed Eissa-Funktion mit Unterstützung für Kleinbuchstaben:
quelle
Erweiterungsmethoden (Haftungsausschluss: vollständig ungetesteter Code, übrigens ...):
etc .. Verwenden Sie eine der drei Lösungen von Tomalak (wobei die letzte eine Erweiterungsmethode für eine Zeichenfolge ist).
quelle
Von den Entwicklern von Microsoft eine nette, einfache Konvertierung:
Während das oben Genannte sauber und kompakt ist, werden Performance-Junkies mithilfe von Enumeratoren darüber schreien. Mit einer verbesserten Version von Tomalaks ursprünglicher Antwort können Sie Spitzenleistungen erzielen :
Dies ist die schnellste aller Routinen, die ich bisher hier gesehen habe. Nehmen Sie nicht einfach mein Wort dafür ... Testen Sie jede Routine auf Leistung und überprüfen Sie den CIL-Code selbst.
quelle
b.ToSting("X2")
.Und zum Einfügen in eine SQL-Zeichenfolge (wenn Sie keine Befehlsparameter verwenden):
quelle
Source == null
oderSource.Length == 0
wir haben ein Problem, Sir!In Bezug auf die Geschwindigkeit scheint dies hier besser zu sein als alles andere:
quelle
Ich habe den von Ihnen vorgeschlagenen Code nicht erhalten, Olipro.
hex[i] + hex[i+1]
anscheinend zurückgegeben einint
.Ich hatte jedoch einige Erfolge, indem ich einige Hinweise aus dem Waleeds-Code nahm und diese zusammenhämmerte. Es ist höllisch hässlich, aber es scheint zu funktionieren und funktioniert in 1/3 der Fälle im Vergleich zu den anderen gemäß meinen Tests (unter Verwendung des Patridges-Testmechanismus). Abhängig von der Eingabegröße. Das Umschalten der ?: S, um zuerst 0-9 zu trennen, würde wahrscheinlich ein etwas schnelleres Ergebnis liefern, da es mehr Zahlen als Buchstaben gibt.
quelle
Diese Version von ByteArrayToHexViaByteManipulation könnte schneller sein.
Aus meinen Berichten:
...
Und ich denke, dies ist eine Optimierung:
quelle
Ich werde an diesem Bit-Fiddling-Wettbewerb teilnehmen, da ich eine Antwort habe, die auch Bit-Fiddling verwendet, um Hexadezimale zu dekodieren . Beachten Sie, dass die Verwendung von Zeichenarrays möglicherweise noch schneller ist, da das Aufrufen von
StringBuilder
Methoden ebenfalls einige Zeit in Anspruch nimmt.Aus Java-Code konvertiert.
quelle
Char[]
undChar
intern anstelle von Ints verwenden ...Für die Leistung würde ich mit drphrozens Lösung gehen. Eine winzige Optimierung für den Decoder könnte darin bestehen, eine Tabelle für eines der beiden Zeichen zu verwenden, um "<< 4" zu entfernen.
Offensichtlich sind die beiden Methodenaufrufe teuer. Wenn eine Art von Überprüfung entweder auf Eingabe- oder Ausgabedaten durchgeführt wird (könnte CRC, Prüfsumme oder was auch immer sein),
if (b == 255)...
könnte dies übersprungen werden und dadurch auch die Methode insgesamt aufrufen.Die Verwendung von
offset++
undoffset
anstelle vonoffset
undoffset + 1
könnte einen theoretischen Vorteil bringen, aber ich vermute, dass der Compiler dies besser handhabt als ich.Dies ist mir ein Rätsel und wurde weder getestet noch bewertet.
quelle
Noch eine Variation für Vielfalt:
quelle
Nicht auf Geschwindigkeit optimiert, aber mehr LINQy als die meisten Antworten (.NET 4.0):
quelle
Zwei Mashups, die die beiden Knabberoperationen zu einer zusammenfassen.
Wahrscheinlich ziemlich effiziente Version:
Dekadente Linq-with-Bit-Hacking-Version:
Und umgekehrt:
quelle
Eine andere Möglichkeit ist die
stackalloc
Reduzierung des GC-Speicherdrucks:quelle
Hier ist mein Schuss darauf. Ich habe ein Paar Erweiterungsklassen erstellt, um Zeichenfolge und Byte zu erweitern. Beim Test mit großen Dateien ist die Leistung mit der von Byte Manipulation 2 vergleichbar.
Der folgende Code für ToHexString ist eine optimierte Implementierung des Lookup- und Shift-Algorithmus. Es ist fast identisch mit dem von Behrooz, aber es stellt sich heraus, dass a
foreach
zum Iterieren verwendet wird und ein Zähler schneller ist als eine explizite Indizierungfor
.Es belegt den 2. Platz hinter Byte Manipulation 2 auf meinem Computer und ist ein gut lesbarer Code. Die folgenden Testergebnisse sind ebenfalls von Interesse:
ToHexStringCharArrayWithCharArrayLookup: 41.589,69 durchschnittliche Ticks (über 1000 Läufe), 1,5-facher ToHexStringCharArrayWithStringLookup: 50.764,06 durchschnittliche Ticks (über 1000 Läufe), 1,2-facher ToHexStringStringBuilderWithCharArrayLookup: 1,2,81
Basierend auf den obigen Ergebnissen scheint es sicher zu sein, dass:
Hier ist der Code:
Nachfolgend sind die Testergebnisse aufgeführt, die ich erhalten habe, als ich meinen Code in das Testprojekt von @ patridge auf meinem Computer eingefügt habe. Ich habe auch einen Test zum Konvertieren in ein Byte-Array von hexadezimal hinzugefügt. Die Testläufe, die meinen Code ausgeführt haben, sind ByteArrayToHexViaOptimizedLookupAndShift und HexToByteArrayViaByteManipulation. Der HexToByteArrayViaConvertToByte wurde aus XXXX entnommen. Das HexToByteArrayViaSoapHexBinary stammt aus der Antwort von @ Mykroft.
quelle
Eine weitere schnelle Funktion ...
quelle