1576 Stimmen

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

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

10 Stimmen

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

20voto

Dotnet 5 Aktualisierung

Zum Konvertieren von byte[] (Byte-Array) in hexadezimal string verwenden:

System.Convert.ToHexString

var myBytes = new byte[100];
var myString = System.Convert.ToHexString(myBytes);

Umrechnung von hexadezimal string a byte[] verwenden:

System.Convert.FromHexString

var myString  = "E10B116E8530A340BCC7B3EAC208487B";
var myBytes = System.Convert.FromHexString(myString);

20voto

drphrozen Punkte 317

Dieses Problem könnte auch mit einer Nachschlagetabelle gelöst werden. Dies würde eine geringe Menge an statischem Speicher sowohl für den Encoder als auch für den Decoder erfordern. Diese Methode ist jedoch sehr schnell:

  • Encodertabelle 512 Bytes oder 1024 Bytes (zweimal der Größe, wenn sowohl Groß- als auch Kleinschreibung benötigt wird)
  • Decoder-Tabelle 256 Bytes oder 64 KiB (entweder ein Single-Char-Look-up oder Dual-Char-Look-up)

Meine Lösung verwendet 1024 Bytes für die Kodierungstabelle und 256 Bytes für die Dekodierung.

Dekodierung

private static readonly byte[] LookupTable = new byte[] {
  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
};

private static byte Lookup(char c)
{
  var b = LookupTable[c];
  if (b == 255)
    throw new IOException("Expected a hex character, got " + c);
  return b;
}

public static byte ToByte(char[] chars, int offset)
{
  return (byte)(Lookup(chars[offset]) << 4 | Lookup(chars[offset + 1]));
}

Kodierung

private static readonly char[][] LookupTableUpper;
private static readonly char[][] LookupTableLower;

static Hex()
{
  LookupTableLower = new char[256][];
  LookupTableUpper = new char[256][];
  for (var i = 0; i < 256; i++)
  {
    LookupTableLower[i] = i.ToString("x2").ToCharArray();
    LookupTableUpper[i] = i.ToString("X2").ToCharArray();
  }
}

public static char[] ToCharLower(byte[] b, int bOffset)
{
  return LookupTableLower[b[bOffset]];
}

public static char[] ToCharUpper(byte[] b, int bOffset)
{
  return LookupTableUpper[b[bOffset]];
}

Vergleich

StringBuilderToStringFromBytes:   106148
BitConverterToStringFromBytes:     15783
ArrayConvertAllToStringFromBytes:  54290
ByteManipulationToCharArray:        8444
TableBasedToCharArray:              5651 *

* diese Lösung

Hinweis

Bei der Dekodierung konnten IOException und IndexOutOfRangeException auftreten (wenn ein Zeichen einen zu hohen Wert > 256 hat). Methoden zur De-/Kodierung von Streams oder Arrays sollten implementiert werden, dies ist nur ein Proof of Concept.

2 Stimmen

Der Speicherverbrauch von 256 Byte ist vernachlässigbar, wenn Sie Code in der CLR ausführen.

14voto

Craig Poulton Punkte 49

Warum sollte man es kompliziert machen? In Visual Studio 2008 ist das ganz einfach:

C#:

string hex = BitConverter.ToString(YourByteArray).Replace("-", "");

VB:

Dim hex As String = BitConverter.ToString(YourByteArray).Replace("-", "")

3 Stimmen

Der Grund ist die Leistung, wenn Sie eine leistungsstarke Lösung benötigen :)

12voto

Chris F Punkte 2786

Dies ist ein großartiger Beitrag. Die Lösung von Waleed gefällt mir. Ich habe sie nicht durch den Test von Patridge laufen lassen, aber sie scheint recht schnell zu sein. Ich brauchte auch den umgekehrten Prozess, die Konvertierung eines Hex-Strings in ein Byte-Array, also habe ich es als Umkehrung von Waleeds Lösung geschrieben. Ich bin mir nicht sicher, ob es schneller ist als Tomalaks ursprüngliche Lösung. Auch hier habe ich den umgekehrten Prozess nicht durch den Test von Patridge laufen lassen.

private byte[] HexStringToByteArray(string hexString)
{
    int hexStringLength = hexString.Length;
    byte[] b = new byte[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;
}

1 Stimmen

Dieser Code geht davon aus, dass die Hex-Zeichenkette Großbuchstaben verwendet, und explodiert, wenn die Hex-Zeichenkette Kleinbuchstaben verwendet. Um sicher zu gehen, sollten Sie die Eingabezeichenfolge in Großbuchstaben umwandeln.

0 Stimmen

Das ist eine kluge Beobachtung, Marc. Der Code wurde geschrieben, um die Lösung von Waleed umzukehren. Der ToUpper-Aufruf würde den Algorithmus etwas verlangsamen, aber er würde es ermöglichen, Kleinbuchstaben-Alpha-Zeichen zu behandeln.

3 Stimmen

Convert.ToByte(topChar + bottomChar) kann geschrieben werden als (byte)(topChar + bottomChar)

9voto

Ben Mosher Punkte 12823

Nicht auf die vielen Antworten hier zu stapeln, aber ich fand eine ziemlich optimale (~ 4,5x besser als akzeptiert), einfache Implementierung des Hex-String-Parser. Zunächst die Ausgabe meiner Tests (der erste Stapel ist meine Implementierung):

Give me that string:
04c63f7842740c77e545bb0b2ade90b384f119f6ab57b680b7aa575a2f40939f

Time to parse 100,000 times: 50.4192 ms
Result as 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-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

Accepted answer: (StringToByteArray)
Time to parse 100000 times: 233.1264ms
Result as 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-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

With Mono's implementation:
Time to parse 100000 times: 777.2544ms
Result as 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-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

With SoapHexBinary:
Time to parse 100000 times: 845.1456ms
Result as 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-5
7-B6-80-B7-AA-57-5A-2F-40-93-9F

Die Zeilen base64 und 'BitConverter'd' dienen dazu, die Korrektheit zu prüfen. Beachten Sie, dass sie gleich sind.

Die Umsetzung:

public static byte[] ToByteArrayFromHex(string hexString)
{
  if (hexString.Length % 2 != 0) throw new ArgumentException("String must have an even length");
  var array = new byte[hexString.Length / 2];
  for (int i = 0; i < hexString.Length; i += 2)
  {
    array[i/2] = ByteFromTwoChars(hexString[i], hexString[i + 1]);
  }
  return array;
}

private static byte ByteFromTwoChars(char p, char p_2)
{
  byte ret;
  if (p <= '9' && p >= '0')
  {
    ret = (byte) ((p - '0') << 4);
  }
  else if (p <= 'f' && p >= 'a')
  {
    ret = (byte) ((p - 'a' + 10) << 4);
  }
  else if (p <= 'F' && p >= 'A')
  {
    ret = (byte) ((p - 'A' + 10) << 4);
  } else throw new ArgumentException("Char is not a hex digit: " + p,"p");

  if (p_2 <= '9' && p_2 >= '0')
  {
    ret |= (byte) ((p_2 - '0'));
  }
  else if (p_2 <= 'f' && p_2 >= 'a')
  {
    ret |= (byte) ((p_2 - 'a' + 10));
  }
  else if (p_2 <= 'F' && p_2 >= 'A')
  {
    ret |= (byte) ((p_2 - 'A' + 10));
  } else throw new ArgumentException("Char is not a hex digit: " + p_2, "p_2");

  return ret;
}

Ich habe einige Sachen mit unsafe und das Verschieben des (eindeutig redundanten) Zeichens-to-nibble if Sequenz zu einer anderen Methode, aber das war die schnellste, die es gab.

(Ich räume ein, dass damit die Hälfte der Frage beantwortet ist. Ich hatte das Gefühl, dass die Konvertierung von string->byte[] unterrepräsentiert war, während der Winkel byte[]->string gut abgedeckt zu sein scheint. Daher diese Antwort.)

1 Stimmen

Für die Anhänger von Knuth: Ich habe dies getan, weil ich alle paar Minuten oder so ein paar tausend Hex-Zeichenketten parsen muss, also ist es wichtig, dass es so schnell wie möglich geht (in der inneren Schleife, sozusagen). Tomalaks Lösung ist nicht nennenswert langsamer, wenn nicht viele solcher Parsingvorgänge anfallen.

CodeJaeger.com

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

Powered by:

X